8306940: test/jdk/java/net/httpclient/XxxxInURI.java should call HttpClient::close

Reviewed-by: jpai, djelinski
This commit is contained in:
Daniel Fuchs 2023-04-27 08:25:40 +00:00
parent d94ce6566d
commit 41d58533ac
3 changed files with 315 additions and 194 deletions

View File

@ -36,9 +36,6 @@
*/ */
//* -Djdk.internal.httpclient.debug=true //* -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 jdk.test.lib.net.SimpleSSLContext;
import org.testng.annotations.AfterClass; import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterTest; import org.testng.annotations.AfterTest;
@ -48,6 +45,7 @@ import org.testng.annotations.Test;
import javax.net.ServerSocketFactory; import javax.net.ServerSocketFactory;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -57,6 +55,7 @@ import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.net.URI; import java.net.URI;
import java.net.http.HttpClient; import java.net.http.HttpClient;
import java.net.http.HttpClient.Version;
import java.net.http.HttpRequest; import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublisher; import java.net.http.HttpRequest.BodyPublisher;
import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpRequest.BodyPublishers;
@ -74,14 +73,11 @@ import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import jdk.httpclient.test.lib.common.HttpServerAdapters; 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.err;
import static java.lang.System.in;
import static java.lang.System.out; import static java.lang.System.out;
import static java.net.http.HttpClient.Version.HTTP_1_1; import static java.net.http.HttpClient.Version.HTTP_1_1;
import static java.net.http.HttpClient.Version.HTTP_2; 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.nio.charset.StandardCharsets.UTF_8;
import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpClient.Builder.NO_PROXY;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
@ -94,8 +90,8 @@ public class EncodedCharsInURI implements HttpServerAdapters {
HttpTestServer httpsTestServer; // HTTPS/1.1 HttpTestServer httpsTestServer; // HTTPS/1.1
HttpTestServer http2TestServer; // HTTP/2 ( h2c ) HttpTestServer http2TestServer; // HTTP/2 ( h2c )
HttpTestServer https2TestServer; // HTTP/2 ( h2 ) HttpTestServer https2TestServer; // HTTP/2 ( h2 )
DummyServer httpDummyServer; // HTTP/1.1 [ 2 servers ] DummyServer httpDummyServer; // HTTP/1.1 [ 2 servers ]
DummyServer httpsDummyServer; // HTTPS/1.1 DummyServer httpsDummyServer; // HTTPS/1.1
String httpURI_fixed; String httpURI_fixed;
String httpURI_chunk; String httpURI_chunk;
String httpsURI_fixed; String httpsURI_fixed;
@ -140,8 +136,8 @@ public class EncodedCharsInURI implements HttpServerAdapters {
command.run(); command.run();
} catch (Throwable t) { } catch (Throwable t) {
tasksFailed = true; tasksFailed = true;
System.out.printf(now() + "Task %s failed: %s%n", id, t); out.printf(now() + "Task %s failed: %s%n", id, t);
System.err.printf(now() + "Task %s failed: %s%n", id, t); err.printf(now() + "Task %s failed: %s%n", id, t);
FAILURES.putIfAbsent("Task " + id, t); FAILURES.putIfAbsent("Task " + id, t);
throw t; throw t;
} }
@ -162,7 +158,7 @@ public class EncodedCharsInURI implements HttpServerAdapters {
e.getValue().printStackTrace(out); e.getValue().printStackTrace(out);
}); });
if (tasksFailed) { if (tasksFailed) {
System.out.println("WARNING: Some tasks failed"); out.println("WARNING: Some tasks failed");
} }
} finally { } finally {
out.println("\n=========================\n"); out.println("\n=========================\n");
@ -201,6 +197,14 @@ public class EncodedCharsInURI implements HttpServerAdapters {
return result; 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() { private HttpClient makeNewClient() {
clientCount.incrementAndGet(); clientCount.incrementAndGet();
return HttpClient.newBuilder() return HttpClient.newBuilder()
@ -225,6 +229,14 @@ public class EncodedCharsInURI implements HttpServerAdapters {
final String ENCODED = "/01%252F03/"; final String ENCODED = "/01%252F03/";
record CloseableClient(HttpClient client, boolean shared)
implements Closeable {
public void close() {
if (shared) return;
client.close();
}
}
@Test(dataProvider = "noThrows") @Test(dataProvider = "noThrows")
public void testEncodedChars(String uri, boolean sameClient) public void testEncodedChars(String uri, boolean sameClient)
throws Exception { throws Exception {
@ -232,29 +244,34 @@ public class EncodedCharsInURI implements HttpServerAdapters {
out.printf("%n%s testEncodedChars(%s, %b)%n", now(), uri, sameClient); out.printf("%n%s testEncodedChars(%s, %b)%n", now(), uri, sameClient);
uri = uri + ENCODED; uri = uri + ENCODED;
for (int i=0; i< ITERATION_COUNT; i++) { for (int i=0; i< ITERATION_COUNT; i++) {
if (!sameClient || client == null) if (!sameClient || client == null) {
client = newHttpClient(sameClient); 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)
HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) .build();
.POST(bodyPublisher) BodyHandler<String> handler = BodyHandlers.ofString();
.build(); CompletableFuture<HttpResponse<String>> responseCF = client.sendAsync(req, handler);
BodyHandler<String> handler = BodyHandlers.ofString(); HttpResponse<String> response = responseCF.join();
CompletableFuture<HttpResponse<String>> responseCF = client.sendAsync(req, handler); String body = response.body();
HttpResponse<String> response = responseCF.join(); if (!uri.contains(body)) {
String body = response.body(); err.println("Test failed: " + response);
if (!uri.contains(body)) { throw new RuntimeException(uri + " doesn't contain '" + body + "'");
System.err.println("Test failed: " + response); } else {
throw new RuntimeException(uri + " doesn't contain '" + body + "'"); out.println("Found expected " + body + " in " + uri);
} else { }
System.out.println("Found expected " + body + " in " + uri); assertEquals(response.version(), version(uri));
} }
} }
} }
@BeforeTest @BeforeTest
public void setup() throws Exception { public void setup() throws Exception {
out.println(now() + "begin setup");
sslContext = new SimpleSSLContext().get(); sslContext = new SimpleSSLContext().get();
if (sslContext == null) if (sslContext == null)
throw new AssertionError("Unexpected null sslContext"); throw new AssertionError("Unexpected null sslContext");
@ -297,6 +314,7 @@ public class EncodedCharsInURI implements HttpServerAdapters {
httpDummy = "http://" + httpDummyServer.serverAuthority() + "/http1/dummy/x"; httpDummy = "http://" + httpDummyServer.serverAuthority() + "/http1/dummy/x";
httpsDummy = "https://" + httpsDummyServer.serverAuthority() + "/https1/dummy/x"; httpsDummy = "https://" + httpsDummyServer.serverAuthority() + "/https1/dummy/x";
err.println(now() + "Starting servers");
serverCount.addAndGet(6); serverCount.addAndGet(6);
httpTestServer.start(); httpTestServer.start();
@ -305,11 +323,21 @@ public class EncodedCharsInURI implements HttpServerAdapters {
https2TestServer.start(); https2TestServer.start();
httpDummyServer.start(); httpDummyServer.start();
httpsDummyServer.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 @AfterTest
public void teardown() throws Exception { public void teardown() throws Exception {
sharedClient = null; sharedClient.close();
httpTestServer.stop(); httpTestServer.stop();
httpsTestServer.stop(); httpsTestServer.stop();
http2TestServer.stop(); http2TestServer.stop();
@ -390,14 +418,13 @@ public class EncodedCharsInURI implements HttpServerAdapters {
while(!stopped) { while(!stopped) {
Socket clientConnection = ss.accept(); Socket clientConnection = ss.accept();
connections.add(clientConnection); connections.add(clientConnection);
System.out.println(now() + getName() + ": Client accepted"); out.println(now() + getName() + ": Client accepted");
StringBuilder headers = new StringBuilder(); StringBuilder headers = new StringBuilder();
Socket targetConnection = null;
InputStream ccis = clientConnection.getInputStream(); InputStream ccis = clientConnection.getInputStream();
OutputStream ccos = clientConnection.getOutputStream(); OutputStream ccos = clientConnection.getOutputStream();
System.out.println(now() + getName() + ": Reading request line"); out.println(now() + getName() + ": Reading request line");
String requestLine = readLine(ccis); String requestLine = readLine(ccis);
System.out.println(now() + getName() + ": Request line: " + requestLine); out.println(now() + getName() + ": Request line: " + requestLine);
StringTokenizer tokenizer = new StringTokenizer(requestLine); StringTokenizer tokenizer = new StringTokenizer(requestLine);
String method = tokenizer.nextToken(); String method = tokenizer.nextToken();
@ -408,7 +435,7 @@ public class EncodedCharsInURI implements HttpServerAdapters {
String hostport = serverAuthority(); String hostport = serverAuthority();
uri = new URI((secure ? "https" : "http") +"://" + hostport + path); uri = new URI((secure ? "https" : "http") +"://" + hostport + path);
} catch (Throwable x) { } catch (Throwable x) {
System.err.printf("Bad target address: \"%s\" in \"%s\"%n", err.printf("Bad target address: \"%s\" in \"%s\"%n",
path, requestLine); path, requestLine);
clientConnection.close(); clientConnection.close();
continue; continue;
@ -418,7 +445,7 @@ public class EncodedCharsInURI implements HttpServerAdapters {
// signals the end of all headers. // signals the end of all headers.
String line = requestLine; String line = requestLine;
while (!line.equals("")) { while (!line.equals("")) {
System.out.println(now() + getName() + ": Reading header: " out.println(now() + getName() + ": Reading header: "
+ (line = readLine(ccis))); + (line = readLine(ccis)));
headers.append(line).append("\r\n"); headers.append(line).append("\r\n");
} }
@ -435,11 +462,11 @@ public class EncodedCharsInURI implements HttpServerAdapters {
StringTokenizer tk = new StringTokenizer(cl); StringTokenizer tk = new StringTokenizer(cl);
int len = Integer.parseInt(tk.nextToken()); int len = Integer.parseInt(tk.nextToken());
assert len < b.length * 2; assert len < b.length * 2;
System.out.println(now() + getName() out.println(now() + getName()
+ ": received body: " + ": received body: "
+ new String(ccis.readNBytes(len), UTF_8)); + new String(ccis.readNBytes(len), UTF_8));
} }
System.out.println(now() out.println(now()
+ getName() + ": sending back " + uri); + getName() + ": sending back " + uri);
response.append("HTTP/1.1 200 OK\r\nContent-Length: ") response.append("HTTP/1.1 200 OK\r\nContent-Length: ")
@ -447,21 +474,21 @@ public class EncodedCharsInURI implements HttpServerAdapters {
.append("\r\n\r\n"); .append("\r\n\r\n");
// Then send the 200 OK response to the client // Then send the 200 OK response to the client
System.out.println(now() + getName() + ": Sending " out.println(now() + getName() + ": Sending "
+ response); + response);
ccos.write(response.toString().getBytes(UTF_8)); ccos.write(response.toString().getBytes(UTF_8));
ccos.flush(); ccos.flush();
System.out.println(now() + getName() + ": sent response headers"); out.println(now() + getName() + ": sent response headers");
ccos.write(b); ccos.write(b);
ccos.flush(); ccos.flush();
ccos.close(); ccos.close();
System.out.println(now() + getName() + ": sent " + b.length + " body bytes"); out.println(now() + getName() + ": sent " + b.length + " body bytes");
connections.remove(clientConnection); connections.remove(clientConnection);
clientConnection.close(); clientConnection.close();
} }
} catch (Throwable t) { } catch (Throwable t) {
if (!stopped) { if (!stopped) {
System.out.println(now() + getName() + ": failed: " + t); out.println(now() + getName() + ": failed: " + t);
t.printStackTrace(); t.printStackTrace();
try { try {
stopServer(); stopServer();
@ -470,7 +497,7 @@ public class EncodedCharsInURI implements HttpServerAdapters {
} }
} }
} finally { } 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); return new DummyServer(ss, true);
} }
} }
} }

View File

@ -32,11 +32,7 @@
* EscapedOctetsInURI * EscapedOctetsInURI
*/ */
import com.sun.net.httpserver.HttpExchange; import java.io.Closeable;
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.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -45,37 +41,43 @@ import java.net.InetSocketAddress;
import java.net.URI; import java.net.URI;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import java.net.http.HttpClient; import java.net.http.HttpClient;
import java.net.http.HttpClient.Version;
import java.net.http.HttpRequest; import java.net.http.HttpRequest;
import java.net.http.HttpResponse; import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers; import java.net.http.HttpResponse.BodyHandlers;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import jdk.httpclient.test.lib.http2.Http2TestServer;
import jdk.httpclient.test.lib.http2.Http2TestExchange; import jdk.httpclient.test.lib.common.HttpServerAdapters;
import jdk.httpclient.test.lib.http2.Http2Handler;
import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.net.SimpleSSLContext;
import org.testng.annotations.AfterTest; import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest; import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider; import org.testng.annotations.DataProvider;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import static java.lang.System.err;
import static java.lang.System.out; 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.US_ASCII;
import static java.net.http.HttpClient.Builder.NO_PROXY; import static java.net.http.HttpClient.Builder.NO_PROXY;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
public class EscapedOctetsInURI { public class EscapedOctetsInURI implements HttpServerAdapters {
SSLContext sslContext; SSLContext sslContext;
HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ] HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ]
HttpsServer httpsTestServer; // HTTPS/1.1 HttpTestServer httpsTestServer; // HTTPS/1.1
Http2TestServer http2TestServer; // HTTP/2 ( h2c ) HttpTestServer http2TestServer; // HTTP/2 ( h2c )
Http2TestServer https2TestServer; // HTTP/2 ( h2 ) HttpTestServer https2TestServer; // HTTP/2 ( h2 )
String httpURI; String httpURI;
String httpsURI; String httpsURI;
String http2URI; String http2URI;
String https2URI; String https2URI;
private volatile HttpClient sharedClient;
static final String[][] pathsAndQueryStrings = new String[][] { static final String[][] pathsAndQueryStrings = new String[][] {
// partial-path URI query // partial-path URI query
{ "/001/noSpace", "?noQuotedOctets" }, { "/001/noSpace", "?noQuotedOctets" },
@ -110,6 +112,52 @@ public class EscapedOctetsInURI {
static final int ITERATION_COUNT = 3; // checks upgrade and re-use 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") @Test(dataProvider = "variants")
void test(String uriString, boolean sameClient) throws Exception { void test(String uriString, boolean sameClient) throws Exception {
System.out.println("\n--- Starting "); System.out.println("\n--- Starting ");
@ -121,122 +169,114 @@ public class EscapedOctetsInURI {
HttpClient client = null; HttpClient client = null;
for (int i=0; i< ITERATION_COUNT; i++) { for (int i=0; i< ITERATION_COUNT; i++) {
if (!sameClient || client == null) if (!sameClient || client == null) {
client = HttpClient.newBuilder() client = newHttpClient(sameClient);
.proxy(NO_PROXY) }
.sslContext(sslContext)
.build();
HttpRequest request = HttpRequest.newBuilder(uri).build(); try (var cl = new CloseableClient(client, sameClient)) {
HttpResponse<String> resp = client.send(request, BodyHandlers.ofString()); HttpRequest request = HttpRequest.newBuilder(uri).build();
HttpResponse<String> resp = client.send(request, BodyHandlers.ofString());
out.println("Got response: " + resp); out.println("Got response: " + resp);
out.println("Got body: " + resp.body()); out.println("Got body: " + resp.body());
assertEquals(resp.statusCode(), 200, assertEquals(resp.statusCode(), 200,
"Expected 200, got:" + resp.statusCode()); "Expected 200, got:" + resp.statusCode());
// the response body should contain the exact escaped request URI // the response body should contain the exact escaped request URI
URI retrievedURI = URI.create(resp.body()); URI retrievedURI = URI.create(resp.body());
assertEquals(retrievedURI.getRawPath(), uri.getRawPath()); assertEquals(retrievedURI.getRawPath(), uri.getRawPath());
assertEquals(retrievedURI.getRawQuery(), uri.getRawQuery()); assertEquals(retrievedURI.getRawQuery(), uri.getRawQuery());
assertEquals(resp.version(), version(uriString));
}
} }
} }
@Test(dataProvider = "variants") @Test(dataProvider = "variants")
void testAsync(String uriString, boolean sameClient) { void testAsync(String uriString, boolean sameClient) throws Exception {
System.out.println("\n--- Starting "); System.out.println("\n--- Starting ");
URI uri = URI.create(uriString); URI uri = URI.create(uriString);
HttpClient client = null; HttpClient client = null;
for (int i=0; i< ITERATION_COUNT; i++) { for (int i=0; i< ITERATION_COUNT; i++) {
if (!sameClient || client == null) if (!sameClient || client == null) {
client = HttpClient.newBuilder() client = newHttpClient(sameClient);
.proxy(NO_PROXY) }
.sslContext(sslContext)
.build();
HttpRequest request = HttpRequest.newBuilder(uri).build(); try (var cl = new CloseableClient(client, sameClient)) {
HttpRequest request = HttpRequest.newBuilder(uri).build();
client.sendAsync(request, BodyHandlers.ofString()) client.sendAsync(request, BodyHandlers.ofString())
.thenApply(response -> { .thenApply(response -> {
out.println("Got response: " + response); out.println("Got response: " + response);
out.println("Got body: " + response.body()); out.println("Got body: " + response.body());
assertEquals(response.statusCode(), 200); assertEquals(response.statusCode(), 200);
return response.body(); }) assertEquals(response.version(), version(uriString));
.thenApply(body -> URI.create(body)) return response.body();
.thenAccept(retrievedURI -> { })
// the body should contain the exact escaped request URI .thenApply(body -> URI.create(body))
assertEquals(retrievedURI.getRawPath(), uri.getRawPath()); .thenAccept(retrievedURI -> {
assertEquals(retrievedURI.getRawQuery(), uri.getRawQuery()); }) // the body should contain the exact escaped request URI
.join(); assertEquals(retrievedURI.getRawPath(), uri.getRawPath());
assertEquals(retrievedURI.getRawQuery(), uri.getRawQuery());
}).join();
}
} }
} }
static String serverAuthority(HttpServer server) {
return InetAddress.getLoopbackAddress().getHostName() + ":"
+ server.getAddress().getPort();
}
@BeforeTest @BeforeTest
public void setup() throws Exception { public void setup() throws Exception {
out.println(now() + "begin setup");
sslContext = new SimpleSSLContext().get(); sslContext = new SimpleSSLContext().get();
if (sslContext == null) if (sslContext == null)
throw new AssertionError("Unexpected null sslContext"); throw new AssertionError("Unexpected null sslContext");
InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
httpTestServer = HttpServer.create(sa, 0); httpTestServer = HttpTestServer.create(HTTP_1_1);
httpTestServer.createContext("/http1", new Http1ASCIIUriStringHandler()); httpTestServer.addHandler(new HttpASCIIUriStringHandler(), "/http1/get");
httpURI = "http://" + serverAuthority(httpTestServer) + "/http1"; httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/get";
httpsTestServer = HttpsServer.create(sa, 0); httpsTestServer = HttpTestServer.create(HTTP_1_1, sslContext);
httpsTestServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); httpsTestServer.addHandler(new HttpASCIIUriStringHandler(), "/https1/get");
httpsTestServer.createContext("/https1", new Http1ASCIIUriStringHandler()); httpsURI = "https://" + httpsTestServer.serverAuthority() + "/https1/get";
httpsURI = "https://" + serverAuthority(httpsTestServer) + "/https1";
http2TestServer = new Http2TestServer("localhost", false, 0); http2TestServer = HttpTestServer.create(HTTP_2);
http2TestServer.addHandler(new HttpASCIIUriStringHandler(), "/http2"); http2TestServer.addHandler(new HttpASCIIUriStringHandler(), "/http2/get");
http2URI = "http://" + http2TestServer.serverAuthority() + "/http2"; http2URI = "http://" + http2TestServer.serverAuthority() + "/http2/get";
https2TestServer = new Http2TestServer("localhost", true, sslContext); https2TestServer = HttpTestServer.create(HTTP_2, sslContext);
https2TestServer.addHandler(new HttpASCIIUriStringHandler(), "/https2"); https2TestServer.addHandler(new HttpASCIIUriStringHandler(), "/https2/get");
https2URI = "https://" + https2TestServer.serverAuthority() + "/https2"; https2URI = "https://" + https2TestServer.serverAuthority() + "/https2/get";
err.println(now() + "Starting servers");
httpTestServer.start(); httpTestServer.start();
httpsTestServer.start(); httpsTestServer.start();
http2TestServer.start(); http2TestServer.start();
https2TestServer.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 @AfterTest
public void teardown() throws Exception { public void teardown() throws Exception {
httpTestServer.stop(0); sharedClient.close();
httpsTestServer.stop(0); httpTestServer.stop();
httpsTestServer.stop();
http2TestServer.stop(); http2TestServer.stop();
https2TestServer.stop(); https2TestServer.stop();
} }
/** A handler that returns as its body the exact escaped request URI. */ /** A handler that returns as its body the exact escaped request URI. */
static class Http1ASCIIUriStringHandler implements HttpHandler { static class HttpASCIIUriStringHandler implements HttpTestHandler {
@Override @Override
public void handle(HttpExchange t) throws IOException { public void handle(HttpTestExchange t) throws IOException {
String asciiUriString = t.getRequestURI().toASCIIString(); String asciiUriString = t.getRequestURI().toASCIIString();
out.println("Http1ASCIIUriString received, asciiUriString: " + asciiUriString); out.println("HttpASCIIUriString 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);
try (InputStream is = t.getRequestBody(); try (InputStream is = t.getRequestBody();
OutputStream os = t.getResponseBody()) { OutputStream os = t.getResponseBody()) {
is.readAllBytes(); is.readAllBytes();

View File

@ -34,17 +34,14 @@
* NonAsciiCharsInURI * NonAsciiCharsInURI
*/ */
import com.sun.net.httpserver.HttpServer; import java.io.Closeable;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsServer;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI; import java.net.URI;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import java.net.http.HttpClient; import java.net.http.HttpClient;
import java.net.http.HttpClient.Version;
import java.net.http.HttpRequest; import java.net.http.HttpRequest;
import java.net.http.HttpResponse; import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers; import java.net.http.HttpResponse.BodyHandlers;
@ -52,7 +49,6 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.httpclient.test.lib.common.HttpServerAdapters;
import jdk.httpclient.test.lib.http2.Http2TestServer;
import jdk.test.lib.net.SimpleSSLContext; import jdk.test.lib.net.SimpleSSLContext;
import org.testng.annotations.AfterTest; import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest; import org.testng.annotations.BeforeTest;
@ -78,6 +74,8 @@ public class NonAsciiCharsInURI implements HttpServerAdapters {
String http2URI; String http2URI;
String https2URI; String https2URI;
private volatile HttpClient sharedClient;
// = '\u20AC' => 0xE20x820xAC // = '\u20AC' => 0xE20x820xAC
static final String[][] pathsAndQueryStrings = new String[][] { static final String[][] pathsAndQueryStrings = new String[][] {
// partial-path // partial-path
@ -110,6 +108,51 @@ public class NonAsciiCharsInURI implements HttpServerAdapters {
return list.stream().toArray(Object[][]::new); 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 static final int ITERATION_COUNT = 3; // checks upgrade and re-use
@Test(dataProvider = "variants") @Test(dataProvider = "variants")
@ -122,106 +165,118 @@ public class NonAsciiCharsInURI implements HttpServerAdapters {
HttpClient client = null; HttpClient client = null;
for (int i=0; i< ITERATION_COUNT; i++) { for (int i=0; i< ITERATION_COUNT; i++) {
if (!sameClient || client == null) if (!sameClient || client == null) {
client = HttpClient.newBuilder() client = newHttpClient(sameClient);
.proxy(NO_PROXY) }
.sslContext(sslContext)
.build();
HttpRequest request = HttpRequest.newBuilder(uri).build();
HttpResponse<String> resp = client.send(request, BodyHandlers.ofString());
out.println("Got response: " + resp); try (var cl = new CloseableClient(client, sameClient)) {
out.println("Got body: " + resp.body()); HttpRequest request = HttpRequest.newBuilder(uri).build();
assertEquals(resp.statusCode(), 200, HttpResponse<String> resp = client.send(request, BodyHandlers.ofString());
"Expected 200, got:" + resp.statusCode());
// the response body should contain the toASCIIString out.println("Got response: " + resp);
// representation of the URI out.println("Got body: " + resp.body());
String expectedURIString = uri.toASCIIString(); assertEquals(resp.statusCode(), 200,
if (!expectedURIString.contains(resp.body())) { "Expected 200, got:" + resp.statusCode());
err.println("Test failed: " + resp);
throw new AssertionError(expectedURIString + // the response body should contain the toASCIIString
" does not contain '" + resp.body() + "'"); // representation of the URI
} else { String expectedURIString = uri.toASCIIString();
out.println("Found expected " + resp.body() + " in " + expectedURIString); 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") @Test(dataProvider = "variants")
void testAsync(String uriString, boolean sameClient) { void testAsync(String uriString, boolean sameClient) throws Exception {
out.println("\n--- Starting "); out.println("\n--- Starting ");
URI uri = URI.create(uriString); URI uri = URI.create(uriString);
HttpClient client = null; HttpClient client = null;
for (int i=0; i< ITERATION_COUNT; i++) { for (int i=0; i< ITERATION_COUNT; i++) {
if (!sameClient || client == null) if (!sameClient || client == null) {
client = HttpClient.newBuilder() client = newHttpClient(sameClient);
.proxy(NO_PROXY) }
.sslContext(sslContext)
.build();
HttpRequest request = HttpRequest.newBuilder(uri).build(); try (var cl = new CloseableClient(client, sameClient)) {
HttpRequest request = HttpRequest.newBuilder(uri).build();
client.sendAsync(request, BodyHandlers.ofString()) client.sendAsync(request, BodyHandlers.ofString())
.thenApply(response -> { .thenApply(response -> {
out.println("Got response: " + response); out.println("Got response: " + response);
out.println("Got body: " + response.body()); out.println("Got body: " + response.body());
assertEquals(response.statusCode(), 200); assertEquals(response.statusCode(), 200);
return response.body(); }) assertEquals(response.version(), version(uriString));
.thenAccept(body -> { return response.body();
// the response body should contain the toASCIIString })
// representation of the URI .thenAccept(body -> {
String expectedURIString = uri.toASCIIString(); // the response body should contain the toASCIIString
if (!expectedURIString.contains(body)) { // representation of the URI
err.println("Test failed: " + body); String expectedURIString = uri.toASCIIString();
throw new AssertionError(expectedURIString + if (!expectedURIString.contains(body)) {
" does not contain '" + body + "'"); err.println("Test failed: " + body);
} else { throw new AssertionError(expectedURIString +
out.println("Found expected " + body + " in " " does not contain '" + body + "'");
} else {
out.println("Found expected " + body + " in "
+ expectedURIString); + expectedURIString);
} }) }
.join(); })
.join();
}
} }
} }
static String serverAuthority(HttpTestServer server) {
return InetAddress.getLoopbackAddress().getHostName() + ":"
+ server.getAddress().getPort();
}
@BeforeTest @BeforeTest
public void setup() throws Exception { public void setup() throws Exception {
out.println(now() + "begin setup");
sslContext = new SimpleSSLContext().get(); sslContext = new SimpleSSLContext().get();
if (sslContext == null) if (sslContext == null)
throw new AssertionError("Unexpected null sslContext"); throw new AssertionError("Unexpected null sslContext");
HttpTestHandler handler = new HttpUriStringHandler(); HttpTestHandler handler = new HttpUriStringHandler();
httpTestServer = HttpTestServer.create(HTTP_1_1); httpTestServer = HttpTestServer.create(HTTP_1_1);
httpTestServer.addHandler(handler, "/http1"); httpTestServer.addHandler(handler, "/http1/get");
httpURI = "http://" + serverAuthority(httpTestServer) + "/http1"; httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/get";
httpsTestServer = HttpTestServer.create(HTTP_1_1, sslContext); httpsTestServer = HttpTestServer.create(HTTP_1_1, sslContext);
httpsTestServer.addHandler(handler, "/https1"); httpsTestServer.addHandler(handler, "/https1/get");
httpsURI = "https://" + serverAuthority(httpsTestServer) + "/https1"; httpsURI = "https://" + httpsTestServer.serverAuthority() + "/https1/get";
http2TestServer = HttpTestServer.create(HTTP_2); http2TestServer = HttpTestServer.create(HTTP_2);
http2TestServer.addHandler(handler, "/http2"); http2TestServer.addHandler(handler, "/http2/get");
http2URI = "http://" + http2TestServer.serverAuthority() + "/http2"; http2URI = "http://" + http2TestServer.serverAuthority() + "/http2/get";
https2TestServer = HttpTestServer.create(HTTP_2, sslContext); https2TestServer = HttpTestServer.create(HTTP_2, sslContext);
https2TestServer.addHandler(handler, "/https2"); https2TestServer.addHandler(handler, "/https2/get");
https2URI = "https://" + https2TestServer.serverAuthority() + "/https2"; https2URI = "https://" + https2TestServer.serverAuthority() + "/https2/get";
err.println(now() + "Starting servers");
httpTestServer.start(); httpTestServer.start();
httpsTestServer.start(); httpsTestServer.start();
http2TestServer.start(); http2TestServer.start();
https2TestServer.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 @AfterTest
public void teardown() throws Exception { public void teardown() throws Exception {
sharedClient.close();
httpTestServer.stop(); httpTestServer.stop();
httpsTestServer.stop(); httpsTestServer.stop();
http2TestServer.stop(); http2TestServer.stop();
@ -233,7 +288,7 @@ public class NonAsciiCharsInURI implements HttpServerAdapters {
@Override @Override
public void handle(HttpTestExchange t) throws IOException { public void handle(HttpTestExchange t) throws IOException {
String uri = t.getRequestURI().toString(); String uri = t.getRequestURI().toString();
out.println("Http1UriStringHandler received, uri: " + uri); out.println("HttpUriStringHandler received, uri: " + uri);
try (InputStream is = t.getRequestBody(); try (InputStream is = t.getRequestBody();
OutputStream os = t.getResponseBody()) { OutputStream os = t.getResponseBody()) {
is.readAllBytes(); is.readAllBytes();