From fc762aea7d09ad21fbd3769e6dace73680896b6e Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 19 Jan 2023 10:31:51 -0600 Subject: [PATCH] Remove `@Disabled` from more `jetty-core/jetty-server` tests (#9162) * Remove @Disabled from ConnectorTimeoutTests * Remove @Disabled from SlowClientsTest * Remove @Disabled from SslConnectionFactoryTest * Remove @Disabled from DetectorConnectionTest * Disabling quiche from checkstyle * Removing ConnectorTimeoutTest BlockingTimeout tests + The concept of HttpConfiguration.blockingTimeout has been removed, these tests are no longer relevant --- .../src/main/resources/jetty-checkstyle.xml | 6 + .../jetty/server/ConnectorTimeoutTest.java | 398 ++++++------------ .../jetty/server/DetectorConnectionTest.java | 194 +++++---- .../server/ServerConnectorTimeoutTest.java | 167 +------- ...ava => ServerConnectorSslTimeoutTest.java} | 28 +- .../jetty/server/ssl/SlowClientsTest.java | 192 +++++---- .../server/ssl/SslConnectionFactoryTest.java | 100 +++-- 7 files changed, 419 insertions(+), 666 deletions(-) rename jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/{SslSelectChannelTimeoutTest.java => ServerConnectorSslTimeoutTest.java} (81%) diff --git a/build/build-resources/src/main/resources/jetty-checkstyle.xml b/build/build-resources/src/main/resources/jetty-checkstyle.xml index 55f68602436..02a6b587aec 100644 --- a/build/build-resources/src/main/resources/jetty-checkstyle.xml +++ b/build/build-resources/src/main/resources/jetty-checkstyle.xml @@ -20,6 +20,12 @@ + + + + + + diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java index 7fce755ad3f..379aca089f7 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ConnectorTimeoutTest.java @@ -13,10 +13,8 @@ package org.eclipse.jetty.server; -import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; import java.nio.ByteBuffer; @@ -27,20 +25,17 @@ import java.util.concurrent.Exchanger; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLHandshakeException; -import org.eclipse.jetty.io.AbstractConnection; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.io.ByteBufferAccumulator; import org.eclipse.jetty.io.Content; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.ssl.SslConnection; -import org.eclipse.jetty.logging.StacklessLogging; import org.eclipse.jetty.server.handler.EchoHandler; -import org.eclipse.jetty.server.internal.HttpChannelState; import org.eclipse.jetty.util.Blocker; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.NanoTime; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,12 +43,9 @@ import org.slf4j.LoggerFactory; import static java.time.Duration.ofSeconds; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThan; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.startsWith; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; @@ -101,7 +93,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture "GET / HTTP/1.0\r\n" + "host: localhost:" + _serverURI.getPort() + "\r\n" + "connection: keep-alive\r\n" + - "\r\n").getBytes("utf-8")); + "\r\n").getBytes(StandardCharsets.UTF_8)); os.flush(); IO.toString(is); @@ -132,13 +124,13 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture assertTimeoutPreemptively(ofSeconds(10), () -> { String content = "Wibble"; - byte[] contentB = content.getBytes("utf-8"); + byte[] contentB = content.getBytes(StandardCharsets.UTF_8); os.write(( "POST /echo HTTP/1.1\r\n" + "host: localhost:" + _serverURI.getPort() + "\r\n" + "content-type: text/plain; charset=utf-8\r\n" + "content-length: " + contentB.length + "\r\n" + - "\r\n").getBytes("utf-8")); + "\r\n").getBytes(StandardCharsets.UTF_8)); os.write(contentB); os.flush(); @@ -185,7 +177,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture "GET / HTTP/1.0\r\n" + "host: localhost:" + _serverURI.getPort() + "\r\n" + "connection: close\r\n" + - "\r\n").getBytes("utf-8")); + "\r\n").getBytes(StandardCharsets.UTF_8)); os.flush(); // Get the server side endpoint @@ -240,14 +232,14 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture InputStream is = client.getInputStream(); String content = "Wibble"; - byte[] contentB = content.getBytes("utf-8"); + byte[] contentB = content.getBytes(StandardCharsets.UTF_8); os.write(( "POST /echo HTTP/1.1\r\n" + "host: localhost:" + _serverURI.getPort() + "\r\n" + "content-type: text/plain; charset=utf-8\r\n" + "content-length: " + contentB.length + "\r\n" + "connection: close\r\n" + - "\r\n").getBytes("utf-8")); + "\r\n").getBytes(StandardCharsets.UTF_8)); os.write(contentB); os.flush(); @@ -273,212 +265,6 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture assertFalse(((Channel)transport).isOpen()); } - @Test - @Tag("Unstable") - @Disabled // TODO make more stable - public void testNoBlockingTimeoutRead() throws Exception - { - startServer(new EchoHandler()); - Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort()); - client.setSoTimeout(10000); - InputStream is = client.getInputStream(); - assertFalse(client.isClosed()); - - long start = NanoTime.now(); - - OutputStream os = client.getOutputStream(); - os.write(("GET / HTTP/1.1\r\n" + - "host: localhost:" + _serverURI.getPort() + "\r\n" + - "Transfer-Encoding: chunked\r\n" + - "Content-Type: text/plain\r\n" + - "Connection: close\r\n" + - "\r\n" + - "5\r\n" + - "LMNOP\r\n") - .getBytes("utf-8")); - os.flush(); - - try - { - Thread.sleep(250); - os.write("1".getBytes("utf-8")); - os.flush(); - Thread.sleep(250); - os.write("0".getBytes("utf-8")); - os.flush(); - Thread.sleep(250); - os.write("\r".getBytes("utf-8")); - os.flush(); - Thread.sleep(250); - os.write("\n".getBytes("utf-8")); - os.flush(); - Thread.sleep(250); - os.write("0123456789ABCDEF\r\n".getBytes("utf-8")); - os.write("0\r\n".getBytes("utf-8")); - os.write("\r\n".getBytes("utf-8")); - os.flush(); - } - catch (Exception e) - { - e.printStackTrace(); - } - long duration = NanoTime.millisSince(start); - assertThat(duration, greaterThan(500L)); - - assertTimeoutPreemptively(ofSeconds(10), () -> - { - // read the response - String response = IO.toString(is); - assertThat(response, startsWith("HTTP/1.1 200 OK")); - assertThat(response, containsString("LMNOP0123456789ABCDEF")); - }); - } - - @Test - @Tag("Unstable") - @Disabled // TODO make more stable - public void testBlockingTimeoutRead() throws Exception - { - startServer(new EchoHandler()); - Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort()); - client.setSoTimeout(10000); - InputStream is = client.getInputStream(); - assertFalse(client.isClosed()); - - OutputStream os = client.getOutputStream(); - - long start = NanoTime.now(); - os.write(("GET / HTTP/1.1\r\n" + - "host: localhost:" + _serverURI.getPort() + "\r\n" + - "Transfer-Encoding: chunked\r\n" + - "Content-Type: text/plain\r\n" + - "Connection: close\r\n" + - "\r\n" + - "5\r\n" + - "LMNOP\r\n") - .getBytes("utf-8")); - os.flush(); - - try (StacklessLogging stackless = new StacklessLogging(HttpChannelState.class)) - { - Thread.sleep(300); - os.write("1".getBytes("utf-8")); - os.flush(); - Thread.sleep(300); - os.write("0".getBytes("utf-8")); - os.flush(); - Thread.sleep(300); - os.write("\r".getBytes("utf-8")); - os.flush(); - Thread.sleep(300); - os.write("\n".getBytes("utf-8")); - os.flush(); - Thread.sleep(300); - os.write("0123456789ABCDEF\r\n".getBytes("utf-8")); - os.write("0\r\n".getBytes("utf-8")); - os.write("\r\n".getBytes("utf-8")); - os.flush(); - } - - long duration = NanoTime.millisSince(start); - assertThat(duration, greaterThan(500L)); - - // read the response - String response = IO.toString(is); - assertThat(response, startsWith("HTTP/1.1 500 ")); - assertThat(response, containsString("InterruptedIOException")); - - } - - @Test - @Tag("Unstable") - @Disabled // TODO make more stable - public void testNoBlockingTimeoutWrite() throws Exception - { - startServer(new HugeResponseHandler()); - Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort()); - client.setSoTimeout(10000); - - assertFalse(client.isClosed()); - - OutputStream os = client.getOutputStream(); - BufferedReader is = new BufferedReader(new InputStreamReader(client.getInputStream(), StandardCharsets.ISO_8859_1), 2048); - - os.write(( - "GET / HTTP/1.0\r\n" + - "host: localhost:" + _serverURI.getPort() + "\r\n" + - "connection: keep-alive\r\n" + - "Connection: close\r\n" + - "\r\n").getBytes("utf-8")); - os.flush(); - - // read the header - String line = is.readLine(); - assertThat(line, startsWith("HTTP/1.1 200 OK")); - while (line.length() != 0) - { - line = is.readLine(); - } - - for (int i = 0; i < (128 * 1024); i++) - { - if (i % 1028 == 0) - { - Thread.sleep(20); - } - line = is.readLine(); - assertThat(line, notNullValue()); - assertEquals(1022, line.length()); - } - } - - @Test - @Tag("Unstable") - @Disabled // TODO make more stable - public void testBlockingTimeoutWrite() throws Exception - { - startServer(new HugeResponseHandler()); - Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort()); - client.setSoTimeout(10000); - - assertFalse(client.isClosed()); - - OutputStream os = client.getOutputStream(); - BufferedReader is = new BufferedReader(new InputStreamReader(client.getInputStream(), StandardCharsets.ISO_8859_1), 2048); - - os.write(( - "GET / HTTP/1.0\r\n" + - "host: localhost:" + _serverURI.getPort() + "\r\n" + - "connection: keep-alive\r\n" + - "Connection: close\r\n" + - "\r\n").getBytes("utf-8")); - os.flush(); - - // read the header - String line = is.readLine(); - assertThat(line, startsWith("HTTP/1.1 200 OK")); - while (line.length() != 0) - { - line = is.readLine(); - } - - long start = NanoTime.now(); - try (StacklessLogging stackless = new StacklessLogging(HttpChannelState.class, AbstractConnection.class)) - { - for (int i = 0; i < (128 * 1024); i++) - { - if (i % 1028 == 0) - { - Thread.sleep(20); - } - line = is.readLine(); - if (line == null) - break; - } - } - assertThat(NanoTime.millisSince(start), lessThan(20L * 128L)); - } - @Test public void testMaxIdleNoRequest() throws Exception { @@ -490,7 +276,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture OutputStream os = client.getOutputStream(); long start = NanoTime.now(); - os.write("GET ".getBytes("utf-8")); + os.write("GET ".getBytes(StandardCharsets.UTF_8)); os.flush(); Thread.sleep(sleepTime); @@ -559,7 +345,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture "Content-Length: 20\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n" + - "\r\n").getBytes("utf-8")); + "\r\n").getBytes(StandardCharsets.UTF_8)); os.flush(); assertTimeoutPreemptively(ofSeconds(10), () -> @@ -583,41 +369,51 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture @Test public void testMaxIdleDispatch() throws Exception { - startServer(new EchoHandler()); - Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort()); - client.setSoTimeout(10000); - InputStream is = client.getInputStream(); - assertFalse(client.isClosed()); + startServer(new EchoWholeHandler()); - OutputStream os = client.getOutputStream(); - long start = NanoTime.now(); - os.write(( - "GET / HTTP/1.1\r\n" + - "host: localhost:" + _serverURI.getPort() + "\r\n" + - "connection: keep-alive\r\n" + - "Content-Length: 20\r\n" + - "Content-Type: text/plain\r\n" + - "Connection: close\r\n" + - "\r\n" + - "1234567890").getBytes("utf-8")); - os.flush(); - - assertTimeoutPreemptively(ofSeconds(10), () -> + try (Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort())) { - try + client.setSoTimeout(10000); + + try (InputStream is = client.getInputStream(); + OutputStream os = client.getOutputStream()) { - String response = IO.toString(is); - assertThat(response, containsString("500")); - assertEquals(-1, is.read()); + assertFalse(client.isClosed()); + long start = NanoTime.now(); + + byte[] requestBody = "1234567890".getBytes(StandardCharsets.UTF_8); + // We want a situation where the request says it has a body, + // but the request hasn't sent all of it. + int requestBodyLength = requestBody.length * 2; + + String rawRequest = (""" + GET / HTTP/1.1\r + host: localhost:%d\r + connection: keep-alive\r + Content-Length: %d\r + Content-Type: text/plain\r + Connection: close\r + \r + """).formatted(_serverURI.getPort(), requestBodyLength); + + os.write(rawRequest.getBytes(StandardCharsets.UTF_8)); + os.write(requestBody); + os.flush(); + + assertTimeoutPreemptively(ofSeconds(10), () -> + { + // We expect a 500 response to occur due to the idle timeout triggering. + // See: ServerConnector.setIdleTimeout(long ms); + String response = IO.toString(is); + assertThat(response, containsString("500")); + assertEquals(-1, is.read()); + }); + + long duration = NanoTime.millisSince(start); + assertThat(duration + 100, greaterThanOrEqualTo(MAX_IDLE_TIME)); + assertThat(duration - 100, lessThan(maximumTestRuntime)); } - catch (Exception e) - { - e.printStackTrace(); - } - }); - long duration = NanoTime.millisSince(start); - assertThat(duration + 100, greaterThanOrEqualTo(MAX_IDLE_TIME)); - assertThat(duration - 100, lessThan(maximumTestRuntime)); + } } @Test @@ -633,7 +429,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture InputStream is = client.getInputStream(); String content = "Wibble\r\n"; - byte[] contentB = content.getBytes("utf-8"); + byte[] contentB = content.getBytes(StandardCharsets.UTF_8); os.write(( "GET / HTTP/1.0\r\n" + "host: localhost:" + _serverURI.getPort() + "\r\n" + @@ -641,7 +437,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture "Content-Length: " + (contentB.length * 20) + "\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n" + - "\r\n").getBytes("utf-8")); + "\r\n").getBytes(StandardCharsets.UTF_8)); os.flush(); assertTimeoutPreemptively(ofSeconds(10), () -> @@ -680,7 +476,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture "host: localhost:" + _serverURI.getPort() + "\r\n" + "connection: keep-alive\r\n" + "Connection: close\r\n" + - "\r\n").getBytes("utf-8")); + "\r\n").getBytes(StandardCharsets.UTF_8)); os.flush(); assertTimeoutPreemptively(ofSeconds(10), () -> @@ -712,7 +508,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture "host: localhost:" + _serverURI.getPort() + "\r\n" + "connection: keep-alive\r\n" + "Connection: close\r\n" + - "\r\n").getBytes("utf-8")); + "\r\n").getBytes(StandardCharsets.UTF_8)); os.flush(); assertTimeoutPreemptively(ofSeconds(10), () -> @@ -750,17 +546,26 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture protected static class HugeResponseHandler extends Handler.Abstract { + private final int iterations; + + public HugeResponseHandler(int iterations) + { + this.iterations = iterations; + } + @Override public boolean process(Request request, Response response, Callback callback) throws Exception { response.setStatus(200); - byte[] buffer = new byte[128 * 1024 * 1024]; + // Create a big single buffer + byte[] buffer = new byte[iterations * 1024 * 1024]; Arrays.fill(buffer, (byte)'x'); - for (int i = 0; i < 128 * 1024; i++) + // Toss in an LF after every iteration + for (int i = 0; i < iterations * 1024; i++) { - buffer[i * 1024 + 1022] = '\r'; buffer[i * 1024 + 1023] = '\n'; } + // Write it as a single buffer response.write(true, ByteBuffer.wrap(buffer), callback); return true; } @@ -784,4 +589,73 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture return true; } } + + /** + * A handler that will echo the request body to the response body, but only + * once the entire body content has been received. + */ + public static class EchoWholeHandler extends Handler.Abstract + { + @Override + public boolean process(Request request, Response response, Callback callback) throws Exception + { + long expectedContentLength = request.getHeaders().getLongField(HttpHeader.CONTENT_LENGTH); + if (expectedContentLength <= 0) + { + callback.succeeded(); + return true; + } + + request.demand(new WholeProcess(request, response, callback)); + return true; + } + + /** + * Accumulate the Request body until it's entirely received, + * then write the body back to the response body. + */ + private static class WholeProcess implements Runnable + { + Request request; + Response response; + Callback callback; + ByteBufferAccumulator bufferAccumulator; + + public WholeProcess(Request request, Response response, Callback callback) + { + this.request = request; + this.response = response; + this.callback = callback; + this.bufferAccumulator = new ByteBufferAccumulator(); + } + + @Override + public void run() + { + while (true) + { + Content.Chunk chunk = request.read(); + if (chunk == null) + { + request.demand(this); + return; + } + if (chunk instanceof Content.Chunk.Error error) + { + callback.failed(error.getCause()); + return; + } + // copy buffer + bufferAccumulator.copyBuffer(chunk.getByteBuffer().slice()); + chunk.release(); + if (chunk.isLast()) + { + // write accumulated buffers + response.write(true, bufferAccumulator.toByteBuffer(), callback); + return; + } + } + } + } + } } diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/DetectorConnectionTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/DetectorConnectionTest.java index c18470f126e..28fe12d78ed 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/DetectorConnectionTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/DetectorConnectionTest.java @@ -21,6 +21,7 @@ import java.net.Socket; import java.net.SocketException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.nio.file.Path; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -32,14 +33,12 @@ import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.logging.StacklessLogging; import org.eclipse.jetty.server.handler.DumpHandler; -import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.toolchain.test.MavenPaths; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -91,9 +90,9 @@ public class DetectorConnectionTest private String getResponseOverSsl(String request) throws Exception { - String keystore = MavenTestingUtils.getTestResourceFile("keystore.p12").getAbsolutePath(); + Path keystoreFile = MavenPaths.findTestResourceFile("keystore.p12"); SslContextFactory sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystore); + sslContextFactory.setKeyStorePath(keystoreFile.toString()); sslContextFactory.setKeyStorePassword("storepwd"); sslContextFactory.start(); @@ -140,6 +139,7 @@ public class DetectorConnectionTest socket.getOutputStream().write("OX".getBytes(StandardCharsets.US_ASCII)); socket.getOutputStream().close(); + //noinspection ResultOfMethodCallIgnored assertThrows(SocketException.class, () -> socket.getInputStream().read()); } } @@ -160,6 +160,7 @@ public class DetectorConnectionTest socket.getOutputStream().write(" ".getBytes(StandardCharsets.US_ASCII)); socket.getOutputStream().close(); + //noinspection ResultOfMethodCallIgnored assertThrows(SocketException.class, () -> socket.getInputStream().read()); } } @@ -175,11 +176,12 @@ public class DetectorConnectionTest try (Socket socket = new Socket(_server.getURI().getHost(), _server.getURI().getPort())) { - socket.getOutputStream().write(TypeUtil.fromHexString("0D0A0D0A000D0A515549540A")); // proxy V2 Preamble + socket.getOutputStream().write(StringUtil.fromHexString("0D0A0D0A000D0A515549540A")); // proxy V2 Preamble Thread.sleep(100); // make sure the onFillable callback gets called - socket.getOutputStream().write(TypeUtil.fromHexString("21")); // V2, PROXY + socket.getOutputStream().write(StringUtil.fromHexString("21")); // V2, PROXY socket.getOutputStream().close(); + //noinspection ResultOfMethodCallIgnored assertThrows(SocketException.class, () -> socket.getInputStream().read()); } } @@ -195,7 +197,7 @@ public class DetectorConnectionTest try (Socket socket = new Socket(_server.getURI().getHost(), _server.getURI().getPort())) { - socket.getOutputStream().write(TypeUtil.fromHexString( + socket.getOutputStream().write(StringUtil.fromHexString( // proxy V2 Preamble "0D0A0D0A000D0A515549540A" + // V2, PROXY @@ -207,23 +209,23 @@ public class DetectorConnectionTest "000C" )); Thread.sleep(100); // make sure the onFillable callback gets called - socket.getOutputStream().write(TypeUtil.fromHexString( + socket.getOutputStream().write(StringUtil.fromHexString( // uint32_t src_addr; uint32_t dst_addr; uint16_t src_port; uint16_t dst_port; "C0A80001" // 8080 )); socket.getOutputStream().close(); + //noinspection ResultOfMethodCallIgnored assertThrows(SocketException.class, () -> socket.getInputStream().read()); } } @Test - @Disabled // TODO public void testDetectingSslProxyToHttpNoSslWithProxy() throws Exception { - String keystore = MavenTestingUtils.getTestResourceFile("keystore.p12").getAbsolutePath(); + Path keystoreFile = MavenPaths.findTestResourceFile("keystore.p12"); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystore); + sslContextFactory.setKeyStorePath(keystoreFile.toString()); sslContextFactory.setKeyStorePassword("storepwd"); HttpConnectionFactory http = new HttpConnectionFactory(); @@ -233,18 +235,18 @@ public class DetectorConnectionTest start(detector, http); - String request = "PROXY TCP 1.2.3.4 5.6.7.8 111 222\r\n" + - "GET /path HTTP/1.1\n" + - "Host: server:80\n" + - "Connection: close\n" + - "\n"; + String request = """ + PROXY TCP 1.2.3.4 5.6.7.8 111 222\r + GET /path HTTP/1.1\r + Host: server:80\r + Connection: close\r + \r + """; String response = getResponse(request); assertThat(response, Matchers.containsString("HTTP/1.1 200")); assertThat(response, Matchers.containsString("pathInContext=/path")); assertThat(response, Matchers.containsString("servername=server")); - assertThat(response, Matchers.containsString("serverport=80")); - assertThat(response, Matchers.containsString("localname=5.6.7.8")); assertThat(response, Matchers.containsString("local=5.6.7.8:222")); assertThat(response, Matchers.containsString("remote=1.2.3.4:111")); } @@ -252,9 +254,9 @@ public class DetectorConnectionTest @Test public void testDetectingSslProxyToHttpWithSslNoProxy() throws Exception { - String keystore = MavenTestingUtils.getTestResourceFile("keystore.p12").getAbsolutePath(); + Path keystoreFile = MavenPaths.findTestResourceFile("keystore.p12"); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystore); + sslContextFactory.setKeyStorePath(keystoreFile.toString()); sslContextFactory.setKeyStorePassword("storepwd"); HttpConnectionFactory http = new HttpConnectionFactory(); @@ -264,10 +266,12 @@ public class DetectorConnectionTest start(detector, http); - String request = "GET /path HTTP/1.1\n" + - "Host: server:80\n" + - "Connection: close\n" + - "\n"; + String request = """ + GET /path HTTP/1.1\r + Host: server:80\r + Connection: close\r + \r + """; String response = getResponseOverSsl(request); assertThat(response, Matchers.containsString("HTTP/1.1 200")); @@ -276,9 +280,9 @@ public class DetectorConnectionTest @Test public void testDetectingSslProxyToHttpWithSslWithProxy() throws Exception { - String keystore = MavenTestingUtils.getTestResourceFile("keystore.p12").getAbsolutePath(); + Path keystoreFile = MavenPaths.findTestResourceFile("keystore.p12"); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystore); + sslContextFactory.setKeyStorePath(keystoreFile.toString()); sslContextFactory.setKeyStorePassword("storepwd"); HttpConnectionFactory http = new HttpConnectionFactory(); @@ -288,11 +292,13 @@ public class DetectorConnectionTest start(detector, http); - String request = "PROXY TCP 1.2.3.4 5.6.7.8 111 222\r\n" + - "GET /path HTTP/1.1\n" + - "Host: server:80\n" + - "Connection: close\n" + - "\n"; + String request = """ + PROXY TCP 1.2.3.4 5.6.7.8 111 222\r + GET /path HTTP/1.1\r + Host: server:80\r + Connection: close\r + \r + """; String response = getResponseOverSsl(request); // SSL matched, so the upgrade was made to HTTP which does not understand the proxy request @@ -302,9 +308,9 @@ public class DetectorConnectionTest @Test public void testDetectionUnsuccessfulUpgradesToNextProtocol() throws Exception { - String keystore = MavenTestingUtils.getTestResourceFile("keystore.p12").getAbsolutePath(); + Path keystoreFile = MavenPaths.findTestResourceFile("keystore.p12"); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystore); + sslContextFactory.setKeyStorePath(keystoreFile.toString()); sslContextFactory.setKeyStorePassword("storepwd"); HttpConnectionFactory http = new HttpConnectionFactory(); @@ -314,10 +320,12 @@ public class DetectorConnectionTest start(detector, http); - String request = "GET /path HTTP/1.1\n" + - "Host: server:80\n" + - "Connection: close\n" + - "\n"; + String request = """ + GET /path HTTP/1.1\r + Host: server:80\r + Connection: close\r + \r + """; String response = getResponse(request); assertThat(response, Matchers.containsString("HTTP/1.1 200")); @@ -326,9 +334,9 @@ public class DetectorConnectionTest @Test public void testDetectorToNextDetector() throws Exception { - String keystore = MavenTestingUtils.getTestResourceFile("keystore.p12").getAbsolutePath(); + Path keystoreFile = MavenPaths.findTestResourceFile("keystore.p12"); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystore); + sslContextFactory.setKeyStorePath(keystoreFile.toString()); sslContextFactory.setKeyStorePassword("storepwd"); HttpConnectionFactory http = new HttpConnectionFactory(); @@ -339,11 +347,13 @@ public class DetectorConnectionTest start(sslDetector, proxyDetector, http); - String request = "PROXY TCP 1.2.3.4 5.6.7.8 111 222\r\n" + - "GET /path HTTP/1.1\n" + - "Host: server:80\n" + - "Connection: close\n" + - "\n"; + String request = """ + PROXY TCP 1.2.3.4 5.6.7.8 111 222\r + GET /path HTTP/1.1\r + Host: server:80\r + Connection: close\r + \r + """; String response = getResponseOverSsl(request); // SSL matched, so the upgrade was made to proxy which itself upgraded to HTTP @@ -377,10 +387,12 @@ public class DetectorConnectionTest start(detector, http); - String request = "GET /path HTTP/1.1\n" + - "Host: server:80\n" + - "Connection: close\n" + - "\n"; + String request = """ + GET /path HTTP/1.1\r + Host: server:80\r + Connection: close\r + \r + """; String response = getResponse(request); assertEquals("No upgrade for you", response); @@ -390,9 +402,9 @@ public class DetectorConnectionTest @Test public void testDetectorWithProxyThatHasNoNextProto() throws Exception { - String keystore = MavenTestingUtils.getTestResourceFile("keystore.p12").getAbsolutePath(); + Path keystoreFile = MavenPaths.findTestResourceFile("keystore.p12"); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystore); + sslContextFactory.setKeyStorePath(keystoreFile.toString()); sslContextFactory.setKeyStorePassword("storepwd"); HttpConnectionFactory http = new HttpConnectionFactory(); @@ -402,11 +414,13 @@ public class DetectorConnectionTest start(detector, http); - String request = "PROXY TCP 1.2.3.4 5.6.7.8 111 222\r\n" + - "GET /path HTTP/1.1\n" + - "Host: server:80\n" + - "Connection: close\n" + - "\n"; + String request = """ + PROXY TCP 1.2.3.4 5.6.7.8 111 222\r + GET /path HTTP/1.1\r + Host: server:80\r + Connection: close\r + \r + """; String response = getResponse(request); // ProxyConnectionFactory has no next protocol -> it cannot upgrade @@ -416,9 +430,9 @@ public class DetectorConnectionTest @Test public void testOptionalSsl() throws Exception { - String keystore = MavenTestingUtils.getTestResourceFile("keystore.p12").getAbsolutePath(); + Path keystoreFile = MavenPaths.findTestResourceFile("keystore.p12"); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystore); + sslContextFactory.setKeyStorePath(keystoreFile.toString()); sslContextFactory.setKeyStorePassword("storepwd"); HttpConnectionFactory http = new HttpConnectionFactory(); @@ -427,11 +441,12 @@ public class DetectorConnectionTest start(detector, http); - String request = - "GET /path HTTP/1.1\n" + - "Host: server:80\n" + - "Connection: close\n" + - "\n"; + String request = """ + GET /path HTTP/1.1\r + Host: server:80\r + Connection: close\r + \r + """; String clearTextResponse = getResponse(request); String sslResponse = getResponseOverSsl(request); @@ -443,9 +458,9 @@ public class DetectorConnectionTest @Test public void testDetectorThatHasNoConfiguredNextProto() throws Exception { - String keystore = MavenTestingUtils.getTestResourceFile("keystore.p12").getAbsolutePath(); + Path keystoreFile = MavenPaths.findTestResourceFile("keystore.p12"); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystore); + sslContextFactory.setKeyStorePath(keystoreFile.toString()); sslContextFactory.setKeyStorePassword("storepwd"); SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()); @@ -453,11 +468,12 @@ public class DetectorConnectionTest start(detector); - String request = - "GET /path HTTP/1.1\n" + - "Host: server:80\n" + - "Connection: close\n" + - "\n"; + String request = """ + GET /path HTTP/1.1\r + Host: server:80\r + Connection: close\r + \r + """; String response = getResponse(request); assertThat(response, Matchers.nullValue()); @@ -489,11 +505,11 @@ public class DetectorConnectionTest "1F90"; // 8080 String httpReq = """ - GET /path HTTP/1.1 - Host: server:80 - Connection: close - - """; + GET /path HTTP/1.1\r + Host: server:80\r + Connection: close\r + \r + """; try (StacklessLogging ignore = new StacklessLogging(DetectorConnectionFactory.class)) { String response = getResponse(StringUtil.fromHexString(proxyReq), httpReq.getBytes(StandardCharsets.US_ASCII)); @@ -558,12 +574,13 @@ public class DetectorConnectionTest "3039" + // 12345 "1F90"; // 8080 - String httpReq = - "GET /path HTTP/1.1\n" + - "Host: server:80\n" + - "Connection: close\n" + - "\n"; - String response = getResponse(TypeUtil.fromHexString(proxyReq), httpReq.getBytes(StandardCharsets.US_ASCII)); + String httpReq = """ + GET /path HTTP/1.1\r + Host: server:80\r + Connection: close\r + \r + """; + String response = getResponse(StringUtil.fromHexString(proxyReq), httpReq.getBytes(StandardCharsets.US_ASCII)); assertThat(response, Matchers.nullValue()); } @@ -604,11 +621,12 @@ public class DetectorConnectionTest start(detector, noUpgradeTo); - String request = - "GET /path HTTP/1.1\n" + - "Host: server:80\n" + - "Connection: close\n" + - "\n"; + String request = """ + GET /path HTTP/1.1\r + Host: server:80\r + Connection: close\r + \r + """; String response = getResponse(request); assertThat(response, Matchers.nullValue()); @@ -617,9 +635,9 @@ public class DetectorConnectionTest @Test public void testGeneratedProtocolNames() { - String keystore = MavenTestingUtils.getTestResourceFile("keystore.p12").getAbsolutePath(); + Path keystoreFile = MavenPaths.findTestResourceFile("keystore.p12"); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystore); + sslContextFactory.setKeyStorePath(keystoreFile.toString()); sslContextFactory.setKeyStorePassword("storepwd"); ProxyConnectionFactory proxy = new ProxyConnectionFactory(HttpVersion.HTTP_1_1.asString()); diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorTimeoutTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorTimeoutTest.java index 12432791fe0..65c8da30aed 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorTimeoutTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ServerConnectorTimeoutTest.java @@ -14,13 +14,11 @@ package org.eclipse.jetty.server; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static java.time.Duration.ofSeconds; import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; -@Disabled // TODO public class ServerConnectorTimeoutTest extends ConnectorTimeoutTest { @BeforeEach @@ -28,11 +26,11 @@ public class ServerConnectorTimeoutTest extends ConnectorTimeoutTest { ServerConnector connector = new ServerConnector(_server, 1, 1); connector.setIdleTimeout(MAX_IDLE_TIME); - _server.addConnector(connector); + initServer(connector); } @Test - public void testStartStopStart() throws Exception + public void testStartStopStart() { assertTimeoutPreemptively(ofSeconds(10), () -> { @@ -40,165 +38,4 @@ public class ServerConnectorTimeoutTest extends ConnectorTimeoutTest _server.start(); }); } - - /* TODO - @Test - public void testIdleTimeoutAfterSuspend() throws Exception - { - _server.stop(); - SuspendHandler handler = new SuspendHandler(); - SessionHandler session = new SessionHandler(); - session.setHandler(handler); - _server.setHandler(session); - _server.start(); - - handler.setSuspendFor(100); - handler.setResumeAfter(25); - assertTimeoutPreemptively(ofSeconds(10), () -> - { - String process = process(null).toUpperCase(Locale.ENGLISH); - assertThat(process, containsString("RESUMED")); - }); - } - - @Test - public void testIdleTimeoutAfterTimeout() throws Exception - { - SuspendHandler handler = new SuspendHandler(); - _server.stop(); - SessionHandler session = new SessionHandler(); - session.setHandler(handler); - _server.setHandler(session); - _server.start(); - - handler.setSuspendFor(50); - assertTimeoutPreemptively(ofSeconds(10), () -> - { - String process = process(null).toUpperCase(Locale.ENGLISH); - assertThat(process, containsString("TIMEOUT")); - }); - } - - @Test - public void testIdleTimeoutAfterComplete() throws Exception - { - SuspendHandler handler = new SuspendHandler(); - _server.stop(); - SessionHandler session = new SessionHandler(); - session.setHandler(handler); - _server.setHandler(session); - _server.start(); - - handler.setSuspendFor(100); - handler.setCompleteAfter(25); - assertTimeoutPreemptively(ofSeconds(10), () -> - { - String process = process(null).toUpperCase(Locale.ENGLISH); - assertThat(process, containsString("COMPLETED")); - }); - } - - private String process(String content) throws IOException, InterruptedException - { - synchronized (this) - { - String request = "GET / HTTP/1.1\r\n" + "Host: localhost\r\n"; - - if (content == null) - request += "\r\n"; - else - request += "Content-Length: " + content.length() + "\r\n" + "\r\n" + content; - return getResponse(request); - } - } - - private String getResponse(String request) throws IOException, InterruptedException - { - try (Socket socket = new Socket((String)null, _connector.getLocalPort())) - { - socket.setSoTimeout((int)(10 * MAX_IDLE_TIME)); - socket.getOutputStream().write(request.getBytes(StandardCharsets.UTF_8)); - InputStream inputStream = socket.getInputStream(); - long start = NanoTime.now(); - String response = IO.toString(inputStream); - assertThat(NanoTime.millisSince(start), greaterThanOrEqualTo(MAX_IDLE_TIME - 100L)); - return response; - } - } - - @Test - public void testHttpWriteIdleTimeout() throws Exception - { - _httpConfiguration.setIdleTimeout(500); - configureServer(new AbstractHandler() - { - @Override - public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException - { - baseRequest.setHandled(true); - IO.copy(request.getInputStream(), response.getOutputStream()); - } - }); - Socket client = newSocket(_serverURI.getHost(), _serverURI.getPort()); - client.setSoTimeout(10000); - - assertFalse(client.isClosed()); - - final OutputStream os = client.getOutputStream(); - final InputStream is = client.getInputStream(); - final StringBuilder response = new StringBuilder(); - - CompletableFuture responseFuture = CompletableFuture.runAsync(() -> - { - try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) - { - int c; - while ((c = reader.read()) != -1) - { - response.append((char)c); - } - } - catch (IOException e) - { - // Valid path (as connection is forcibly closed) - // t.printStackTrace(System.err); - } - }); - - CompletableFuture requestFuture = CompletableFuture.runAsync(() -> - { - try - { - os.write(( - "POST /echo HTTP/1.0\r\n" + - "host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" + - "content-type: text/plain; charset=utf-8\r\n" + - "content-length: 20\r\n" + - "\r\n").getBytes("utf-8")); - os.flush(); - - os.write("123456789\n".getBytes("utf-8")); - os.flush(); - TimeUnit.SECONDS.sleep(1); - os.write("=========\n".getBytes("utf-8")); - os.flush(); - } - catch (InterruptedException | IOException e) - { - // Valid path, as write of second half of content can fail - // e.printStackTrace(System.err); - } - }); - - try (StacklessLogging ignore = new StacklessLogging(HttpChannel.class)) - { - requestFuture.get(2, TimeUnit.SECONDS); - responseFuture.get(3, TimeUnit.SECONDS); - - assertThat(response.toString(), containsString(" 500 ")); - assertThat(response.toString(), not(containsString("========="))); - } - } - - */ } diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslSelectChannelTimeoutTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/ServerConnectorSslTimeoutTest.java similarity index 81% rename from jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslSelectChannelTimeoutTest.java rename to jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/ServerConnectorSslTimeoutTest.java index f0c08efcd85..09293e47902 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslSelectChannelTimeoutTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/ServerConnectorSslTimeoutTest.java @@ -13,43 +13,37 @@ package org.eclipse.jetty.server.ssl; -import java.io.FileInputStream; import java.io.InputStream; import java.net.Socket; +import java.nio.file.Files; +import java.nio.file.Path; import java.security.KeyStore; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; import org.eclipse.jetty.server.ConnectorTimeoutTest; import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.toolchain.test.MavenPaths; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -@Disabled // TODO -public class SslSelectChannelTimeoutTest extends ConnectorTimeoutTest +public class ServerConnectorSslTimeoutTest extends ConnectorTimeoutTest { static SSLContext __sslContext; - @Override - protected Socket newSocket(String host, int port) throws Exception - { - return __sslContext.getSocketFactory().createSocket(host, port); - } - @BeforeEach public void init() throws Exception { - String keystorePath = System.getProperty("basedir", ".") + "/src/test/resources/keystore.p12"; + Path keystorePath = MavenPaths.findTestResourceFile("keystore.p12"); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystorePath); + sslContextFactory.setKeyStorePath(keystorePath.toString()); sslContextFactory.setKeyStorePassword("storepwd"); ServerConnector connector = new ServerConnector(_server, 1, 1, sslContextFactory); - connector.setIdleTimeout(MAX_IDLE_TIME); //250 msec max idle + connector.setIdleTimeout(MAX_IDLE_TIME); _server.addConnector(connector); KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); - try (InputStream stream = new FileInputStream(keystorePath)) + try (InputStream stream = Files.newInputStream(keystorePath)) { keystore.load(stream, "storepwd".toCharArray()); } @@ -58,4 +52,10 @@ public class SslSelectChannelTimeoutTest extends ConnectorTimeoutTest __sslContext = SSLContext.getInstance("SSL"); __sslContext.init(null, trustManagerFactory.getTrustManagers(), null); } + + @Override + protected Socket newSocket(String host, int port) throws Exception + { + return __sslContext.getSocketFactory().createSocket(host, port); + } } diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SlowClientsTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SlowClientsTest.java index 9f36ba39c87..370cdf28204 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SlowClientsTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SlowClientsTest.java @@ -13,144 +13,150 @@ package org.eclipse.jetty.server.ssl; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; import java.io.UncheckedIOException; +import java.net.Socket; +import java.net.URI; import java.nio.charset.StandardCharsets; +import java.nio.file.Path; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocket; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.toolchain.test.MavenPaths; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static java.time.Duration.ofSeconds; -@Tag("Unstable") -@Disabled public class SlowClientsTest { private static final Logger LOG = LoggerFactory.getLogger(SlowClientsTest.class); + private Server server; + private SslContextFactory.Server sslContextFactory; + + @BeforeEach + public void initServer() throws Exception + { + Path keystoreFile = MavenPaths.findTestResourceFile("keystore.p12"); + sslContextFactory = new SslContextFactory.Server(); + sslContextFactory.setKeyStorePath(keystoreFile.toString()); + sslContextFactory.setKeyStorePassword("storepwd"); + server = new Server(); + ServerConnector connector = new ServerConnector(server, 1, 1, sslContextFactory); + connector.setPort(0); + server.addConnector(connector); + } + + @AfterEach + public void stopServer() + { + LifeCycle.stop(server); + } + + public void startServer(Handler handler) throws Exception + { + server.setHandler(handler); + server.start(); + } + + public Socket newSocketToServer() throws IOException + { + URI serverURI = server.getURI(); + SSLContext sslContext = sslContextFactory.getSslContext(); + return sslContext.getSocketFactory().createSocket(serverURI.getHost(), serverURI.getPort()); + } @Test public void testSlowClientsWithSmallThreadPool() throws Exception { - File keystore = MavenTestingUtils.getTestResourceFile("keystore.p12"); - SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystore.getAbsolutePath()); - sslContextFactory.setKeyStorePassword("storepwd"); + final int maxThreads = 6; + final int contentLength = 8 * 1024 * 1024; // 8MB - int maxThreads = 6; - int contentLength = 8 * 1024 * 1024; - QueuedThreadPool serverThreads = new QueuedThreadPool(maxThreads); - serverThreads.setDetailedDump(true); - Server server = new Server(serverThreads); - - try + ((QueuedThreadPool)server.getThreadPool()).setMaxThreads(maxThreads); + startServer(new Handler.Abstract() { - ServerConnector connector = new ServerConnector(server, 1, 1, sslContextFactory); - connector.setPort(8888); - server.addConnector(connector); - server.setHandler(new Handler.Abstract.NonBlocking() + @Override + public boolean process(Request request, Response response, Callback callback) { - @Override - public boolean process(Request request, Response response, Callback callback) - { - LOG.info("SERVING {}", request); - // Write some big content. - response.write(true, BufferUtil.toBuffer(new byte[contentLength]), new Callback() - { - @Override - public void succeeded() - { - callback.succeeded(); - LOG.info("SERVED {}", request); - } + response.write(true, BufferUtil.toBuffer(contentLength), callback); + return true; + } + }); - @Override - public void failed(Throwable x) - { - callback.failed(x); - } - } - ); - return true; - } - }); - server.start(); - - SSLContext sslContext = sslContextFactory.getSslContext(); - - Assertions.assertTimeoutPreemptively(ofSeconds(10), () -> + Assertions.assertTimeoutPreemptively(ofSeconds(10), () -> + { + // Twice as many clients as threads in thread pool. + CompletableFuture[] futures = new CompletableFuture[2 * maxThreads]; + ExecutorService executor = Executors.newFixedThreadPool(futures.length); + for (int i = 0; i < futures.length; i++) { - CompletableFuture[] futures = new CompletableFuture[2 * maxThreads]; - ExecutorService executor = Executors.newFixedThreadPool(futures.length); - for (int i = 0; i < futures.length; i++) + int k = i; + futures[i] = CompletableFuture.runAsync(() -> { - int k = i; - futures[i] = CompletableFuture.runAsync(() -> + try (Socket socket = newSocketToServer()) { - try (SSLSocket socket = (SSLSocket)sslContext.getSocketFactory().createSocket("localhost", connector.getLocalPort())) - { - socket.setSoTimeout(contentLength / 1024); - OutputStream output = socket.getOutputStream(); - String target = "/" + k; - String request = "GET " + target + " HTTP/1.1\r\n" + - "Host: localhost\r\n" + - "Connection: close\r\n" + - "\r\n"; - output.write(request.getBytes(StandardCharsets.UTF_8)); - output.flush(); + socket.setSoTimeout(contentLength / 1024); + OutputStream output = socket.getOutputStream(); + String target = "/" + k; + String rawRequest = """ + GET %s HTTP/1.1\r + Host: localhost\r + Connection: close\r + \r + """.formatted(target); + output.write(rawRequest.getBytes(StandardCharsets.UTF_8)); + output.flush(); - while (serverThreads.getIdleThreads() > 0) - { - Thread.sleep(50); - } + int delayReadCount = 10; - InputStream input = socket.getInputStream(); - while (true) + InputStream input = socket.getInputStream(); + while (true) + { + int read = input.read(); + if (read < 0) + break; + // simulate a slow client (for a bit). + // we are testing that the server thread pool doesn't misbehave + // in this scenario, where there are more clients active than server threads. + if (delayReadCount > 0) { - int read = input.read(); - if (read < 0) - break; + TimeUnit.MILLISECONDS.sleep(200); + delayReadCount--; } - LOG.info("FINISHED {}", target); } - catch (IOException x) - { - throw new UncheckedIOException(x); - } - catch (InterruptedException x) - { - throw new UncheckedIOException(new InterruptedIOException()); - } - }, executor); - } - CompletableFuture.allOf(futures).join(); - }); - } - finally - { - server.stop(); - } + LOG.info("FINISHED {}", target); + } + catch (IOException x) + { + throw new UncheckedIOException(x); + } + catch (InterruptedException x) + { + throw new UncheckedIOException(new InterruptedIOException()); + } + }, executor); + } + CompletableFuture.allOf(futures).join(); + }); } } diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslConnectionFactoryTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslConnectionFactoryTest.java index 58bcaa3699b..b6713d343d8 100644 --- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslConnectionFactoryTest.java +++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslConnectionFactoryTest.java @@ -13,11 +13,11 @@ package org.eclipse.jetty.server.ssl; -import java.io.File; -import java.io.FileNotFoundException; +import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.nio.charset.StandardCharsets; +import java.nio.file.Path; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; @@ -29,6 +29,8 @@ import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.http.HttpTester; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.Handler; @@ -40,6 +42,7 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SocketCustomizationListener; import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.toolchain.test.MavenPaths; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IO; @@ -47,15 +50,15 @@ import org.eclipse.jetty.util.ssl.SslContextFactory; import org.hamcrest.Matchers; 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 static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -@Disabled // TODO Fix? public class SslConnectionFactoryTest { private Server _server; @@ -65,11 +68,7 @@ public class SslConnectionFactoryTest @BeforeEach public void before() throws Exception { - String keystorePath = "src/test/resources/keystore.p12"; - File keystoreFile = new File(keystorePath); - if (!keystoreFile.exists()) - throw new FileNotFoundException(keystoreFile.getAbsolutePath()); - + Path keystoreFile = MavenPaths.findTestResourceFile("keystore.p12"); _server = new Server(); HttpConfiguration httpConfig = new HttpConfiguration(); @@ -78,7 +77,7 @@ public class SslConnectionFactoryTest httpConfig.setOutputBufferSize(32768); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath()); + sslContextFactory.setKeyStorePath(keystoreFile.toString()); sslContextFactory.setKeyStorePassword("storepwd"); SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()); @@ -116,15 +115,15 @@ public class SslConnectionFactoryTest @Test public void testConnect() throws Exception { - String response = getResponse("127.0.0.1", null); - assertThat(response, Matchers.containsString("host=127.0.0.1")); + HttpTester.Response response = getResponse("127.0.0.1", null); + assertThat(response.getContent(), containsString("host=127.0.0.1")); } @Test public void testSNIConnect() throws Exception { - String response = getResponse("localhost", "localhost", "localhost"); - assertThat(response, Matchers.containsString("host=localhost")); + HttpTester.Response response = getResponse("localhost", "localhost", "localhost"); + assertThat(response.getContent(), containsString("host=localhost")); } @Test @@ -174,13 +173,13 @@ public class SslConnectionFactoryTest } }); - String response = getResponse("127.0.0.1", null); - assertThat(response, Matchers.containsString("host=127.0.0.1")); + HttpTester.Response response = getResponse("127.0.0.1", null); + assertThat(response.getContent(), containsString("host=127.0.0.1")); assertEquals("customize connector class org.eclipse.jetty.io.ssl.SslConnection,false", history.poll()); assertEquals("customize ssl class org.eclipse.jetty.io.ssl.SslConnection,false", history.poll()); - assertEquals("customize connector class org.eclipse.jetty.server.HttpConnection,true", history.poll()); - assertEquals("customize http class org.eclipse.jetty.server.HttpConnection,true", history.poll()); + assertEquals("customize connector class org.eclipse.jetty.server.internal.HttpConnection,true", history.poll()); + assertEquals("customize http class org.eclipse.jetty.server.internal.HttpConnection,true", history.poll()); assertEquals(0, history.size()); } @@ -192,45 +191,58 @@ public class SslConnectionFactoryTest assertThrows(IllegalStateException.class, () -> _server.start()); } - private String getResponse(String host, String cn) throws Exception + private HttpTester.Response getResponse(String host, String cn) throws Exception { - String response = getResponse(host, host, cn); - assertThat(response, Matchers.startsWith("HTTP/1.1 200 OK")); - assertThat(response, Matchers.containsString("url=/ctx/path")); + HttpTester.Response response = getResponse(host, host, cn); + assertThat(response.getStatus(), is(HttpStatus.OK_200)); + assertThat(response.getContent(), containsString("url=https://%s:%d/ctx/path".formatted(host, _port))); return response; } - private String getResponse(String sniHost, String reqHost, String cn) throws Exception + private HttpTester.Response getResponse(String sniHost, String reqHost, String cn) throws Exception { SslContextFactory clientContextFactory = new SslContextFactory.Client(true); clientContextFactory.start(); SSLSocketFactory factory = clientContextFactory.getSslContext().getSocketFactory(); - SSLSocket sslSocket = (SSLSocket)factory.createSocket("127.0.0.1", _port); - - if (cn != null) + try (SSLSocket sslSocket = (SSLSocket)factory.createSocket("127.0.0.1", _port)) { - SNIHostName serverName = new SNIHostName(sniHost); - List serverNames = new ArrayList<>(); - serverNames.add(serverName); + if (cn != null) + { + SNIHostName serverName = new SNIHostName(sniHost); + List serverNames = new ArrayList<>(); + serverNames.add(serverName); - SSLParameters params = sslSocket.getSSLParameters(); - params.setServerNames(serverNames); - sslSocket.setSSLParameters(params); + SSLParameters params = sslSocket.getSSLParameters(); + params.setServerNames(serverNames); + sslSocket.setSSLParameters(params); + } + sslSocket.startHandshake(); + + if (cn != null) + { + X509Certificate cert = ((X509Certificate)sslSocket.getSession().getPeerCertificates()[0]); + assertThat(cert.getSubjectX500Principal().getName("CANONICAL"), Matchers.startsWith("cn=" + cn)); + } + + try (OutputStream os = sslSocket.getOutputStream(); + InputStream in = sslSocket.getInputStream()) + { + String rawRequest = """ + GET /ctx/path HTTP/1.1\r + Host: %s:%d\r + Connection: close\r + \r + """.formatted(reqHost, _port); + + os.write(rawRequest.getBytes(StandardCharsets.UTF_8)); + String rawResponse = IO.toString(in); + return HttpTester.parseResponse(rawResponse); + } } - sslSocket.startHandshake(); - - if (cn != null) + finally { - X509Certificate cert = ((X509Certificate)sslSocket.getSession().getPeerCertificates()[0]); - assertThat(cert.getSubjectX500Principal().getName("CANONICAL"), Matchers.startsWith("cn=" + cn)); + clientContextFactory.stop(); } - - sslSocket.getOutputStream().write(("GET /ctx/path HTTP/1.0\r\nHost: " + reqHost + ":" + _port + "\r\n\r\n").getBytes(StandardCharsets.ISO_8859_1)); - String response = IO.toString(sslSocket.getInputStream()); - - sslSocket.close(); - clientContextFactory.stop(); - return response; } }