From 423555eaa7d9e380e4d961f29e120326f5e09c5c Mon Sep 17 00:00:00 2001 From: Sheldon Date: Tue, 29 Oct 2024 13:33:26 -0700 Subject: [PATCH 01/11] Allow headers size extend to maxRequestHeadersSize in http client --- .../HttpClientTransportOverHTTP.java | 14 ++ .../internal/HttpSenderOverHTTP.java | 57 +++-- .../eclipse/jetty/client/HttpClientTest.java | 207 +++++++++++++++++- 3 files changed, 254 insertions(+), 24 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..925c9920491c 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,16 @@ 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..6c3a2731dec2 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; @@ -26,7 +28,6 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Content; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.Retainable; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -179,9 +180,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: { @@ -238,9 +259,8 @@ protected Action process() throws Exception @Override protected void onSuccess() { - headerBuffer = Retainable.release(headerBuffer); - chunkBuffer = Retainable.release(chunkBuffer); - contentByteBuffer = null; + release(); + super.succeeded(); } @Override @@ -251,16 +271,20 @@ protected void onCompleteSuccess() } @Override - protected void onFailure(Throwable cause) + protected void onCompleteFailure(Throwable cause) { - callback.failed(cause); + super.onCompleteFailure(cause); + release(); } - @Override - protected void onCompleteFailure(Throwable cause) + private void release() { - headerBuffer = Retainable.release(headerBuffer); - chunkBuffer = Retainable.release(chunkBuffer); + if (headerBuffer != null) + headerBuffer.release(); + headerBuffer = null; + if (chunkBuffer != null) + chunkBuffer.release(); + chunkBuffer = null; contentByteBuffer = null; } } @@ -332,16 +356,11 @@ protected Action process() throws Exception } } - @Override - protected void onFailure(Throwable cause) - { - callback.failed(cause); - } - @Override protected void onCompleteFailure(Throwable cause) { release(); + callback.failed(cause); } private void release() 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..bd1d48d83be3 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 @@ -28,11 +28,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Random; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Exchanger; @@ -56,6 +52,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 +73,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 +2010,203 @@ 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(); + } + + @Test + public void testSmallHeadersSize() throws Exception + { + 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)); + } + + @Test + public void testMaxRequestHeadersSize() throws Exception + { + 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)); + } + + @Test + public void testMaxRequestHeadersSizeOverflow() throws Exception + { + 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 84a617bb1178a39713efeb6f9139d08197b2eb10 Mon Sep 17 00:00:00 2001 From: Sheldon Date: Thu, 7 Nov 2024 09:56:49 -0800 Subject: [PATCH 02/11] Remove merged changes --- .../internal/HttpSenderOverHTTP.java | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) 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 6c3a2731dec2..f7f9ec25e55c 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 @@ -27,6 +27,7 @@ import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.io.Retainable; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.util.BufferUtil; @@ -259,8 +260,9 @@ protected Action process() throws Exception @Override protected void onSuccess() { - release(); - super.succeeded(); + headerBuffer = Retainable.release(headerBuffer); + chunkBuffer = Retainable.release(chunkBuffer); + contentByteBuffer = null; } @Override @@ -269,22 +271,17 @@ protected void onCompleteSuccess() super.onCompleteSuccess(); callback.succeeded(); } - @Override - protected void onCompleteFailure(Throwable cause) + protected void onFailure(Throwable cause) { - super.onCompleteFailure(cause); - release(); + callback.failed(cause); } - private void release() + @Override + protected void onCompleteFailure(Throwable cause) { - if (headerBuffer != null) - headerBuffer.release(); - headerBuffer = null; - if (chunkBuffer != null) - chunkBuffer.release(); - chunkBuffer = null; + headerBuffer = Retainable.release(headerBuffer); + chunkBuffer = Retainable.release(chunkBuffer); contentByteBuffer = null; } } From b678efbaff135676dc8c4e6da5b90f06a5c3fd0d Mon Sep 17 00:00:00 2001 From: Sheldon Date: Thu, 7 Nov 2024 10:01:03 -0800 Subject: [PATCH 03/11] Remove merged changes --- .../jetty/client/transport/internal/HttpSenderOverHTTP.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f7f9ec25e55c..fb04b68a0764 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 @@ -271,6 +271,7 @@ protected void onCompleteSuccess() super.onCompleteSuccess(); callback.succeeded(); } + @Override protected void onFailure(Throwable cause) { @@ -357,7 +358,6 @@ protected Action process() throws Exception protected void onCompleteFailure(Throwable cause) { release(); - callback.failed(cause); } private void release() From e03eb26ecea3f686e4480016c5c83e77bc976bd5 Mon Sep 17 00:00:00 2001 From: Sheldon Date: Thu, 7 Nov 2024 10:01:55 -0800 Subject: [PATCH 04/11] Remove merged changes --- .../jetty/client/transport/internal/HttpSenderOverHTTP.java | 6 ++++++ 1 file changed, 6 insertions(+) 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 fb04b68a0764..35f3949548a9 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 @@ -354,6 +354,12 @@ protected Action process() throws Exception } } + @Override + protected void onFailure(Throwable cause) + { + callback.failed(cause); + } + @Override protected void onCompleteFailure(Throwable cause) { From a6384d6d33e7271de241ca07010e6ba97bf2916f Mon Sep 17 00:00:00 2001 From: Sheldon Date: Thu, 7 Nov 2024 10:02:30 -0800 Subject: [PATCH 05/11] Remove merged changes --- .../jetty/client/transport/internal/HttpSenderOverHTTP.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 35f3949548a9..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 @@ -27,8 +27,8 @@ import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Content; -import org.eclipse.jetty.io.Retainable; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.Retainable; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; From 94ad986ef4a4f67f9d808a151eca174c5e9ad0c9 Mon Sep 17 00:00:00 2001 From: Sheldon Date: Tue, 29 Oct 2024 13:33:26 -0700 Subject: [PATCH 06/11] Allow headers size extend to maxRequestHeadersSize in http client --- .../HttpClientTransportOverHTTP.java | 14 ++ .../internal/HttpSenderOverHTTP.java | 28 ++- .../eclipse/jetty/client/HttpClientTest.java | 207 +++++++++++++++++- 3 files changed, 241 insertions(+), 8 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..925c9920491c 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,16 @@ 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..bd1d48d83be3 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 @@ -28,11 +28,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Random; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Exchanger; @@ -56,6 +52,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 +73,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 +2010,203 @@ 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(); + } + + @Test + public void testSmallHeadersSize() throws Exception + { + 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)); + } + + @Test + public void testMaxRequestHeadersSize() throws Exception + { + 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)); + } + + @Test + public void testMaxRequestHeadersSizeOverflow() throws Exception + { + 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 f0114e7165572150f582f834c6cfbde3fd0abce9 Mon Sep 17 00:00:00 2001 From: Sheldon Date: Thu, 7 Nov 2024 10:17:06 -0800 Subject: [PATCH 07/11] Fix build error --- .../client/transport/HttpClientTransportOverHTTP.java | 6 ++++-- .../test/java/org/eclipse/jetty/client/HttpClientTest.java | 7 ++++++- 2 files changed, 10 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 925c9920491c..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 @@ -134,11 +134,13 @@ public void setInitializeConnections(boolean 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() { + public int getMaxRequestHeadersSize() + { return maxRequestHeadersSize; } - public void setMaxRequestHeadersSize(int maxRequestHeadersSize) { + public void setMaxRequestHeadersSize(int maxRequestHeadersSize) + { this.maxRequestHeadersSize = maxRequestHeadersSize; } } 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 bd1d48d83be3..606d44c4c3b9 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 @@ -28,7 +28,12 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; -import java.util.*; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.HashMap; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Exchanger; From 691383928c9eeef1527659e1bb72f245defc4bc2 Mon Sep 17 00:00:00 2001 From: Sheldon Date: Thu, 7 Nov 2024 10:25:09 -0800 Subject: [PATCH 08/11] Fix build error --- .../jetty/client/transport/HttpClientTransportOverHTTP.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 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 925c9920491c..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 @@ -134,11 +134,13 @@ public void setInitializeConnections(boolean 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() { + public int getMaxRequestHeadersSize() + { return maxRequestHeadersSize; } - public void setMaxRequestHeadersSize(int maxRequestHeadersSize) { + public void setMaxRequestHeadersSize(int maxRequestHeadersSize) + { this.maxRequestHeadersSize = maxRequestHeadersSize; } } From 5c091237631150b5f84d82ce0c9d0de3d32d3ec6 Mon Sep 17 00:00:00 2001 From: Sheldon Date: Thu, 7 Nov 2024 10:50:36 -0800 Subject: [PATCH 09/11] Fix build error --- .../src/test/java/org/eclipse/jetty/client/HttpClientTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 606d44c4c3b9..99a72858d60b 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 @@ -32,8 +32,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Random; import java.util.HashMap; +import java.util.Random; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Exchanger; @@ -2016,7 +2016,6 @@ public void perform() } } - private static Random rnd = new Random(); private static final String CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"; From fcc5d2c390e3f8476dfe015ac555b1a34afff7d0 Mon Sep 17 00:00:00 2001 From: Sheldon Date: Thu, 7 Nov 2024 11:00:28 -0800 Subject: [PATCH 10/11] Fix import order --- .../src/test/java/org/eclipse/jetty/client/HttpClientTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 99a72858d60b..4c96345acc95 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,10 +29,10 @@ 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; -import java.util.HashMap; import java.util.Random; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; From 8a2a3cc6445ab45e2b4fa077f7f46e80186f05d1 Mon Sep 17 00:00:00 2001 From: Sheldon Date: Tue, 12 Nov 2024 09:59:48 -0800 Subject: [PATCH 11/11] Fix test case error --- .../eclipse/jetty/client/HttpClientTest.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) 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 4c96345acc95..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 @@ -2032,9 +2032,11 @@ protected static String getRandomString(int size) return sb.toString(); } - @Test - public void testSmallHeadersSize() throws Exception + @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(); @@ -2072,9 +2074,11 @@ public void onFailure(Request request, Throwable failure) assertTrue(successLatch.await(5, TimeUnit.SECONDS)); } - @Test - public void testMaxRequestHeadersSize() throws Exception + @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)); @@ -2144,9 +2148,11 @@ public void onSuccess(Request request) assertTrue(successLatch.await(5, TimeUnit.SECONDS)); } - @Test - public void testMaxRequestHeadersSizeOverflow() throws Exception + @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));