diff --git a/test/jdk/java/net/httpclient/EncodedCharsInURI.java b/test/jdk/java/net/httpclient/EncodedCharsInURI.java index c25fe1b6560..bcda1f32539 100644 --- a/test/jdk/java/net/httpclient/EncodedCharsInURI.java +++ b/test/jdk/java/net/httpclient/EncodedCharsInURI.java @@ -36,9 +36,6 @@ */ //* -Djdk.internal.httpclient.debug=true -import com.sun.net.httpserver.HttpServer; -import com.sun.net.httpserver.HttpsConfigurator; -import com.sun.net.httpserver.HttpsServer; import jdk.test.lib.net.SimpleSSLContext; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterTest; @@ -48,6 +45,7 @@ import org.testng.annotations.Test; import javax.net.ServerSocketFactory; import javax.net.ssl.SSLContext; +import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -57,6 +55,7 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.URI; import java.net.http.HttpClient; +import java.net.http.HttpClient.Version; import java.net.http.HttpRequest; import java.net.http.HttpRequest.BodyPublisher; import java.net.http.HttpRequest.BodyPublishers; @@ -74,14 +73,11 @@ import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; import jdk.httpclient.test.lib.common.HttpServerAdapters; -import jdk.httpclient.test.lib.http2.Http2TestServer; -import static java.lang.String.format; -import static java.lang.System.in; +import static java.lang.System.err; import static java.lang.System.out; import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_2; -import static java.nio.charset.StandardCharsets.US_ASCII; import static java.nio.charset.StandardCharsets.UTF_8; import static java.net.http.HttpClient.Builder.NO_PROXY; import static org.testng.Assert.assertEquals; @@ -94,8 +90,8 @@ public class EncodedCharsInURI implements HttpServerAdapters { HttpTestServer httpsTestServer; // HTTPS/1.1 HttpTestServer http2TestServer; // HTTP/2 ( h2c ) HttpTestServer https2TestServer; // HTTP/2 ( h2 ) - DummyServer httpDummyServer; // HTTP/1.1 [ 2 servers ] - DummyServer httpsDummyServer; // HTTPS/1.1 + DummyServer httpDummyServer; // HTTP/1.1 [ 2 servers ] + DummyServer httpsDummyServer; // HTTPS/1.1 String httpURI_fixed; String httpURI_chunk; String httpsURI_fixed; @@ -140,8 +136,8 @@ public class EncodedCharsInURI implements HttpServerAdapters { command.run(); } catch (Throwable t) { tasksFailed = true; - System.out.printf(now() + "Task %s failed: %s%n", id, t); - System.err.printf(now() + "Task %s failed: %s%n", id, t); + out.printf(now() + "Task %s failed: %s%n", id, t); + err.printf(now() + "Task %s failed: %s%n", id, t); FAILURES.putIfAbsent("Task " + id, t); throw t; } @@ -162,7 +158,7 @@ public class EncodedCharsInURI implements HttpServerAdapters { e.getValue().printStackTrace(out); }); if (tasksFailed) { - System.out.println("WARNING: Some tasks failed"); + out.println("WARNING: Some tasks failed"); } } finally { out.println("\n=========================\n"); @@ -201,6 +197,14 @@ public class EncodedCharsInURI implements HttpServerAdapters { return result; } + static Version version(String uri) { + if (uri.contains("/http1/") || uri.contains("/https1/")) + return HTTP_1_1; + if (uri.contains("/http2/") || uri.contains("/https2/")) + return HTTP_2; + return null; + } + private HttpClient makeNewClient() { clientCount.incrementAndGet(); return HttpClient.newBuilder() @@ -225,6 +229,14 @@ public class EncodedCharsInURI implements HttpServerAdapters { final String ENCODED = "/01%252F03/"; + record CloseableClient(HttpClient client, boolean shared) + implements Closeable { + public void close() { + if (shared) return; + client.close(); + } + } + @Test(dataProvider = "noThrows") public void testEncodedChars(String uri, boolean sameClient) throws Exception { @@ -232,29 +244,34 @@ public class EncodedCharsInURI implements HttpServerAdapters { out.printf("%n%s testEncodedChars(%s, %b)%n", now(), uri, sameClient); uri = uri + ENCODED; for (int i=0; i< ITERATION_COUNT; i++) { - if (!sameClient || client == null) + if (!sameClient || client == null) { client = newHttpClient(sameClient); + } + try (var cl = new CloseableClient(client, sameClient)) { + BodyPublisher bodyPublisher = BodyPublishers.ofString(uri); - BodyPublisher bodyPublisher = BodyPublishers.ofString(uri); - - HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) - .POST(bodyPublisher) - .build(); - BodyHandler handler = BodyHandlers.ofString(); - CompletableFuture> responseCF = client.sendAsync(req, handler); - HttpResponse response = responseCF.join(); - String body = response.body(); - if (!uri.contains(body)) { - System.err.println("Test failed: " + response); - throw new RuntimeException(uri + " doesn't contain '" + body + "'"); - } else { - System.out.println("Found expected " + body + " in " + uri); + HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) + .POST(bodyPublisher) + .build(); + BodyHandler handler = BodyHandlers.ofString(); + CompletableFuture> responseCF = client.sendAsync(req, handler); + HttpResponse response = responseCF.join(); + String body = response.body(); + if (!uri.contains(body)) { + err.println("Test failed: " + response); + throw new RuntimeException(uri + " doesn't contain '" + body + "'"); + } else { + out.println("Found expected " + body + " in " + uri); + } + assertEquals(response.version(), version(uri)); } } } @BeforeTest public void setup() throws Exception { + out.println(now() + "begin setup"); + sslContext = new SimpleSSLContext().get(); if (sslContext == null) throw new AssertionError("Unexpected null sslContext"); @@ -297,6 +314,7 @@ public class EncodedCharsInURI implements HttpServerAdapters { httpDummy = "http://" + httpDummyServer.serverAuthority() + "/http1/dummy/x"; httpsDummy = "https://" + httpsDummyServer.serverAuthority() + "/https1/dummy/x"; + err.println(now() + "Starting servers"); serverCount.addAndGet(6); httpTestServer.start(); @@ -305,11 +323,21 @@ public class EncodedCharsInURI implements HttpServerAdapters { https2TestServer.start(); httpDummyServer.start(); httpsDummyServer.start(); + + out.println("HTTP/1.1 dummy server (http) listening at: " + httpDummyServer.serverAuthority()); + out.println("HTTP/1.1 dummy server (TLS) listening at: " + httpsDummyServer.serverAuthority()); + out.println("HTTP/1.1 server (http) listening at: " + httpTestServer.serverAuthority()); + out.println("HTTP/1.1 server (TLS) listening at: " + httpsTestServer.serverAuthority()); + out.println("HTTP/2 server (h2c) listening at: " + http2TestServer.serverAuthority()); + out.println("HTTP/2 server (h2) listening at: " + https2TestServer.serverAuthority()); + + out.println(now() + "setup done"); + err.println(now() + "setup done"); } @AfterTest public void teardown() throws Exception { - sharedClient = null; + sharedClient.close(); httpTestServer.stop(); httpsTestServer.stop(); http2TestServer.stop(); @@ -390,14 +418,13 @@ public class EncodedCharsInURI implements HttpServerAdapters { while(!stopped) { Socket clientConnection = ss.accept(); connections.add(clientConnection); - System.out.println(now() + getName() + ": Client accepted"); + out.println(now() + getName() + ": Client accepted"); StringBuilder headers = new StringBuilder(); - Socket targetConnection = null; InputStream ccis = clientConnection.getInputStream(); OutputStream ccos = clientConnection.getOutputStream(); - System.out.println(now() + getName() + ": Reading request line"); + out.println(now() + getName() + ": Reading request line"); String requestLine = readLine(ccis); - System.out.println(now() + getName() + ": Request line: " + requestLine); + out.println(now() + getName() + ": Request line: " + requestLine); StringTokenizer tokenizer = new StringTokenizer(requestLine); String method = tokenizer.nextToken(); @@ -408,7 +435,7 @@ public class EncodedCharsInURI implements HttpServerAdapters { String hostport = serverAuthority(); uri = new URI((secure ? "https" : "http") +"://" + hostport + path); } catch (Throwable x) { - System.err.printf("Bad target address: \"%s\" in \"%s\"%n", + err.printf("Bad target address: \"%s\" in \"%s\"%n", path, requestLine); clientConnection.close(); continue; @@ -418,7 +445,7 @@ public class EncodedCharsInURI implements HttpServerAdapters { // signals the end of all headers. String line = requestLine; while (!line.equals("")) { - System.out.println(now() + getName() + ": Reading header: " + out.println(now() + getName() + ": Reading header: " + (line = readLine(ccis))); headers.append(line).append("\r\n"); } @@ -435,11 +462,11 @@ public class EncodedCharsInURI implements HttpServerAdapters { StringTokenizer tk = new StringTokenizer(cl); int len = Integer.parseInt(tk.nextToken()); assert len < b.length * 2; - System.out.println(now() + getName() + out.println(now() + getName() + ": received body: " + new String(ccis.readNBytes(len), UTF_8)); } - System.out.println(now() + out.println(now() + getName() + ": sending back " + uri); response.append("HTTP/1.1 200 OK\r\nContent-Length: ") @@ -447,21 +474,21 @@ public class EncodedCharsInURI implements HttpServerAdapters { .append("\r\n\r\n"); // Then send the 200 OK response to the client - System.out.println(now() + getName() + ": Sending " + out.println(now() + getName() + ": Sending " + response); ccos.write(response.toString().getBytes(UTF_8)); ccos.flush(); - System.out.println(now() + getName() + ": sent response headers"); + out.println(now() + getName() + ": sent response headers"); ccos.write(b); ccos.flush(); ccos.close(); - System.out.println(now() + getName() + ": sent " + b.length + " body bytes"); + out.println(now() + getName() + ": sent " + b.length + " body bytes"); connections.remove(clientConnection); clientConnection.close(); } } catch (Throwable t) { if (!stopped) { - System.out.println(now() + getName() + ": failed: " + t); + out.println(now() + getName() + ": failed: " + t); t.printStackTrace(); try { stopServer(); @@ -470,7 +497,7 @@ public class EncodedCharsInURI implements HttpServerAdapters { } } } finally { - System.out.println(now() + getName() + ": exiting"); + out.println(now() + getName() + ": exiting"); } } @@ -504,7 +531,6 @@ public class EncodedCharsInURI implements HttpServerAdapters { return new DummyServer(ss, true); } - } } diff --git a/test/jdk/java/net/httpclient/EscapedOctetsInURI.java b/test/jdk/java/net/httpclient/EscapedOctetsInURI.java index 00061c1edf0..8a17cea78c4 100644 --- a/test/jdk/java/net/httpclient/EscapedOctetsInURI.java +++ b/test/jdk/java/net/httpclient/EscapedOctetsInURI.java @@ -32,11 +32,7 @@ * EscapedOctetsInURI */ -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; -import com.sun.net.httpserver.HttpsConfigurator; -import com.sun.net.httpserver.HttpsServer; +import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -45,37 +41,43 @@ import java.net.InetSocketAddress; import java.net.URI; import javax.net.ssl.SSLContext; import java.net.http.HttpClient; +import java.net.http.HttpClient.Version; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import jdk.httpclient.test.lib.http2.Http2TestServer; -import jdk.httpclient.test.lib.http2.Http2TestExchange; -import jdk.httpclient.test.lib.http2.Http2Handler; + +import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.test.lib.net.SimpleSSLContext; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; + +import static java.lang.System.err; import static java.lang.System.out; +import static java.net.http.HttpClient.Version.HTTP_1_1; +import static java.net.http.HttpClient.Version.HTTP_2; import static java.nio.charset.StandardCharsets.US_ASCII; import static java.net.http.HttpClient.Builder.NO_PROXY; import static org.testng.Assert.assertEquals; -public class EscapedOctetsInURI { +public class EscapedOctetsInURI implements HttpServerAdapters { SSLContext sslContext; - HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ] - HttpsServer httpsTestServer; // HTTPS/1.1 - Http2TestServer http2TestServer; // HTTP/2 ( h2c ) - Http2TestServer https2TestServer; // HTTP/2 ( h2 ) + HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] + HttpTestServer httpsTestServer; // HTTPS/1.1 + HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + HttpTestServer https2TestServer; // HTTP/2 ( h2 ) String httpURI; String httpsURI; String http2URI; String https2URI; + private volatile HttpClient sharedClient; + static final String[][] pathsAndQueryStrings = new String[][] { // partial-path URI query { "/001/noSpace", "?noQuotedOctets" }, @@ -110,6 +112,52 @@ public class EscapedOctetsInURI { static final int ITERATION_COUNT = 3; // checks upgrade and re-use + static final long start = System.nanoTime(); + public static String now() { + long now = System.nanoTime() - start; + long secs = now / 1000_000_000; + long mill = (now % 1000_000_000) / 1000_000; + long nan = now % 1000_000; + return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan); + } + + static Version version(String uri) { + if (uri.contains("/http1/") || uri.contains("/https1/")) + return HTTP_1_1; + if (uri.contains("/http2/") || uri.contains("/https2/")) + return HTTP_2; + return null; + } + + + private HttpClient makeNewClient() { + return HttpClient.newBuilder() + .proxy(NO_PROXY) + .sslContext(sslContext) + .build(); + } + + HttpClient newHttpClient(boolean share) { + if (!share) return makeNewClient(); + HttpClient shared = sharedClient; + if (shared != null) return shared; + synchronized (this) { + shared = sharedClient; + if (shared == null) { + shared = sharedClient = makeNewClient(); + } + return shared; + } + } + + record CloseableClient(HttpClient client, boolean shared) + implements Closeable { + public void close() { + if (shared) return; + client.close(); + } + } + @Test(dataProvider = "variants") void test(String uriString, boolean sameClient) throws Exception { System.out.println("\n--- Starting "); @@ -121,122 +169,114 @@ public class EscapedOctetsInURI { HttpClient client = null; for (int i=0; i< ITERATION_COUNT; i++) { - if (!sameClient || client == null) - client = HttpClient.newBuilder() - .proxy(NO_PROXY) - .sslContext(sslContext) - .build(); + if (!sameClient || client == null) { + client = newHttpClient(sameClient); + } - HttpRequest request = HttpRequest.newBuilder(uri).build(); - HttpResponse resp = client.send(request, BodyHandlers.ofString()); + try (var cl = new CloseableClient(client, sameClient)) { + HttpRequest request = HttpRequest.newBuilder(uri).build(); + HttpResponse resp = client.send(request, BodyHandlers.ofString()); - out.println("Got response: " + resp); - out.println("Got body: " + resp.body()); - assertEquals(resp.statusCode(), 200, - "Expected 200, got:" + resp.statusCode()); + out.println("Got response: " + resp); + out.println("Got body: " + resp.body()); + assertEquals(resp.statusCode(), 200, + "Expected 200, got:" + resp.statusCode()); - // the response body should contain the exact escaped request URI - URI retrievedURI = URI.create(resp.body()); - assertEquals(retrievedURI.getRawPath(), uri.getRawPath()); - assertEquals(retrievedURI.getRawQuery(), uri.getRawQuery()); + // the response body should contain the exact escaped request URI + URI retrievedURI = URI.create(resp.body()); + assertEquals(retrievedURI.getRawPath(), uri.getRawPath()); + assertEquals(retrievedURI.getRawQuery(), uri.getRawQuery()); + assertEquals(resp.version(), version(uriString)); + } } } @Test(dataProvider = "variants") - void testAsync(String uriString, boolean sameClient) { + void testAsync(String uriString, boolean sameClient) throws Exception { System.out.println("\n--- Starting "); URI uri = URI.create(uriString); HttpClient client = null; for (int i=0; i< ITERATION_COUNT; i++) { - if (!sameClient || client == null) - client = HttpClient.newBuilder() - .proxy(NO_PROXY) - .sslContext(sslContext) - .build(); + if (!sameClient || client == null) { + client = newHttpClient(sameClient); + } - HttpRequest request = HttpRequest.newBuilder(uri).build(); - - client.sendAsync(request, BodyHandlers.ofString()) - .thenApply(response -> { - out.println("Got response: " + response); - out.println("Got body: " + response.body()); - assertEquals(response.statusCode(), 200); - return response.body(); }) - .thenApply(body -> URI.create(body)) - .thenAccept(retrievedURI -> { - // the body should contain the exact escaped request URI - assertEquals(retrievedURI.getRawPath(), uri.getRawPath()); - assertEquals(retrievedURI.getRawQuery(), uri.getRawQuery()); }) - .join(); + try (var cl = new CloseableClient(client, sameClient)) { + HttpRequest request = HttpRequest.newBuilder(uri).build(); + client.sendAsync(request, BodyHandlers.ofString()) + .thenApply(response -> { + out.println("Got response: " + response); + out.println("Got body: " + response.body()); + assertEquals(response.statusCode(), 200); + assertEquals(response.version(), version(uriString)); + return response.body(); + }) + .thenApply(body -> URI.create(body)) + .thenAccept(retrievedURI -> { + // the body should contain the exact escaped request URI + assertEquals(retrievedURI.getRawPath(), uri.getRawPath()); + assertEquals(retrievedURI.getRawQuery(), uri.getRawQuery()); + }).join(); + } } } - static String serverAuthority(HttpServer server) { - return InetAddress.getLoopbackAddress().getHostName() + ":" - + server.getAddress().getPort(); - } - @BeforeTest public void setup() throws Exception { + out.println(now() + "begin setup"); + sslContext = new SimpleSSLContext().get(); if (sslContext == null) throw new AssertionError("Unexpected null sslContext"); InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); - httpTestServer = HttpServer.create(sa, 0); - httpTestServer.createContext("/http1", new Http1ASCIIUriStringHandler()); - httpURI = "http://" + serverAuthority(httpTestServer) + "/http1"; + httpTestServer = HttpTestServer.create(HTTP_1_1); + httpTestServer.addHandler(new HttpASCIIUriStringHandler(), "/http1/get"); + httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/get"; - httpsTestServer = HttpsServer.create(sa, 0); - httpsTestServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); - httpsTestServer.createContext("/https1", new Http1ASCIIUriStringHandler()); - httpsURI = "https://" + serverAuthority(httpsTestServer) + "/https1"; + httpsTestServer = HttpTestServer.create(HTTP_1_1, sslContext); + httpsTestServer.addHandler(new HttpASCIIUriStringHandler(), "/https1/get"); + httpsURI = "https://" + httpsTestServer.serverAuthority() + "/https1/get"; - http2TestServer = new Http2TestServer("localhost", false, 0); - http2TestServer.addHandler(new HttpASCIIUriStringHandler(), "/http2"); - http2URI = "http://" + http2TestServer.serverAuthority() + "/http2"; + http2TestServer = HttpTestServer.create(HTTP_2); + http2TestServer.addHandler(new HttpASCIIUriStringHandler(), "/http2/get"); + http2URI = "http://" + http2TestServer.serverAuthority() + "/http2/get"; - https2TestServer = new Http2TestServer("localhost", true, sslContext); - https2TestServer.addHandler(new HttpASCIIUriStringHandler(), "/https2"); - https2URI = "https://" + https2TestServer.serverAuthority() + "/https2"; + https2TestServer = HttpTestServer.create(HTTP_2, sslContext); + https2TestServer.addHandler(new HttpASCIIUriStringHandler(), "/https2/get"); + https2URI = "https://" + https2TestServer.serverAuthority() + "/https2/get"; + err.println(now() + "Starting servers"); httpTestServer.start(); httpsTestServer.start(); http2TestServer.start(); https2TestServer.start(); + + out.println("HTTP/1.1 server (http) listening at: " + httpTestServer.serverAuthority()); + out.println("HTTP/1.1 server (TLS) listening at: " + httpsTestServer.serverAuthority()); + out.println("HTTP/2 server (h2c) listening at: " + http2TestServer.serverAuthority()); + out.println("HTTP/2 server (h2) listening at: " + https2TestServer.serverAuthority()); + + out.println(now() + "setup done"); + err.println(now() + "setup done"); } @AfterTest public void teardown() throws Exception { - httpTestServer.stop(0); - httpsTestServer.stop(0); + sharedClient.close(); + httpTestServer.stop(); + httpsTestServer.stop(); http2TestServer.stop(); https2TestServer.stop(); } /** A handler that returns as its body the exact escaped request URI. */ - static class Http1ASCIIUriStringHandler implements HttpHandler { + static class HttpASCIIUriStringHandler implements HttpTestHandler { @Override - public void handle(HttpExchange t) throws IOException { + public void handle(HttpTestExchange t) throws IOException { String asciiUriString = t.getRequestURI().toASCIIString(); - out.println("Http1ASCIIUriString received, asciiUriString: " + asciiUriString); - try (InputStream is = t.getRequestBody(); - OutputStream os = t.getResponseBody()) { - is.readAllBytes(); - byte[] bytes = asciiUriString.getBytes(US_ASCII); - t.sendResponseHeaders(200, bytes.length); - os.write(bytes); - } - } - } - - /** A handler that returns as its body the exact escaped request URI. */ - static class HttpASCIIUriStringHandler implements Http2Handler { - @Override - public void handle(Http2TestExchange t) throws IOException { - String asciiUriString = t.getRequestURI().toASCIIString(); - out.println("Http2ASCIIUriString received, asciiUriString: " + asciiUriString); + out.println("HttpASCIIUriString received, asciiUriString: " + asciiUriString); try (InputStream is = t.getRequestBody(); OutputStream os = t.getResponseBody()) { is.readAllBytes(); diff --git a/test/jdk/java/net/httpclient/NonAsciiCharsInURI.java b/test/jdk/java/net/httpclient/NonAsciiCharsInURI.java index 84519feeac1..56a35119ae2 100644 --- a/test/jdk/java/net/httpclient/NonAsciiCharsInURI.java +++ b/test/jdk/java/net/httpclient/NonAsciiCharsInURI.java @@ -34,17 +34,14 @@ * NonAsciiCharsInURI */ -import com.sun.net.httpserver.HttpServer; -import com.sun.net.httpserver.HttpsConfigurator; -import com.sun.net.httpserver.HttpsServer; +import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.InetAddress; -import java.net.InetSocketAddress; import java.net.URI; import javax.net.ssl.SSLContext; import java.net.http.HttpClient; +import java.net.http.HttpClient.Version; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; @@ -52,7 +49,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import jdk.httpclient.test.lib.common.HttpServerAdapters; -import jdk.httpclient.test.lib.http2.Http2TestServer; import jdk.test.lib.net.SimpleSSLContext; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; @@ -78,6 +74,8 @@ public class NonAsciiCharsInURI implements HttpServerAdapters { String http2URI; String https2URI; + private volatile HttpClient sharedClient; + // € = '\u20AC' => 0xE20x820xAC static final String[][] pathsAndQueryStrings = new String[][] { // partial-path @@ -110,6 +108,51 @@ public class NonAsciiCharsInURI implements HttpServerAdapters { return list.stream().toArray(Object[][]::new); } + static final long start = System.nanoTime(); + public static String now() { + long now = System.nanoTime() - start; + long secs = now / 1000_000_000; + long mill = (now % 1000_000_000) / 1000_000; + long nan = now % 1000_000; + return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan); + } + + static Version version(String uri) { + if (uri.contains("/http1/") || uri.contains("/https1/")) + return HTTP_1_1; + if (uri.contains("/http2/") || uri.contains("/https2/")) + return HTTP_2; + return null; + } + + private HttpClient makeNewClient() { + return HttpClient.newBuilder() + .proxy(NO_PROXY) + .sslContext(sslContext) + .build(); + } + + HttpClient newHttpClient(boolean share) { + if (!share) return makeNewClient(); + HttpClient shared = sharedClient; + if (shared != null) return shared; + synchronized (this) { + shared = sharedClient; + if (shared == null) { + shared = sharedClient = makeNewClient(); + } + return shared; + } + } + + record CloseableClient(HttpClient client, boolean shared) + implements Closeable { + public void close() { + if (shared) return; + client.close(); + } + } + static final int ITERATION_COUNT = 3; // checks upgrade and re-use @Test(dataProvider = "variants") @@ -122,106 +165,118 @@ public class NonAsciiCharsInURI implements HttpServerAdapters { HttpClient client = null; for (int i=0; i< ITERATION_COUNT; i++) { - if (!sameClient || client == null) - client = HttpClient.newBuilder() - .proxy(NO_PROXY) - .sslContext(sslContext) - .build(); + if (!sameClient || client == null) { + client = newHttpClient(sameClient); + } - HttpRequest request = HttpRequest.newBuilder(uri).build(); - HttpResponse resp = client.send(request, BodyHandlers.ofString()); - out.println("Got response: " + resp); - out.println("Got body: " + resp.body()); - assertEquals(resp.statusCode(), 200, - "Expected 200, got:" + resp.statusCode()); + try (var cl = new CloseableClient(client, sameClient)) { + HttpRequest request = HttpRequest.newBuilder(uri).build(); + HttpResponse resp = client.send(request, BodyHandlers.ofString()); - // the response body should contain the toASCIIString - // representation of the URI - String expectedURIString = uri.toASCIIString(); - if (!expectedURIString.contains(resp.body())) { - err.println("Test failed: " + resp); - throw new AssertionError(expectedURIString + - " does not contain '" + resp.body() + "'"); - } else { - out.println("Found expected " + resp.body() + " in " + expectedURIString); + out.println("Got response: " + resp); + out.println("Got body: " + resp.body()); + assertEquals(resp.statusCode(), 200, + "Expected 200, got:" + resp.statusCode()); + + // the response body should contain the toASCIIString + // representation of the URI + String expectedURIString = uri.toASCIIString(); + if (!expectedURIString.contains(resp.body())) { + err.println("Test failed: " + resp); + throw new AssertionError(expectedURIString + + " does not contain '" + resp.body() + "'"); + } else { + out.println("Found expected " + resp.body() + " in " + expectedURIString); + } + assertEquals(resp.version(), version(uriString)); } } } @Test(dataProvider = "variants") - void testAsync(String uriString, boolean sameClient) { + void testAsync(String uriString, boolean sameClient) throws Exception { out.println("\n--- Starting "); URI uri = URI.create(uriString); HttpClient client = null; for (int i=0; i< ITERATION_COUNT; i++) { - if (!sameClient || client == null) - client = HttpClient.newBuilder() - .proxy(NO_PROXY) - .sslContext(sslContext) - .build(); + if (!sameClient || client == null) { + client = newHttpClient(sameClient); + } - HttpRequest request = HttpRequest.newBuilder(uri).build(); + try (var cl = new CloseableClient(client, sameClient)) { + HttpRequest request = HttpRequest.newBuilder(uri).build(); - client.sendAsync(request, BodyHandlers.ofString()) - .thenApply(response -> { - out.println("Got response: " + response); - out.println("Got body: " + response.body()); - assertEquals(response.statusCode(), 200); - return response.body(); }) - .thenAccept(body -> { - // the response body should contain the toASCIIString - // representation of the URI - String expectedURIString = uri.toASCIIString(); - if (!expectedURIString.contains(body)) { - err.println("Test failed: " + body); - throw new AssertionError(expectedURIString + - " does not contain '" + body + "'"); - } else { - out.println("Found expected " + body + " in " + client.sendAsync(request, BodyHandlers.ofString()) + .thenApply(response -> { + out.println("Got response: " + response); + out.println("Got body: " + response.body()); + assertEquals(response.statusCode(), 200); + assertEquals(response.version(), version(uriString)); + return response.body(); + }) + .thenAccept(body -> { + // the response body should contain the toASCIIString + // representation of the URI + String expectedURIString = uri.toASCIIString(); + if (!expectedURIString.contains(body)) { + err.println("Test failed: " + body); + throw new AssertionError(expectedURIString + + " does not contain '" + body + "'"); + } else { + out.println("Found expected " + body + " in " + expectedURIString); - } }) - .join(); + } + }) + .join(); + } } } - static String serverAuthority(HttpTestServer server) { - return InetAddress.getLoopbackAddress().getHostName() + ":" - + server.getAddress().getPort(); - } - @BeforeTest public void setup() throws Exception { + out.println(now() + "begin setup"); + sslContext = new SimpleSSLContext().get(); if (sslContext == null) throw new AssertionError("Unexpected null sslContext"); HttpTestHandler handler = new HttpUriStringHandler(); httpTestServer = HttpTestServer.create(HTTP_1_1); - httpTestServer.addHandler(handler, "/http1"); - httpURI = "http://" + serverAuthority(httpTestServer) + "/http1"; + httpTestServer.addHandler(handler, "/http1/get"); + httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/get"; httpsTestServer = HttpTestServer.create(HTTP_1_1, sslContext); - httpsTestServer.addHandler(handler, "/https1"); - httpsURI = "https://" + serverAuthority(httpsTestServer) + "/https1"; + httpsTestServer.addHandler(handler, "/https1/get"); + httpsURI = "https://" + httpsTestServer.serverAuthority() + "/https1/get"; http2TestServer = HttpTestServer.create(HTTP_2); - http2TestServer.addHandler(handler, "/http2"); - http2URI = "http://" + http2TestServer.serverAuthority() + "/http2"; + http2TestServer.addHandler(handler, "/http2/get"); + http2URI = "http://" + http2TestServer.serverAuthority() + "/http2/get"; https2TestServer = HttpTestServer.create(HTTP_2, sslContext); - https2TestServer.addHandler(handler, "/https2"); - https2URI = "https://" + https2TestServer.serverAuthority() + "/https2"; + https2TestServer.addHandler(handler, "/https2/get"); + https2URI = "https://" + https2TestServer.serverAuthority() + "/https2/get"; + err.println(now() + "Starting servers"); httpTestServer.start(); httpsTestServer.start(); http2TestServer.start(); https2TestServer.start(); + + out.println("HTTP/1.1 server (http) listening at: " + httpTestServer.serverAuthority()); + out.println("HTTP/1.1 server (TLS) listening at: " + httpsTestServer.serverAuthority()); + out.println("HTTP/2 server (h2c) listening at: " + http2TestServer.serverAuthority()); + out.println("HTTP/2 server (h2) listening at: " + https2TestServer.serverAuthority()); + + out.println(now() + "setup done"); + err.println(now() + "setup done"); } @AfterTest public void teardown() throws Exception { + sharedClient.close(); httpTestServer.stop(); httpsTestServer.stop(); http2TestServer.stop(); @@ -233,7 +288,7 @@ public class NonAsciiCharsInURI implements HttpServerAdapters { @Override public void handle(HttpTestExchange t) throws IOException { String uri = t.getRequestURI().toString(); - out.println("Http1UriStringHandler received, uri: " + uri); + out.println("HttpUriStringHandler received, uri: " + uri); try (InputStream is = t.getRequestBody(); OutputStream os = t.getResponseBody()) { is.readAllBytes();