From 294f33bd33cd60b56ba3bf780333575987aacea4 Mon Sep 17 00:00:00 2001 From: Sheldon Date: Tue, 29 Oct 2024 13:33:26 -0700 Subject: [PATCH 1/2] Issue #12436 - Allow headers size extend to maxRequestHeadersSize in http client. --- .../HttpClientTransportOverHTTP.java | 16 ++ .../internal/HttpSenderOverHTTP.java | 28 ++- .../eclipse/jetty/client/HttpClientTest.java | 207 ++++++++++++++++++ 3 files changed, 248 insertions(+), 3 deletions(-) diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientTransportOverHTTP.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientTransportOverHTTP.java index 61add8e5791b..08c152202801 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientTransportOverHTTP.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientTransportOverHTTP.java @@ -39,6 +39,8 @@ public class HttpClientTransportOverHTTP extends AbstractConnectorHttpClientTran private int headerCacheSize = 1024; private boolean headerCacheCaseSensitive; + private int maxRequestHeadersSize = 32 * 1024; + public HttpClientTransportOverHTTP() { this(1); @@ -127,4 +129,18 @@ public void setInitializeConnections(boolean initialize) { factory.setInitializeConnections(initialize); } + + /** + * @return The maximum allowed size in bytes for the HTTP request headers + */ + @ManagedAttribute("The maximum allowed size in bytes for the HTTP request headers") + public int getMaxRequestHeadersSize() + { + return maxRequestHeadersSize; + } + + public void setMaxRequestHeadersSize(int maxRequestHeadersSize) + { + this.maxRequestHeadersSize = maxRequestHeadersSize; + } } diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/internal/HttpSenderOverHTTP.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/internal/HttpSenderOverHTTP.java index 39a7787ace58..49f3e9ecb212 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/internal/HttpSenderOverHTTP.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/internal/HttpSenderOverHTTP.java @@ -16,7 +16,9 @@ import java.nio.ByteBuffer; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpClientTransport; import org.eclipse.jetty.client.HttpRequestException; +import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP; import org.eclipse.jetty.client.transport.HttpExchange; import org.eclipse.jetty.client.transport.HttpRequest; import org.eclipse.jetty.client.transport.HttpSender; @@ -179,9 +181,29 @@ protected Action process() throws Exception } case HEADER_OVERFLOW: { - headerBuffer.release(); - headerBuffer = null; - throw new IllegalArgumentException("Request header too large"); + int maxRequestHeadersSize = -1; + //For HTTP1.1 only + HttpClientTransport transport = httpClient.getTransport(); + if (transport instanceof HttpClientTransportOverHTTP httpTransport) + { + maxRequestHeadersSize = httpTransport.getMaxRequestHeadersSize(); + } + if (headerBuffer.capacity() < maxRequestHeadersSize) + { + RetainableByteBuffer newHeaderBuffer = bufferPool.acquire(maxRequestHeadersSize, useDirectByteBuffers); + headerBuffer.getByteBuffer().flip(); + newHeaderBuffer.getByteBuffer().put(headerBuffer.getByteBuffer()); + RetainableByteBuffer toRelease = headerBuffer; + headerBuffer = newHeaderBuffer; + toRelease.release(); + break; + } + else + { + headerBuffer.release(); + headerBuffer = null; + throw new IllegalArgumentException("Request header too large"); + } } case NEED_CHUNK: { diff --git a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java index f76edcfd7e4b..d8ad9b8c4f28 100644 --- a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java +++ b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java @@ -29,6 +29,7 @@ import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Arrays; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -56,6 +57,7 @@ import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.io.ByteArrayEndPoint; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.io.Content; import org.eclipse.jetty.io.EndPoint; @@ -76,6 +78,7 @@ import org.eclipse.jetty.util.component.LifeCycle; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ArgumentsSource; @@ -2012,4 +2015,208 @@ public void perform() .send(this); } } + + private static Random rnd = new Random(); + private static final String CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"; + + public static final int CHARS_LENGTH = CHARS.length(); + + protected static String getRandomString(int size) + { + StringBuilder sb = new StringBuilder(size); + while (sb.length() < size) + { // length of the random string. + int index = rnd.nextInt(CHARS_LENGTH); + sb.append(CHARS.charAt(index)); + } + return sb.toString(); + } + + @ParameterizedTest + @ArgumentsSource(ScenarioProvider.class) + public void testSmallHeadersSize(Scenario scenario) throws Exception + { + startClient(scenario); + ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); + HttpDestination destination = new HttpDestination(client, new Origin("http", "localhost", 8080)); + destination.start(); + HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter()); + Request request = client.newRequest(URI.create("http://localhost/")); + request.agent(getRandomString(888)); //More than the request buffer size, but less than the default max request headers size + final CountDownLatch headersLatch = new CountDownLatch(1); + final CountDownLatch successLatch = new CountDownLatch(1); + final CountDownLatch failureLatch = new CountDownLatch(1); + request.listener(new Request.Listener() + { + @Override + public void onHeaders(Request request) + { + headersLatch.countDown(); + } + + @Override + public void onSuccess(Request request) + { + successLatch.countDown(); + } + + @Override + public void onFailure(Request request, Throwable failure) + { + failureLatch.countDown(); + } + }); + connection.send(request, null); + + String requestString = endPoint.takeOutputString(); + assertTrue(requestString.startsWith("GET / HTTP/1.1\r\nAccept-Encoding: gzip\r\n")); + assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); + assertTrue(successLatch.await(5, TimeUnit.SECONDS)); + } + + @ParameterizedTest + @ArgumentsSource(ScenarioProvider.class) + public void testMaxRequestHeadersSize(Scenario scenario) throws Exception + { + startClient(scenario); + byte[] buffer = new byte[32 * 1024]; + ByteArrayEndPoint endPoint = new ByteArrayEndPoint(buffer, buffer.length); + HttpDestination destination = new HttpDestination(client, new Origin("http", "localhost", 8080)); + destination.start(); + HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter()); + Request request = client.newRequest(URI.create("http://localhost/")); + //More than the request buffer size, but less than the default max request headers size + + int desiredHeadersSize = 20 * 1024; + int currentHeadersSize = 0; + int i = 0; + while (currentHeadersSize < desiredHeadersSize) + { + final int index = i++; + final String headerValue = getRandomString(800); + final int headerSize = headerValue.length(); + currentHeadersSize += headerSize; + request.cookie(new HttpCookie() + { + @Override + public String getName() + { + return "large" + index; + } + + @Override + public String getValue() + { + return headerValue; + } + + @Override + public int getVersion() + { + return 0; + } + + @Override + public Map getAttributes() + { + return new HashMap<>(); + } + }); + } + + final CountDownLatch headersLatch = new CountDownLatch(1); + final CountDownLatch successLatch = new CountDownLatch(1); + request.listener(new Request.Listener() + { + @Override + public void onHeaders(Request request) + { + headersLatch.countDown(); + } + + @Override + public void onSuccess(Request request) + { + successLatch.countDown(); + } + }); + connection.send(request, null); + + String requestString = endPoint.takeOutputString(); + assertTrue(requestString.startsWith("GET / HTTP/1.1\r\nAccept-Encoding: gzip\r\n")); + assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); + assertTrue(successLatch.await(5, TimeUnit.SECONDS)); + } + + @ParameterizedTest + @ArgumentsSource(ScenarioProvider.class) + public void testMaxRequestHeadersSizeOverflow(Scenario scenario) throws Exception + { + startClient(scenario); + byte[] buffer = new byte[32 * 1024]; + ByteArrayEndPoint endPoint = new ByteArrayEndPoint(buffer, buffer.length); + HttpDestination destination = new HttpDestination(client, new Origin("http", "localhost", 8080)); + destination.start(); + HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter()); + Request request = client.newRequest(URI.create("http://localhost/")); + //More than the request buffer size, but less than the default max request headers size + + int desiredHeadersSize = 35 * 1024; + int currentHeadersSize = 0; + int i = 0; + while (currentHeadersSize < desiredHeadersSize) + { + final int index = i++; + final String headerValue = getRandomString(800); + final int headerSize = headerValue.length(); + currentHeadersSize += headerSize; + request.cookie(new HttpCookie() + { + @Override + public String getName() + { + return "large" + index; + } + + @Override + public String getValue() + { + return headerValue; + } + + @Override + public int getVersion() + { + return 0; + } + + @Override + public Map getAttributes() + { + return new HashMap<>(); + } + }); + } + + final CountDownLatch headersLatch = new CountDownLatch(1); + final CountDownLatch failureLatch = new CountDownLatch(1); + request.listener(new Request.Listener() + { + @Override + public void onHeaders(Request request) + { + headersLatch.countDown(); + } + + @Override + public void onFailure(Request request, Throwable failure) + { + failureLatch.countDown(); + } + }); + connection.send(request, null); + + assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); + assertTrue(failureLatch.await(5, TimeUnit.SECONDS)); + } } From 042e2a3df23479185d5394294b1d4f39dbb5f0e0 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Sat, 16 Nov 2024 00:06:13 +0100 Subject: [PATCH 2/2] Fixes #12436 - Allow headers size extend to maxRequestHeadersSize in http client. Reworked contribution by @shaoxt in light of the work done in #12351. Signed-off-by: Simone Bordet --- .../org/eclipse/jetty/client/HttpClient.java | 19 ++ .../HttpClientTransportOverHTTP.java | 16 -- .../internal/HttpSenderOverHTTP.java | 30 +-- .../eclipse/jetty/client/HttpClientTest.java | 251 +++--------------- .../HttpClientTransportOverHTTP2.java | 2 +- .../HttpClientTransportOverHTTP2Test.java | 2 +- .../HttpClientTransportOverHTTP3.java | 1 + .../HttpClientTransportOverHTTP3Test.java | 1 + 8 files changed, 77 insertions(+), 245 deletions(-) diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index 86bdbe7d2d82..387c8db7feca 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -135,6 +135,7 @@ public class HttpClient extends ContainerLifeCycle implements AutoCloseable private String defaultRequestContentType = "application/octet-stream"; private boolean useInputDirectByteBuffers = true; private boolean useOutputDirectByteBuffers = true; + private int maxRequestHeadersSize = 8192; private int maxResponseHeadersSize = -1; private Sweeper destinationSweeper; @@ -1071,6 +1072,24 @@ public void setUseInputDirectByteBuffers(boolean useInputDirectByteBuffers) this.useInputDirectByteBuffers = useInputDirectByteBuffers; } + /** + * @return the max size in bytes of the request headers + */ + @ManagedAttribute("The max size in bytes of the request headers") + public int getMaxRequestHeadersSize() + { + return maxRequestHeadersSize; + } + + /** + * Set the max size in bytes of the request headers. + * @param maxRequestHeadersSize the max size in bytes of the request headers + */ + public void setMaxRequestHeadersSize(int maxRequestHeadersSize) + { + this.maxRequestHeadersSize = maxRequestHeadersSize; + } + /** * @return whether to use direct ByteBuffers for writing */ diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientTransportOverHTTP.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientTransportOverHTTP.java index 08c152202801..61add8e5791b 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientTransportOverHTTP.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpClientTransportOverHTTP.java @@ -39,8 +39,6 @@ public class HttpClientTransportOverHTTP extends AbstractConnectorHttpClientTran private int headerCacheSize = 1024; private boolean headerCacheCaseSensitive; - private int maxRequestHeadersSize = 32 * 1024; - public HttpClientTransportOverHTTP() { this(1); @@ -129,18 +127,4 @@ public void setInitializeConnections(boolean initialize) { factory.setInitializeConnections(initialize); } - - /** - * @return The maximum allowed size in bytes for the HTTP request headers - */ - @ManagedAttribute("The maximum allowed size in bytes for the HTTP request headers") - public int getMaxRequestHeadersSize() - { - return maxRequestHeadersSize; - } - - public void setMaxRequestHeadersSize(int maxRequestHeadersSize) - { - this.maxRequestHeadersSize = maxRequestHeadersSize; - } } diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/internal/HttpSenderOverHTTP.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/internal/HttpSenderOverHTTP.java index 49f3e9ecb212..8edfea405d76 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/internal/HttpSenderOverHTTP.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/internal/HttpSenderOverHTTP.java @@ -16,9 +16,7 @@ import java.nio.ByteBuffer; import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.client.HttpClientTransport; import org.eclipse.jetty.client.HttpRequestException; -import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP; import org.eclipse.jetty.client.transport.HttpExchange; import org.eclipse.jetty.client.transport.HttpRequest; import org.eclipse.jetty.client.transport.HttpSender; @@ -52,6 +50,7 @@ public class HttpSenderOverHTTP extends HttpSender public HttpSenderOverHTTP(HttpChannelOverHTTP channel) { super(channel); + generator.setMaxHeaderBytes(channel.getHttpDestination().getHttpClient().getMaxRequestHeadersSize()); } @Override @@ -160,6 +159,7 @@ protected Action process() throws Exception HttpClient httpClient = getHttpChannel().getHttpDestination().getHttpClient(); HttpExchange exchange = getHttpExchange(); ByteBufferPool bufferPool = httpClient.getByteBufferPool(); + int requestHeadersSize = httpClient.getRequestBufferSize(); boolean useDirectByteBuffers = httpClient.isUseOutputDirectByteBuffers(); while (true) { @@ -176,33 +176,23 @@ protected Action process() throws Exception { case NEED_HEADER: { - headerBuffer = bufferPool.acquire(httpClient.getRequestBufferSize(), useDirectByteBuffers); + headerBuffer = bufferPool.acquire(requestHeadersSize, useDirectByteBuffers); break; } case HEADER_OVERFLOW: { - int maxRequestHeadersSize = -1; - //For HTTP1.1 only - HttpClientTransport transport = httpClient.getTransport(); - if (transport instanceof HttpClientTransportOverHTTP httpTransport) + int maxRequestHeadersSize = httpClient.getMaxRequestHeadersSize(); + if (maxRequestHeadersSize > requestHeadersSize) { - maxRequestHeadersSize = httpTransport.getMaxRequestHeadersSize(); - } - if (headerBuffer.capacity() < maxRequestHeadersSize) - { - RetainableByteBuffer newHeaderBuffer = bufferPool.acquire(maxRequestHeadersSize, useDirectByteBuffers); - headerBuffer.getByteBuffer().flip(); - newHeaderBuffer.getByteBuffer().put(headerBuffer.getByteBuffer()); - RetainableByteBuffer toRelease = headerBuffer; - headerBuffer = newHeaderBuffer; - toRelease.release(); + generator.reset(); + headerBuffer.release(); + headerBuffer = bufferPool.acquire(maxRequestHeadersSize, useDirectByteBuffers); + requestHeadersSize = maxRequestHeadersSize; break; } else { - headerBuffer.release(); - headerBuffer = null; - throw new IllegalArgumentException("Request header too large"); + throw new IllegalArgumentException("Request headers too large"); } } case NEED_CHUNK: diff --git a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java index d8ad9b8c4f28..785115587631 100644 --- a/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java +++ b/jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java @@ -29,7 +29,6 @@ import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Arrays; -import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -57,12 +56,13 @@ import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.io.ByteArrayEndPoint; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.io.Content; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.logging.StacklessLogging; import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.internal.HttpChannelState; import org.eclipse.jetty.toolchain.test.Net; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; @@ -78,7 +78,6 @@ import org.eclipse.jetty.util.component.LifeCycle; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ArgumentsSource; @@ -1939,6 +1938,48 @@ protected void service(org.eclipse.jetty.server.Request request, org.eclipse.jet assertEquals(HttpStatus.OK_200, response.getStatus()); } + @ParameterizedTest + @ArgumentsSource(ScenarioProvider.class) + public void testRequestHeadersSizeOverflow(Scenario scenario) throws Exception + { + start(scenario, new EmptyServerHandler()); + + RetainableByteBuffer.Mutable buffer = client.getByteBufferPool().acquire(client.getRequestBufferSize(), false); + int capacity = buffer.capacity(); + buffer.release(); + client.setMaxRequestHeadersSize(3 * capacity); + connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setRequestHeaderSize(3 * capacity); + + ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) + .scheme(scenario.getScheme()) + // Overflow the request headers size, but don't exceed the max. + .agent("A".repeat(2 * capacity)) + .timeout(5, TimeUnit.SECONDS) + .send(); + + assertEquals(HttpStatus.OK_200, response.getStatus()); + } + + @ParameterizedTest + @ArgumentsSource(ScenarioProvider.class) + public void testMaxRequestHeadersSize(Scenario scenario) throws Exception + { + start(scenario, new EmptyServerHandler()); + + RetainableByteBuffer.Mutable buffer = client.getByteBufferPool().acquire(client.getRequestBufferSize(), false); + int capacity = buffer.capacity(); + buffer.release(); + client.setMaxRequestHeadersSize(2 * capacity); + connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setRequestHeaderSize(4 * capacity); + + assertThrows(ExecutionException.class, () -> client.newRequest("localhost", connector.getLocalPort()) + .scheme(scenario.getScheme()) + // Overflow the max request headers size. + .agent("A".repeat(3 * capacity)) + .timeout(5, TimeUnit.SECONDS) + .send()); + } + private void assertCopyRequest(Request original) { Request copy = client.copyRequest(original, original.getURI()); @@ -2015,208 +2056,4 @@ public void perform() .send(this); } } - - private static Random rnd = new Random(); - private static final String CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"; - - public static final int CHARS_LENGTH = CHARS.length(); - - protected static String getRandomString(int size) - { - StringBuilder sb = new StringBuilder(size); - while (sb.length() < size) - { // length of the random string. - int index = rnd.nextInt(CHARS_LENGTH); - sb.append(CHARS.charAt(index)); - } - return sb.toString(); - } - - @ParameterizedTest - @ArgumentsSource(ScenarioProvider.class) - public void testSmallHeadersSize(Scenario scenario) throws Exception - { - startClient(scenario); - ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); - HttpDestination destination = new HttpDestination(client, new Origin("http", "localhost", 8080)); - destination.start(); - HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter()); - Request request = client.newRequest(URI.create("http://localhost/")); - request.agent(getRandomString(888)); //More than the request buffer size, but less than the default max request headers size - final CountDownLatch headersLatch = new CountDownLatch(1); - final CountDownLatch successLatch = new CountDownLatch(1); - final CountDownLatch failureLatch = new CountDownLatch(1); - request.listener(new Request.Listener() - { - @Override - public void onHeaders(Request request) - { - headersLatch.countDown(); - } - - @Override - public void onSuccess(Request request) - { - successLatch.countDown(); - } - - @Override - public void onFailure(Request request, Throwable failure) - { - failureLatch.countDown(); - } - }); - connection.send(request, null); - - String requestString = endPoint.takeOutputString(); - assertTrue(requestString.startsWith("GET / HTTP/1.1\r\nAccept-Encoding: gzip\r\n")); - assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); - assertTrue(successLatch.await(5, TimeUnit.SECONDS)); - } - - @ParameterizedTest - @ArgumentsSource(ScenarioProvider.class) - public void testMaxRequestHeadersSize(Scenario scenario) throws Exception - { - startClient(scenario); - byte[] buffer = new byte[32 * 1024]; - ByteArrayEndPoint endPoint = new ByteArrayEndPoint(buffer, buffer.length); - HttpDestination destination = new HttpDestination(client, new Origin("http", "localhost", 8080)); - destination.start(); - HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter()); - Request request = client.newRequest(URI.create("http://localhost/")); - //More than the request buffer size, but less than the default max request headers size - - int desiredHeadersSize = 20 * 1024; - int currentHeadersSize = 0; - int i = 0; - while (currentHeadersSize < desiredHeadersSize) - { - final int index = i++; - final String headerValue = getRandomString(800); - final int headerSize = headerValue.length(); - currentHeadersSize += headerSize; - request.cookie(new HttpCookie() - { - @Override - public String getName() - { - return "large" + index; - } - - @Override - public String getValue() - { - return headerValue; - } - - @Override - public int getVersion() - { - return 0; - } - - @Override - public Map getAttributes() - { - return new HashMap<>(); - } - }); - } - - final CountDownLatch headersLatch = new CountDownLatch(1); - final CountDownLatch successLatch = new CountDownLatch(1); - request.listener(new Request.Listener() - { - @Override - public void onHeaders(Request request) - { - headersLatch.countDown(); - } - - @Override - public void onSuccess(Request request) - { - successLatch.countDown(); - } - }); - connection.send(request, null); - - String requestString = endPoint.takeOutputString(); - assertTrue(requestString.startsWith("GET / HTTP/1.1\r\nAccept-Encoding: gzip\r\n")); - assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); - assertTrue(successLatch.await(5, TimeUnit.SECONDS)); - } - - @ParameterizedTest - @ArgumentsSource(ScenarioProvider.class) - public void testMaxRequestHeadersSizeOverflow(Scenario scenario) throws Exception - { - startClient(scenario); - byte[] buffer = new byte[32 * 1024]; - ByteArrayEndPoint endPoint = new ByteArrayEndPoint(buffer, buffer.length); - HttpDestination destination = new HttpDestination(client, new Origin("http", "localhost", 8080)); - destination.start(); - HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter()); - Request request = client.newRequest(URI.create("http://localhost/")); - //More than the request buffer size, but less than the default max request headers size - - int desiredHeadersSize = 35 * 1024; - int currentHeadersSize = 0; - int i = 0; - while (currentHeadersSize < desiredHeadersSize) - { - final int index = i++; - final String headerValue = getRandomString(800); - final int headerSize = headerValue.length(); - currentHeadersSize += headerSize; - request.cookie(new HttpCookie() - { - @Override - public String getName() - { - return "large" + index; - } - - @Override - public String getValue() - { - return headerValue; - } - - @Override - public int getVersion() - { - return 0; - } - - @Override - public Map getAttributes() - { - return new HashMap<>(); - } - }); - } - - final CountDownLatch headersLatch = new CountDownLatch(1); - final CountDownLatch failureLatch = new CountDownLatch(1); - request.listener(new Request.Listener() - { - @Override - public void onHeaders(Request request) - { - headersLatch.countDown(); - } - - @Override - public void onFailure(Request request, Throwable failure) - { - failureLatch.countDown(); - } - }); - connection.send(request, null); - - assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); - assertTrue(failureLatch.await(5, TimeUnit.SECONDS)); - } } diff --git a/jetty-core/jetty-http2/jetty-http2-client-transport/src/main/java/org/eclipse/jetty/http2/client/transport/HttpClientTransportOverHTTP2.java b/jetty-core/jetty-http2/jetty-http2-client-transport/src/main/java/org/eclipse/jetty/http2/client/transport/HttpClientTransportOverHTTP2.java index d87442ab436c..72b2a4367cf9 100644 --- a/jetty-core/jetty-http2/jetty-http2-client-transport/src/main/java/org/eclipse/jetty/http2/client/transport/HttpClientTransportOverHTTP2.java +++ b/jetty-core/jetty-http2/jetty-http2-client-transport/src/main/java/org/eclipse/jetty/http2/client/transport/HttpClientTransportOverHTTP2.java @@ -103,7 +103,7 @@ static void configure(HttpClient httpClient, HTTP2Client http2Client) http2Client.setUseOutputDirectByteBuffers(httpClient.isUseOutputDirectByteBuffers()); http2Client.setConnectBlocking(httpClient.isConnectBlocking()); http2Client.setBindAddress(httpClient.getBindAddress()); - http2Client.setMaxRequestHeadersSize(httpClient.getRequestBufferSize()); + http2Client.setMaxRequestHeadersSize(httpClient.getMaxRequestHeadersSize()); http2Client.setMaxResponseHeadersSize(httpClient.getMaxResponseHeadersSize()); } diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HttpClientTransportOverHTTP2Test.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HttpClientTransportOverHTTP2Test.java index 0bd1df1c8c25..f6b936ece250 100644 --- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HttpClientTransportOverHTTP2Test.java +++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HttpClientTransportOverHTTP2Test.java @@ -145,7 +145,7 @@ private void testPropertiesAreForwarded(HTTP2Client http2Client, HttpClientTrans assertEquals(httpClient.getIdleTimeout(), http2Client.getIdleTimeout()); assertEquals(httpClient.isUseInputDirectByteBuffers(), http2Client.isUseInputDirectByteBuffers()); assertEquals(httpClient.isUseOutputDirectByteBuffers(), http2Client.isUseOutputDirectByteBuffers()); - assertEquals(httpClient.getRequestBufferSize(), http2Client.getMaxRequestHeadersSize()); + assertEquals(httpClient.getMaxRequestHeadersSize(), http2Client.getMaxRequestHeadersSize()); assertEquals(httpClient.getMaxResponseHeadersSize(), http2Client.getMaxResponseHeadersSize()); } assertTrue(http2Client.isStopped()); diff --git a/jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/HttpClientTransportOverHTTP3.java b/jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/HttpClientTransportOverHTTP3.java index c828c3028957..58eee23d0016 100644 --- a/jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/HttpClientTransportOverHTTP3.java +++ b/jetty-core/jetty-http3/jetty-http3-client-transport/src/main/java/org/eclipse/jetty/http3/client/transport/HttpClientTransportOverHTTP3.java @@ -83,6 +83,7 @@ static void configure(HttpClient httpClient, HTTP3Client http3Client) configuration.setInputBufferSize(httpClient.getResponseBufferSize()); configuration.setUseInputDirectByteBuffers(httpClient.isUseInputDirectByteBuffers()); configuration.setUseOutputDirectByteBuffers(httpClient.isUseOutputDirectByteBuffers()); + configuration.setMaxRequestHeadersSize(httpClient.getMaxRequestHeadersSize()); configuration.setMaxResponseHeadersSize(httpClient.getMaxResponseHeadersSize()); } diff --git a/jetty-core/jetty-http3/jetty-http3-tests/src/test/java/org/eclipse/jetty/http3/tests/HttpClientTransportOverHTTP3Test.java b/jetty-core/jetty-http3/jetty-http3-tests/src/test/java/org/eclipse/jetty/http3/tests/HttpClientTransportOverHTTP3Test.java index efb7c3442470..9a457a8c6393 100644 --- a/jetty-core/jetty-http3/jetty-http3-tests/src/test/java/org/eclipse/jetty/http3/tests/HttpClientTransportOverHTTP3Test.java +++ b/jetty-core/jetty-http3/jetty-http3-tests/src/test/java/org/eclipse/jetty/http3/tests/HttpClientTransportOverHTTP3Test.java @@ -92,6 +92,7 @@ private void testPropertiesAreForwarded(HTTP3Client http3Client, HttpClientTrans HTTP3Configuration http3Configuration = http3Client.getHTTP3Configuration(); assertEquals(httpClient.isUseInputDirectByteBuffers(), http3Configuration.isUseInputDirectByteBuffers()); assertEquals(httpClient.isUseOutputDirectByteBuffers(), http3Configuration.isUseOutputDirectByteBuffers()); + assertEquals(httpClient.getMaxRequestHeadersSize(), http3Configuration.getMaxRequestHeadersSize()); assertEquals(httpClient.getMaxResponseHeadersSize(), http3Configuration.getMaxResponseHeadersSize()); } assertTrue(http3Client.isStopped());