diff --git a/connectors/netty-connector/pom.xml b/connectors/netty-connector/pom.xml
index 088c6ec9bb..b547a990b2 100644
--- a/connectors/netty-connector/pom.xml
+++ b/connectors/netty-connector/pom.xml
@@ -49,6 +49,12 @@
${project.version}
test
+
+ org.glassfish.jersey.test-framework.providers
+ jersey-test-framework-provider-jetty
+ ${project.version}
+ test
+
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 9c79d1281d..562edadd8b 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, 2023 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 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
@@ -129,4 +129,56 @@ public class NettyClientProperties {
public static final Integer
DEFAULT_EXPECT_100_CONTINUE_TIMEOUT_VALUE = 500;
+
+ /**
+ * Parameter which allows extending of the header size for the Netty connector
+ *
+ * @since 2.44
+ */
+ public static final String
+ MAX_HEADER_SIZE = "jersey.config.client.netty.maxHeaderSize";
+
+ /**
+ * Default header size for Netty Connector.
+ * Taken from {@link io.netty.handler.codec.http.HttpClientCodec#HttpClientCodec(int, int, int)}
+ *
+ * @since 2.44
+ */
+ public static final Integer
+ DEFAULT_HEADER_SIZE = 8192;
+
+ /**
+ * Parameter which allows extending of the initial line length for the Netty connector
+ *
+ * @since 2.44
+ */
+ public static final String
+ MAX_INITIAL_LINE_LENGTH = "jersey.config.client.netty.maxInitialLineLength";
+
+ /**
+ * Default initial line length for Netty Connector.
+ * Taken from {@link io.netty.handler.codec.http.HttpClientCodec#HttpClientCodec(int, int, int)}
+ *
+ * @since 2.44
+ */
+ public static final Integer
+ DEFAULT_INITIAL_LINE_LENGTH = 4096;
+
+ /**
+ * Parameter which allows extending of the chunk size for the Netty connector
+ *
+ * @since 2.44
+ */
+ public static final String
+ MAX_CHUNK_SIZE = "jersey.config.client.netty.maxChunkSize";
+
+ /**
+ * Default chunk size for Netty Connector.
+ * Taken from {@link io.netty.handler.codec.http.HttpClientCodec#HttpClientCodec(int, int, int)}
+ *
+ * @since 2.44
+ */
+ public static final Integer
+ DEFAULT_CHUNK_SIZE = 8192;
+
}
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 1a548dcb26..faba326475 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
@@ -302,7 +302,17 @@ protected void initChannel(SocketChannel ch) throws Exception {
p.addLast(sslHandler);
}
- p.addLast(new HttpClientCodec());
+ final Integer maxHeaderSize = ClientProperties.getValue(config.getProperties(),
+ NettyClientProperties.MAX_HEADER_SIZE,
+ NettyClientProperties.DEFAULT_HEADER_SIZE);
+ final Integer maxChunkSize = ClientProperties.getValue(config.getProperties(),
+ NettyClientProperties.MAX_CHUNK_SIZE,
+ NettyClientProperties.DEFAULT_CHUNK_SIZE);
+ 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(new ChunkedWriteHandler());
p.addLast(new HttpContentDecompressor());
}
diff --git a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HugeHeaderTest.java b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HugeHeaderTest.java
new file mode 100644
index 0000000000..6f2d433a8a
--- /dev/null
+++ b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/HugeHeaderTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.netty.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.jetty.JettyTestContainerFactory;
+import org.glassfish.jersey.test.jetty.JettyTestContainerProperties;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+import org.junit.jupiter.api.Test;
+
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class HugeHeaderTest extends JerseyTest {
+
+ private static final int SERVER_HEADER_SIZE = 1234567;
+
+ private static final String hugeHeader =
+ "abcdefghijklmnopqrstuvwxyz"
+ + "abcdefghijklmnopqrstuvwxyz"
+ + "abcdefghijklmnopqrstuvwxyz"
+ + "abcdefghijklmnopqrstuvwxyz"
+ + "abcdefghijklmnopqrstuvwxyz"
+ + "abcdefghijklmnopqrstuvwxyz"
+ + "abcdefghijklmnopqrstuvwxyz"
+ + "abcdefghijklmnopqrstuvwxyz"
+ + "abcdefghijklmnopqrstuvwxyz"
+ + "abcdefghijklmnopqrstuvwxyz";
+
+ @Path("/test")
+ public static class HttpMethodResource {
+ @POST
+ public Response post(
+ @HeaderParam("X-HUGE-HEADER") String hugeHeader,
+ String entity) {
+
+ return Response.noContent()
+ .header("X-HUGE-HEADER", hugeHeader)
+ .header("X-HUGE-HEADER-SIZE", hugeHeader.length())
+ .build();
+ }
+ }
+
+ @Override
+ protected Application configure() {
+ return new ResourceConfig(HugeHeaderTest.HttpMethodResource.class);
+ }
+
+ @Override
+ protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
+ final Map configurationProperties = new HashMap<>();
+ configurationProperties.put(JettyTestContainerProperties.HEADER_SIZE, SERVER_HEADER_SIZE);
+ return new JettyTestContainerFactory(configurationProperties);
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.connectorProvider(new NettyConnectorProvider());
+ }
+
+ @Test
+ public void testContentHeaderTrunked() {
+ final StringBuffer veryHugeHeader = new StringBuffer();
+ for (int i = 1; i < 33; i++) {
+ veryHugeHeader.append(hugeHeader);
+ }
+ final Response response = target("test").request()
+ .header("X-HUGE-HEADER", veryHugeHeader.toString())
+ .post(null);
+
+ assertNull(response.getHeaderString("X-HUGE-HEADER-SIZE"));
+ assertNull(response.getHeaderString("X-HUGE-HEADER"));
+ response.close();
+ }
+
+ @Test
+ public void testConnectorHeaderSize() {
+ final StringBuffer veryHugeHeader = new StringBuffer();
+ for (int i = 1; i < 35; i++) {
+ veryHugeHeader.append(hugeHeader);
+ }
+ int headerSize = veryHugeHeader.length();
+ Response response = target("test")
+ .property(NettyClientProperties.MAX_HEADER_SIZE, 77750)
+ .request()
+
+
+ .header("X-HUGE-HEADER", veryHugeHeader.toString())
+ .post(null);
+ assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
+
+ assertEquals(String.valueOf(headerSize), response.getHeaderString("X-HUGE-HEADER-SIZE"));
+ assertEquals(veryHugeHeader.toString(), response.getHeaderString("X-HUGE-HEADER"));
+ response.close();
+ }
+}
diff --git a/test-framework/core/src/main/java/org/glassfish/jersey/test/JerseyTest.java b/test-framework/core/src/main/java/org/glassfish/jersey/test/JerseyTest.java
index b1d8268713..33de7d989b 100644
--- a/test-framework/core/src/main/java/org/glassfish/jersey/test/JerseyTest.java
+++ b/test-framework/core/src/main/java/org/glassfish/jersey/test/JerseyTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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
@@ -621,6 +621,7 @@ public void setUp() throws Exception {
if (!isConcurrent() || activeThreadCount.getAndIncrement() == 0) {
registerLogHandlerIfEnabled();
final TestContainer testContainer = createTestContainer(context);
+ testContainer.configureContainer();
// Set current instance of test container and start it.
setTestContainer(testContainer);
diff --git a/test-framework/core/src/main/java/org/glassfish/jersey/test/spi/TestContainer.java b/test-framework/core/src/main/java/org/glassfish/jersey/test/spi/TestContainer.java
index 68a5bf8df0..d2a0030f4d 100644
--- a/test-framework/core/src/main/java/org/glassfish/jersey/test/spi/TestContainer.java
+++ b/test-framework/core/src/main/java/org/glassfish/jersey/test/spi/TestContainer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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
@@ -54,4 +54,13 @@ public interface TestContainer {
* Stop the container.
*/
public void stop();
+
+ /**
+ * optional method to configure container before it's being started
+ *
+ * @since 2.44
+ */
+ default void configureContainer() {
+
+ }
}
diff --git a/test-framework/providers/jetty/src/main/java/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java b/test-framework/providers/jetty/src/main/java/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
index 6ea1f25527..254eb75ad4 100644
--- a/test-framework/providers/jetty/src/main/java/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
+++ b/test-framework/providers/jetty/src/main/java/org/glassfish/jersey/test/jetty/JettyTestContainerFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -17,11 +17,13 @@
package org.glassfish.jersey.test.jetty;
import java.net.URI;
+import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.core.UriBuilder;
+import org.eclipse.jetty.server.HttpConnectionFactory;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
import org.glassfish.jersey.test.DeploymentContext;
@@ -42,16 +44,21 @@
*/
public class JettyTestContainerFactory implements TestContainerFactory {
+ private final Map propertiesMap;
+
private static class JettyTestContainer implements TestContainer {
private static final Logger LOGGER = Logger.getLogger(JettyTestContainer.class.getName());
+ private final Map propertiesMap;
+
private URI baseUri;
private final Server server;
-
- private JettyTestContainer(final URI baseUri, final DeploymentContext context) {
+ private JettyTestContainer(final URI baseUri, final DeploymentContext context, final Map propertiesMap) {
final URI base = UriBuilder.fromUri(baseUri).path(context.getContextPath()).build();
+ this.propertiesMap = propertiesMap;
+
if (!"/".equals(base.getRawPath())) {
throw new TestContainerException(String.format(
"Cannot deploy on %s. Jetty HTTP container only supports deployment on root path.",
@@ -68,6 +75,26 @@ private JettyTestContainer(final URI baseUri, final DeploymentContext context) {
this.server = JettyHttpContainerFactory.createServer(this.baseUri, context.getResourceConfig(), false);
}
+ @Override
+ public void configureContainer() {
+
+ if (propertiesMap == null
+ || !propertiesMap.containsKey(JettyTestContainerProperties.HEADER_SIZE)) {
+ return;
+ }
+
+ for (Connector c : server.getConnectors()) {
+ c.getConnectionFactory(HttpConnectionFactory.class)
+ .getHttpConfiguration().setRequestHeaderSize(
+ (Integer) propertiesMap.get(JettyTestContainerProperties.HEADER_SIZE));
+ c.getConnectionFactory(HttpConnectionFactory.class)
+ .getHttpConfiguration().setResponseHeaderSize(
+ (Integer) propertiesMap.get(JettyTestContainerProperties.HEADER_SIZE));
+ c.getConnectionFactory(HttpConnectionFactory.class);
+ }
+
+ }
+
@Override
public ClientConfig getClientConfig() {
return null;
@@ -123,6 +150,14 @@ public void stop() {
@Override
public TestContainer create(final URI baseUri, final DeploymentContext context) throws IllegalArgumentException {
- return new JettyTestContainer(baseUri, context);
+ return new JettyTestContainer(baseUri, context, propertiesMap);
+ }
+
+ public JettyTestContainerFactory() {
+ this(null);
+ }
+
+ public JettyTestContainerFactory(Map properties) {
+ this.propertiesMap = properties;
}
}
diff --git a/test-framework/providers/jetty/src/main/java/org/glassfish/jersey/test/jetty/JettyTestContainerProperties.java b/test-framework/providers/jetty/src/main/java/org/glassfish/jersey/test/jetty/JettyTestContainerProperties.java
new file mode 100644
index 0000000000..beabef2a8b
--- /dev/null
+++ b/test-framework/providers/jetty/src/main/java/org/glassfish/jersey/test/jetty/JettyTestContainerProperties.java
@@ -0,0 +1,36 @@
+/*
+ * 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.test.jetty;
+
+import org.glassfish.jersey.internal.util.PropertiesClass;
+
+/**
+ * Properties which relates only to Jetty test container configuration
+ *
+ * @since 2.44
+ */
+@PropertiesClass
+public class JettyTestContainerProperties {
+
+ /**
+ * Parameter which allows settings custom header size for request and response.
+ *
+ * @since 2.44
+ */
+ public static final String HEADER_SIZE = "jersey.test.jetty.container.header.size";
+
+}