1 /* 2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8156514 27 * @library /lib/testlibrary server 28 * @build jdk.testlibrary.SimpleSSLContext 29 * @modules java.base/sun.net.www.http 30 * jdk.incubator.httpclient/jdk.incubator.http.internal.common 31 * jdk.incubator.httpclient/jdk.incubator.http.internal.frame 32 * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack 33 * @run testng/othervm -Djdk.httpclient.HttpClient.log=frames,ssl,requests,responses,errors RedirectTest 34 */ 35 36 import java.net.*; 37 import jdk.incubator.http.*; 38 import java.util.Optional; 39 import java.util.concurrent.*; 40 import java.util.function.*; 41 import java.util.Arrays; 42 import java.util.Iterator; 43 import org.testng.annotations.Test; 44 import static jdk.incubator.http.HttpClient.Version.HTTP_2; 45 import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString; 46 import static jdk.incubator.http.HttpResponse.BodyHandler.asString; 47 48 public class RedirectTest { 49 static int httpPort; 50 static Http2TestServer httpServer; 51 static HttpClient client; 52 53 static String httpURIString, altURIString1, altURIString2; 54 static URI httpURI, altURI1, altURI2; 55 56 static Supplier<String> sup(String... args) { 57 Iterator<String> i = Arrays.asList(args).iterator(); 58 // need to know when to stop calling it. 59 return () -> i.next(); 60 } 61 62 static class Redirector extends Http2RedirectHandler { 63 private InetSocketAddress remoteAddr; 64 private boolean error = false; 65 66 Redirector(Supplier<String> supplier) { 67 super(supplier); 68 } 69 70 protected synchronized void examineExchange(Http2TestExchange ex) { 71 InetSocketAddress addr = ex.getRemoteAddress(); 72 if (remoteAddr == null) { 73 remoteAddr = addr; 74 return; 75 } 76 // check that the client addr/port stays the same, proving 77 // that the connection didn't get dropped. 78 if (!remoteAddr.equals(addr)) { 79 System.err.printf("Error %s/%s\n", remoteAddr.toString(), 80 addr.toString()); 81 error = true; 82 } 83 } 84 85 public synchronized boolean error() { 86 return error; 87 } 88 } 89 90 static void initialize() throws Exception { 91 try { 92 client = getClient(); 93 httpServer = new Http2TestServer(false, 0, null, null); 94 httpPort = httpServer.getAddress().getPort(); 95 96 // urls are accessed in sequence below. The first two are on 97 // different servers. Third on same server as second. So, the 98 // client should use the same http connection. 99 httpURIString = "http://127.0.0.1:" + httpPort + "/foo/"; 100 httpURI = URI.create(httpURIString); 101 altURIString1 = "http://127.0.0.1:" + httpPort + "/redir"; 102 altURI1 = URI.create(altURIString1); 103 altURIString2 = "http://127.0.0.1:" + httpPort + "/redir_again"; 104 altURI2 = URI.create(altURIString2); 105 106 Redirector r = new Redirector(sup(altURIString1, altURIString2)); 107 httpServer.addHandler(r, "/foo"); 108 httpServer.addHandler(r, "/redir"); 109 httpServer.addHandler(new Http2EchoHandler(), "/redir_again"); 110 111 httpServer.start(); 112 } catch (Throwable e) { 113 System.err.println("Throwing now"); 114 e.printStackTrace(); 115 throw e; 116 } 117 } 118 119 @Test 120 public static void test() throws Exception { 121 try { 122 initialize(); 123 simpleTest(); 124 } finally { 125 httpServer.stop(); 126 } 127 } 128 129 static HttpClient getClient() { 130 if (client == null) { 131 client = HttpClient.newBuilder() 132 .followRedirects(HttpClient.Redirect.ALWAYS) 133 .version(HTTP_2) 134 .build(); 135 } 136 return client; 137 } 138 139 static URI getURI() { 140 return URI.create(httpURIString); 141 } 142 143 static void checkStatus(int expected, int found) throws Exception { 144 if (expected != found) { 145 System.err.printf ("Test failed: wrong status code %d/%d\n", 146 expected, found); 147 throw new RuntimeException("Test failed"); 148 } 149 } 150 151 static void checkURIs(URI expected, URI found) throws Exception { 152 System.out.printf ("Expected: %s, Found: %s\n", expected.toString(), found.toString()); 153 if (!expected.equals(found)) { 154 System.err.printf ("Test failed: wrong URI %s/%s\n", 155 expected.toString(), found.toString()); 156 throw new RuntimeException("Test failed"); 157 } 158 } 159 160 static void checkStrings(String expected, String found) throws Exception { 161 if (!expected.equals(found)) { 162 System.err.printf ("Test failed: wrong string %s/%s\n", 163 expected, found); 164 throw new RuntimeException("Test failed"); 165 } 166 } 167 168 static void check(boolean cond, Object... msg) { 169 if (cond) 170 return; 171 StringBuilder sb = new StringBuilder(); 172 for (Object o : msg) 173 sb.append(o); 174 throw new RuntimeException(sb.toString()); 175 } 176 177 static final String SIMPLE_STRING = "Hello world Goodbye world"; 178 179 static void simpleTest() throws Exception { 180 URI uri = getURI(); 181 System.err.println("Request to " + uri); 182 183 HttpClient client = getClient(); 184 HttpRequest req = HttpRequest.newBuilder(uri) 185 .POST(fromString(SIMPLE_STRING)) 186 .build(); 187 CompletableFuture<HttpResponse<String>> cf = client.sendAsync(req, asString()); 188 HttpResponse<String> response = cf.join(); 189 190 checkStatus(200, response.statusCode()); 191 String responseBody = response.body(); 192 checkStrings(SIMPLE_STRING, responseBody); 193 checkURIs(response.uri(), altURI2); 194 195 // check two previous responses 196 HttpResponse<String> prev = response.previousResponse() 197 .orElseThrow(() -> new RuntimeException("no previous response")); 198 checkURIs(prev.uri(), altURI1); 199 200 prev = prev.previousResponse() 201 .orElseThrow(() -> new RuntimeException("no previous response")); 202 checkURIs(prev.uri(), httpURI); 203 204 checkPreviousRedirectResponses(req, response); 205 206 System.err.println("DONE"); 207 } 208 209 static void checkPreviousRedirectResponses(HttpRequest initialRequest, 210 HttpResponse<?> finalResponse) { 211 // there must be at least one previous response 212 finalResponse.previousResponse() 213 .orElseThrow(() -> new RuntimeException("no previous response")); 214 215 HttpResponse<?> response = finalResponse; 216 do { 217 URI uri = response.uri(); 218 response = response.previousResponse().get(); 219 check(300 <= response.statusCode() && response.statusCode() <= 309, 220 "Expected 300 <= code <= 309, got:" + response.statusCode()); 221 check(response.body() == null, "Unexpected body: " + response.body()); 222 String locationHeader = response.headers().firstValue("Location") 223 .orElseThrow(() -> new RuntimeException("no previous Location")); 224 check(uri.toString().endsWith(locationHeader), 225 "URI: " + uri + ", Location: " + locationHeader); 226 } while (response.previousResponse().isPresent()); 227 228 // initial 229 check(initialRequest.equals(response.request()), 230 "Expected initial request [%s] to equal last prev req [%s]", 231 initialRequest, response.request()); 232 } 233 }