Path: blob/master/test/jdk/java/net/httpclient/EscapedOctetsInURI.java
41152 views
/*1* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @summary Preserve URI component escaped octets when converting to HTTP headers26* @bug 819871627* @modules java.base/sun.net.www.http28* java.net.http/jdk.internal.net.http.common29* java.net.http/jdk.internal.net.http.frame30* java.net.http/jdk.internal.net.http.hpack31* java.logging32* jdk.httpserver33* @library /test/lib http2/server34* @build Http2TestServer35* @build jdk.test.lib.net.SimpleSSLContext36* @run testng/othervm37* -Djdk.httpclient.HttpClient.log=reqeusts,headers38* EscapedOctetsInURI39*/4041import com.sun.net.httpserver.HttpExchange;42import com.sun.net.httpserver.HttpHandler;43import com.sun.net.httpserver.HttpServer;44import com.sun.net.httpserver.HttpsConfigurator;45import com.sun.net.httpserver.HttpsServer;46import java.io.IOException;47import java.io.InputStream;48import java.io.OutputStream;49import java.net.InetAddress;50import java.net.InetSocketAddress;51import java.net.URI;52import javax.net.ssl.SSLContext;53import java.net.http.HttpClient;54import java.net.http.HttpRequest;55import java.net.http.HttpResponse;56import java.net.http.HttpResponse.BodyHandlers;57import java.util.ArrayList;58import java.util.Arrays;59import java.util.List;60import jdk.test.lib.net.SimpleSSLContext;61import org.testng.annotations.AfterTest;62import org.testng.annotations.BeforeTest;63import org.testng.annotations.DataProvider;64import org.testng.annotations.Test;65import static java.lang.System.out;66import static java.nio.charset.StandardCharsets.US_ASCII;67import static java.net.http.HttpClient.Builder.NO_PROXY;68import static org.testng.Assert.assertEquals;6970public class EscapedOctetsInURI {7172SSLContext sslContext;73HttpServer httpTestServer; // HTTP/1.1 [ 4 servers ]74HttpsServer httpsTestServer; // HTTPS/1.175Http2TestServer http2TestServer; // HTTP/2 ( h2c )76Http2TestServer https2TestServer; // HTTP/2 ( h2 )77String httpURI;78String httpsURI;79String http2URI;80String https2URI;8182static final String[][] pathsAndQueryStrings = new String[][] {83// partial-path URI query84{ "/001/noSpace", "?noQuotedOctets" },85{ "/002/noSpace", "?name=chegar,address=Dublin%20Ireland", },86{ "/003/noSpace", "?target=http%3A%2F%2Fwww.w3.org%2Fns%2Foa%23hasBody" },8788{ "/010/with%20space", "?noQuotedOctets" },89{ "/011/with%20space", "?name=chegar,address=Dublin%20Ireland" },90{ "/012/with%20space", "?target=http%3A%2F%2Fwww.w3.org%2Fns%2Foa%23hasBody" },91};9293@DataProvider(name = "variants")94public Object[][] variants() {95List<Object[]> list = new ArrayList<>();9697for (boolean sameClient : new boolean[] { false, true }) {98Arrays.asList(pathsAndQueryStrings).stream()99.map(e -> new Object[] {httpURI + e[0] + e[1], sameClient})100.forEach(list::add);101Arrays.asList(pathsAndQueryStrings).stream()102.map(e -> new Object[] {httpsURI + e[0] + e[1], sameClient})103.forEach(list::add);104Arrays.asList(pathsAndQueryStrings).stream()105.map(e -> new Object[] {http2URI + e[0] + e[1], sameClient})106.forEach(list::add);107Arrays.asList(pathsAndQueryStrings).stream()108.map(e -> new Object[] {https2URI + e[0] + e[1], sameClient})109.forEach(list::add);110}111return list.stream().toArray(Object[][]::new);112}113114static final int ITERATION_COUNT = 3; // checks upgrade and re-use115116@Test(dataProvider = "variants")117void test(String uriString, boolean sameClient) throws Exception {118System.out.println("\n--- Starting ");119120// The single-argument factory requires any illegal characters in its121// argument to be quoted and preserves any escaped octets and other122// characters that are present.123URI uri = URI.create(uriString);124125HttpClient client = null;126for (int i=0; i< ITERATION_COUNT; i++) {127if (!sameClient || client == null)128client = HttpClient.newBuilder()129.proxy(NO_PROXY)130.sslContext(sslContext)131.build();132133HttpRequest request = HttpRequest.newBuilder(uri).build();134HttpResponse<String> resp = client.send(request, BodyHandlers.ofString());135136out.println("Got response: " + resp);137out.println("Got body: " + resp.body());138assertEquals(resp.statusCode(), 200,139"Expected 200, got:" + resp.statusCode());140141// the response body should contain the exact escaped request URI142URI retrievedURI = URI.create(resp.body());143assertEquals(retrievedURI.getRawPath(), uri.getRawPath());144assertEquals(retrievedURI.getRawQuery(), uri.getRawQuery());145}146}147148@Test(dataProvider = "variants")149void testAsync(String uriString, boolean sameClient) {150System.out.println("\n--- Starting ");151URI uri = URI.create(uriString);152153HttpClient client = null;154for (int i=0; i< ITERATION_COUNT; i++) {155if (!sameClient || client == null)156client = HttpClient.newBuilder()157.proxy(NO_PROXY)158.sslContext(sslContext)159.build();160161HttpRequest request = HttpRequest.newBuilder(uri).build();162163client.sendAsync(request, BodyHandlers.ofString())164.thenApply(response -> {165out.println("Got response: " + response);166out.println("Got body: " + response.body());167assertEquals(response.statusCode(), 200);168return response.body(); })169.thenApply(body -> URI.create(body))170.thenAccept(retrievedURI -> {171// the body should contain the exact escaped request URI172assertEquals(retrievedURI.getRawPath(), uri.getRawPath());173assertEquals(retrievedURI.getRawQuery(), uri.getRawQuery()); })174.join();175}176}177178static String serverAuthority(HttpServer server) {179return InetAddress.getLoopbackAddress().getHostName() + ":"180+ server.getAddress().getPort();181}182183@BeforeTest184public void setup() throws Exception {185sslContext = new SimpleSSLContext().get();186if (sslContext == null)187throw new AssertionError("Unexpected null sslContext");188189InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);190httpTestServer = HttpServer.create(sa, 0);191httpTestServer.createContext("/http1", new Http1ASCIIUriStringHandler());192httpURI = "http://" + serverAuthority(httpTestServer) + "/http1";193194httpsTestServer = HttpsServer.create(sa, 0);195httpsTestServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));196httpsTestServer.createContext("/https1", new Http1ASCIIUriStringHandler());197httpsURI = "https://" + serverAuthority(httpsTestServer) + "/https1";198199http2TestServer = new Http2TestServer("localhost", false, 0);200http2TestServer.addHandler(new HttpASCIIUriStringHandler(), "/http2");201http2URI = "http://" + http2TestServer.serverAuthority() + "/http2";202203https2TestServer = new Http2TestServer("localhost", true, sslContext);204https2TestServer.addHandler(new HttpASCIIUriStringHandler(), "/https2");205https2URI = "https://" + https2TestServer.serverAuthority() + "/https2";206207httpTestServer.start();208httpsTestServer.start();209http2TestServer.start();210https2TestServer.start();211}212213@AfterTest214public void teardown() throws Exception {215httpTestServer.stop(0);216httpsTestServer.stop(0);217http2TestServer.stop();218https2TestServer.stop();219}220221/** A handler that returns as its body the exact escaped request URI. */222static class Http1ASCIIUriStringHandler implements HttpHandler {223@Override224public void handle(HttpExchange t) throws IOException {225String asciiUriString = t.getRequestURI().toASCIIString();226out.println("Http1ASCIIUriString received, asciiUriString: " + asciiUriString);227try (InputStream is = t.getRequestBody();228OutputStream os = t.getResponseBody()) {229is.readAllBytes();230byte[] bytes = asciiUriString.getBytes(US_ASCII);231t.sendResponseHeaders(200, bytes.length);232os.write(bytes);233}234}235}236237/** A handler that returns as its body the exact escaped request URI. */238static class HttpASCIIUriStringHandler implements Http2Handler {239@Override240public void handle(Http2TestExchange t) throws IOException {241String asciiUriString = t.getRequestURI().toASCIIString();242out.println("Http2ASCIIUriString received, asciiUriString: " + asciiUriString);243try (InputStream is = t.getRequestBody();244OutputStream os = t.getResponseBody()) {245is.readAllBytes();246byte[] bytes = asciiUriString.getBytes(US_ASCII);247t.sendResponseHeaders(200, bytes.length);248os.write(bytes);249}250}251}252}253254255