1 /* 2 * Copyright (c) 2015, 2019, 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 8087112 8178699 27 * @modules java.net.http 28 * java.logging 29 * jdk.httpserver 30 * @library /test/lib / 31 * @build jdk.test.lib.net.SimpleSSLContext ProxyServer 32 * @compile ../../../com/sun/net/httpserver/LogFilter.java 33 * @compile ../../../com/sun/net/httpserver/FileServerHandler.java 34 * @run main/othervm 35 * -Djdk.internal.httpclient.debug=true 36 * -Djdk.httpclient.HttpClient.log=errors,ssl,trace 37 * SmokeTest 38 */ 39 40 import com.sun.net.httpserver.Headers; 41 import com.sun.net.httpserver.HttpContext; 42 import com.sun.net.httpserver.HttpExchange; 43 import com.sun.net.httpserver.HttpHandler; 44 import com.sun.net.httpserver.HttpServer; 45 import com.sun.net.httpserver.HttpsConfigurator; 46 import com.sun.net.httpserver.HttpsParameters; 47 import com.sun.net.httpserver.HttpsServer; 48 49 import java.net.InetAddress; 50 import java.net.Proxy; 51 import java.net.SocketAddress; 52 import java.net.http.HttpHeaders; 53 import java.nio.charset.StandardCharsets; 54 import java.util.Collections; 55 import java.util.Optional; 56 import java.util.Set; 57 import java.util.concurrent.atomic.AtomicInteger; 58 import java.net.InetSocketAddress; 59 import java.net.PasswordAuthentication; 60 import java.net.ProxySelector; 61 import java.net.URI; 62 import java.net.http.HttpClient; 63 import java.net.http.HttpRequest; 64 import java.net.http.HttpRequest.BodyPublishers; 65 import java.net.http.HttpResponse; 66 import java.net.http.HttpResponse.BodyHandlers; 67 import java.io.File; 68 import java.io.FileInputStream; 69 import java.io.FileOutputStream; 70 import java.io.FileNotFoundException; 71 import java.io.IOException; 72 import java.io.BufferedInputStream; 73 import java.io.InputStream; 74 import java.io.OutputStream; 75 import java.io.UncheckedIOException; 76 import java.util.concurrent.BlockingQueue; 77 import java.util.concurrent.CompletableFuture; 78 import java.util.concurrent.CompletionException; 79 import java.util.concurrent.CyclicBarrier; 80 import java.util.concurrent.Executors; 81 import java.util.concurrent.ExecutorService; 82 import java.util.concurrent.LinkedBlockingQueue; 83 import java.util.concurrent.TimeUnit; 84 import javax.net.ssl.SSLContext; 85 import javax.net.ssl.SSLParameters; 86 import java.nio.file.Files; 87 import java.nio.file.Path; 88 import java.nio.file.Paths; 89 import java.util.HashSet; 90 import java.util.LinkedList; 91 import java.util.List; 92 import java.util.Random; 93 import jdk.test.lib.net.SimpleSSLContext; 94 import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; 95 import static java.nio.file.StandardOpenOption.WRITE; 96 97 import java.util.concurrent.CountDownLatch; 98 import java.util.logging.ConsoleHandler; 99 import java.util.logging.Level; 100 import java.util.logging.Logger; 101 102 /** 103 * * Basic smoke test for Http/1.1 client 104 * - basic request response 105 * - request body POST 106 * - response body GET 107 * - redirect 108 * - chunked request/response 109 * - SSL 110 * - proxies 111 * - 100 continue 112 * - check keep alive appears to be working 113 * - cancel of long request 114 * 115 * Uses a FileServerHandler serving a couple of known files 116 * in docs directory. 117 */ 118 public class SmokeTest { 119 static SSLContext ctx; 120 static SSLParameters sslparams; 121 static HttpServer s1 ; 122 static HttpsServer s2; 123 static ExecutorService executor; 124 static int port; 125 static int httpsport; 126 static String httproot; 127 static String httpsroot; 128 static HttpClient client; 129 static ProxyServer proxy; 130 static int proxyPort; 131 static RedirectErrorHandler redirectErrorHandler, redirectErrorHandlerSecure; 132 static RedirectHandler redirectHandler, redirectHandlerSecure; 133 static DelayHandler delayHandler; 134 final static String midSizedFilename = "/files/notsobigfile.txt"; 135 final static String smallFilename = "/files/smallfile.txt"; 136 static Path midSizedFile; 137 static Path smallFile; 138 static String fileroot; 139 140 static class HttpEchoHandler implements HttpHandler { 141 142 @Override handle(HttpExchange exchange)143 public void handle(HttpExchange exchange) throws IOException { 144 try (InputStream is = exchange.getRequestBody(); 145 OutputStream os = exchange.getResponseBody()) { 146 byte[] bytes = is.readAllBytes(); 147 long responseLength = bytes.length == 0 ? -1 : bytes.length; 148 boolean fixedLength = "yes".equals(exchange.getRequestHeaders() 149 .getFirst("XFixed")); 150 exchange.sendResponseHeaders(200, fixedLength ? responseLength : 0); 151 os.write(bytes); 152 } 153 } 154 } 155 getFileContent(String path)156 static String getFileContent(String path) throws IOException { 157 FileInputStream fis = new FileInputStream(path); 158 byte[] buf = new byte[2000]; 159 StringBuilder sb = new StringBuilder(); 160 int n; 161 while ((n=fis.read(buf)) != -1) { 162 sb.append(new String(buf, 0, n, "US-ASCII")); 163 } 164 fis.close(); 165 return sb.toString(); 166 } 167 cmpFileContent(Path path1, Path path2)168 static void cmpFileContent(Path path1, Path path2) throws IOException { 169 InputStream fis1 = new BufferedInputStream(new FileInputStream(path1.toFile())); 170 InputStream fis2 = new BufferedInputStream(new FileInputStream(path2.toFile())); 171 172 int n1, n2; 173 while ((n1=fis1.read()) != -1) { 174 n2 = fis2.read(); 175 if (n1 != n2) 176 throw new IOException("Content not the same"); 177 } 178 fis1.close(); 179 fis2.close(); 180 } 181 main(String[] args)182 public static void main(String[] args) throws Exception { 183 initServer(); 184 fileroot = System.getProperty ("test.src", ".")+ "/docs"; 185 midSizedFile = Paths.get(fileroot + midSizedFilename); 186 smallFile = Paths.get(fileroot + smallFilename); 187 ExecutorService e = Executors.newCachedThreadPool(); 188 System.out.println(e); 189 client = HttpClient.newBuilder() 190 .sslContext(ctx) 191 .executor(e) 192 .version(HttpClient.Version.HTTP_1_1) 193 .sslParameters(sslparams) 194 .followRedirects(HttpClient.Redirect.ALWAYS) 195 .build(); 196 197 try { 198 test1(httproot + "files/foo.txt", true); 199 test1(httproot + "files/foo.txt", false); 200 test1(httpsroot + "files/foo.txt", true); 201 test1(httpsroot + "files/foo.txt", false); 202 203 test2(httproot + "echo/foo", "This is a short test"); 204 test2(httpsroot + "echo/foo", "This is a short test"); 205 206 test2a(httproot + "echo/foo"); 207 test2a(httpsroot + "echo/foo"); 208 209 test3(httproot + "redirect/foo.txt"); 210 test3(httpsroot + "redirect/foo.txt"); 211 212 test4(httproot + "files/foo.txt"); 213 214 test4(httpsroot + "files/foo.txt"); 215 216 test5(httproot + "echo/foo", true); 217 218 test5(httpsroot + "echo/foo", true); 219 test5(httproot + "echo/foo", false); 220 221 test5(httpsroot + "echo/foo", false); 222 223 test6(httproot + "echo/foo", true); 224 test6(httpsroot + "echo/foo", true); 225 test6(httproot + "echo/foo", false); 226 test6(httpsroot + "echo/foo", false); 227 228 test7(httproot + "keepalive/foo"); 229 /* 230 test10(httproot + "redirecterror/foo.txt"); 231 232 test10(httpsroot + "redirecterror/foo.txt"); 233 234 test11(httproot + "echo/foo"); 235 test11(httpsroot + "echo/foo"); 236 */ 237 //test12(httproot + "delay/foo", delayHandler); 238 239 } finally { 240 s1.stop(0); 241 s2.stop(0); 242 proxy.close(); 243 e.shutdownNow(); 244 executor.shutdownNow(); 245 } 246 } 247 248 static class Auth extends java.net.Authenticator { 249 volatile int count = 0; 250 @Override getPasswordAuthentication()251 protected PasswordAuthentication getPasswordAuthentication() { 252 if (count++ == 0) { 253 return new PasswordAuthentication("user", "passwd".toCharArray()); 254 } else { 255 return new PasswordAuthentication("user", "goober".toCharArray()); 256 } 257 } count()258 int count() { 259 return count; 260 } 261 } 262 263 // Basic test test1(String target, boolean fixedLen)264 static void test1(String target, boolean fixedLen) throws Exception { 265 System.out.print("test1: " + target); 266 URI uri = new URI(target); 267 268 HttpRequest.Builder builder = HttpRequest.newBuilder().uri(uri).GET(); 269 270 if (fixedLen) { 271 builder.header("XFixed", "yes"); 272 } 273 274 HttpRequest request = builder.build(); 275 276 HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); 277 278 checkResponseContentLength(response.headers(), fixedLen); 279 280 String body = response.body(); 281 if (!body.equals("This is foo.txt\r\n")) { 282 throw new RuntimeException("Did not get expected body: " 283 + "\n\t expected \"This is foo.txt\\r\\n\"" 284 + "\n\t received \"" 285 + body.replace("\r", "\\r").replace("\n","\\n") + "\""); 286 } 287 288 // repeat async 289 HttpResponse<String> response1 = client.sendAsync(request, BodyHandlers.ofString()) 290 .join(); 291 292 String body1 = response1.body(); 293 if (!body1.equals("This is foo.txt\r\n")) { 294 throw new RuntimeException(); 295 } 296 System.out.println(" OK"); 297 } 298 299 // POST use echo to check reply test2(String s, String body)300 static void test2(String s, String body) throws Exception { 301 System.out.print("test2: " + s); 302 URI uri = new URI(s); 303 304 HttpRequest request = HttpRequest.newBuilder(uri) 305 .POST(BodyPublishers.ofString(body)) 306 .build(); 307 308 HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); 309 310 if (response.statusCode() != 200) { 311 throw new RuntimeException( 312 "Expected 200, got [ " + response.statusCode() + " ]"); 313 } 314 String reply = response.body(); 315 if (!reply.equals(body)) { 316 throw new RuntimeException( 317 "Body mismatch: expected [" + body + "], got [" + reply + "]"); 318 } 319 System.out.println(" OK"); 320 } 321 322 // POST use echo to check reply test2a(String s)323 static void test2a(String s) throws Exception { 324 System.out.print("test2a: " + s); 325 URI uri = new URI(s); 326 Path p = getTempFile(128 * 1024); 327 328 HttpRequest request = HttpRequest.newBuilder(uri) 329 .POST(BodyPublishers.ofFile(p)) 330 .build(); 331 332 Path resp = getTempFile(1); // will be overwritten 333 334 HttpResponse<Path> response = client.send(request, 335 BodyHandlers.ofFile(resp, TRUNCATE_EXISTING, WRITE)); 336 337 if (response.statusCode() != 200) { 338 throw new RuntimeException( 339 "Expected 200, got [ " + response.statusCode() + " ]"); 340 } 341 // no redirection, etc, should be no previous response 342 if (response.previousResponse().isPresent()) { 343 throw new RuntimeException( 344 "Unexpected previous response: " + response.previousResponse().get()); 345 } 346 Path reply = response.body(); 347 //System.out.println("Reply stored in " + reply.toString()); 348 cmpFileContent(reply, p); 349 System.out.println(" OK"); 350 } 351 352 // Redirect test3(String s)353 static void test3(String s) throws Exception { 354 System.out.print("test3: " + s); 355 URI uri = new URI(s); 356 RedirectHandler handler = uri.getScheme().equals("https") 357 ? redirectHandlerSecure : redirectHandler; 358 359 HttpRequest request = HttpRequest.newBuilder() 360 .uri(uri) 361 .GET() 362 .build(); 363 364 HttpResponse<Path> response = client.send(request, 365 BodyHandlers.ofFile(Paths.get("redir1.txt"))); 366 367 if (response.statusCode() != 200) { 368 throw new RuntimeException( 369 "Expected 200, got [ " + response.statusCode() + " ]"); 370 } else { 371 response.body(); 372 } 373 374 Path downloaded = Paths.get("redir1.txt"); 375 if (Files.size(downloaded) != Files.size(midSizedFile)) { 376 throw new RuntimeException("Size mismatch"); 377 } 378 checkPreviousRedirectResponses(request, response); 379 System.out.printf(" (count: %d) ", handler.count()); 380 // repeat with async api 381 382 handler.reset(); 383 384 request = HttpRequest.newBuilder(uri).build(); 385 386 response = client.sendAsync(request, 387 BodyHandlers.ofFile(Paths.get("redir2.txt"))).join(); 388 389 if (response.statusCode() != 200) { 390 throw new RuntimeException( 391 "Expected 200, got [ " + response.statusCode() + " ]"); 392 } else { 393 response.body(); 394 } 395 396 downloaded = Paths.get("redir2.txt"); 397 if (Files.size(downloaded) != Files.size(midSizedFile)) { 398 throw new RuntimeException("Size mismatch 2"); 399 } 400 401 checkPreviousRedirectResponses(request, response); 402 System.out.printf(" (count: %d) ", handler.count()); 403 System.out.println(" OK"); 404 } 405 checkPreviousRedirectResponses(HttpRequest initialRequest, HttpResponse<?> finalResponse)406 static void checkPreviousRedirectResponses(HttpRequest initialRequest, 407 HttpResponse<?> finalResponse) { 408 // there must be at least one previous response 409 finalResponse.previousResponse() 410 .orElseThrow(() -> new RuntimeException("no previous response")); 411 412 HttpResponse<?> response = finalResponse; 413 do { 414 URI uri = response.uri(); 415 response = response.previousResponse().get(); 416 check(300 <= response.statusCode() && response.statusCode() <= 309, 417 "Expected 300 <= code <= 309, got:" + response.statusCode()); 418 check(response.body() == null, "Unexpected body: " + response.body()); 419 String locationHeader = response.headers().firstValue("Location") 420 .orElseThrow(() -> new RuntimeException("no previous Location")); 421 check(uri.toString().endsWith(locationHeader), 422 "URI: " + uri + ", Location: " + locationHeader); 423 } while (response.previousResponse().isPresent()); 424 425 // initial 426 check(initialRequest.equals(response.request()), 427 "Expected initial request [%s] to equal last prev req [%s]", 428 initialRequest, response.request()); 429 } 430 check(boolean cond, Object... msg)431 static void check(boolean cond, Object... msg) { 432 if (cond) 433 return; 434 StringBuilder sb = new StringBuilder(); 435 for (Object o : msg) 436 sb.append(o); 437 throw new RuntimeException(sb.toString()); 438 } 439 440 /** 441 * A Proxy Selector that wraps a ProxySelector.of(), and counts the number 442 * of times its select method has been invoked. This can be used to ensure 443 * that the Proxy Selector is invoked only once per HttpClient.sendXXX 444 * invocation. 445 */ 446 static class CountingProxySelector extends ProxySelector { 447 private final ProxySelector proxySelector; 448 private volatile int count; // 0 CountingProxySelector(InetSocketAddress proxyAddress)449 private CountingProxySelector(InetSocketAddress proxyAddress) { 450 proxySelector = ProxySelector.of(proxyAddress); 451 } 452 of(InetSocketAddress proxyAddress)453 public static CountingProxySelector of(InetSocketAddress proxyAddress) { 454 return new CountingProxySelector(proxyAddress); 455 } 456 count()457 int count() { return count; } 458 459 @Override select(URI uri)460 public List<Proxy> select(URI uri) { 461 count++; 462 return proxySelector.select(uri); 463 } 464 465 @Override connectFailed(URI uri, SocketAddress sa, IOException ioe)466 public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { 467 proxySelector.connectFailed(uri, sa, ioe); 468 } 469 } 470 471 // Proxies test4(String s)472 static void test4(String s) throws Exception { 473 System.out.print("test4: " + s); 474 URI uri = new URI(s); 475 InetSocketAddress proxyAddr = new InetSocketAddress(InetAddress.getLoopbackAddress(), 476 proxyPort); 477 String filename = fileroot + uri.getPath(); 478 479 ExecutorService e = Executors.newCachedThreadPool(); 480 481 CountingProxySelector ps = CountingProxySelector.of(proxyAddr); 482 HttpClient cl = HttpClient.newBuilder() 483 .executor(e) 484 .proxy(ps) 485 .sslContext(ctx) 486 .sslParameters(sslparams) 487 .build(); 488 489 HttpRequest request = HttpRequest.newBuilder(uri).GET().build(); 490 491 CompletableFuture<String> fut = cl.sendAsync(request, BodyHandlers.ofString()) 492 .thenApply((response) -> response.body()); 493 494 String body = fut.get(5, TimeUnit.HOURS); 495 496 String fc = getFileContent(filename); 497 498 if (!body.equals(fc)) { 499 throw new RuntimeException( 500 "Body mismatch: expected [" + body + "], got [" + fc + "]"); 501 } 502 if (ps.count() != 1) { 503 throw new RuntimeException("CountingProxySelector. Expected 1, got " + ps.count()); 504 } 505 e.shutdownNow(); 506 System.out.println(" OK"); 507 } 508 509 // 100 Continue: use echo target test5(String target, boolean fixedLen)510 static void test5(String target, boolean fixedLen) throws Exception { 511 System.out.print("test5: " + target); 512 URI uri = new URI(target); 513 String requestBody = generateString(12 * 1024 + 13); 514 515 HttpRequest.Builder builder = HttpRequest.newBuilder(uri) 516 .expectContinue(true) 517 .POST(BodyPublishers.ofString(requestBody)); 518 519 if (fixedLen) { 520 builder.header("XFixed", "yes"); 521 } 522 523 HttpRequest request = builder.build(); 524 525 HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); 526 527 checkResponseContentLength(response.headers(), fixedLen); 528 529 String body = response.body(); 530 531 if (!body.equals(requestBody)) { 532 throw new RuntimeException( 533 "Body mismatch: expected [" + body + "], got [" + body + "]"); 534 } 535 System.out.println(" OK"); 536 } 537 538 // use echo test6(String target, boolean fixedLen)539 static void test6(String target, boolean fixedLen) throws Exception { 540 System.out.print("test6: " + target); 541 URI uri = new URI(target); 542 String requestBody = generateString(12 * 1024 + 3); 543 544 HttpRequest.Builder builder = HttpRequest.newBuilder(uri).GET(); 545 546 if (fixedLen) { 547 builder.header("XFixed", "yes"); 548 } 549 550 HttpRequest request = builder.build(); 551 552 HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); 553 554 checkResponseContentLength(response.headers(), fixedLen); 555 556 if (response.statusCode() != 200) { 557 throw new RuntimeException( 558 "Expected 200, got [ " + response.statusCode() + " ]"); 559 } 560 561 String responseBody = response.body(); 562 563 if (responseBody.equals(requestBody)) { 564 throw new RuntimeException( 565 "Body mismatch: expected [" + requestBody + "], got [" + responseBody + "]"); 566 } 567 System.out.println(" OK"); 568 } 569 570 @SuppressWarnings("rawtypes") test7(String target)571 static void test7(String target) throws Exception { 572 System.out.print("test7: " + target); 573 Path requestBody = getTempFile(128 * 1024); 574 // First test 575 URI uri = new URI(target); 576 HttpRequest request = HttpRequest.newBuilder().uri(uri).GET().build(); 577 578 for (int i=0; i<4; i++) { 579 HttpResponse<String> r = client.send(request, BodyHandlers.ofString()); 580 String body = r.body(); 581 if (!body.equals("OK")) { 582 throw new RuntimeException("Expected OK, got: " + body); 583 } 584 } 585 586 // Second test: 4 x parallel 587 request = HttpRequest.newBuilder() 588 .uri(uri) 589 .POST(BodyPublishers.ofFile(requestBody)) 590 .build(); 591 List<CompletableFuture<String>> futures = new LinkedList<>(); 592 for (int i=0; i<4; i++) { 593 futures.add(client.sendAsync(request, BodyHandlers.ofString()) 594 .thenApply((response) -> { 595 if (response.statusCode() == 200) 596 return response.body(); 597 else 598 return "ERROR"; 599 })); 600 } 601 // all sent? 602 CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) 603 .join(); 604 605 for (CompletableFuture<String> future : futures) { 606 String body = future.get(); 607 if (!body.equals("OK")) { 608 throw new RuntimeException("Expected OK, got: " + body); 609 } 610 } 611 612 // Third test: Multiple of 4 parallel requests 613 request = HttpRequest.newBuilder(uri).GET().build(); 614 BlockingQueue<String> q = new LinkedBlockingQueue<>(); 615 for (int i=0; i<4; i++) { 616 client.sendAsync(request, BodyHandlers.ofString()) 617 .thenApply((HttpResponse<String> resp) -> { 618 String body = resp.body(); 619 putQ(q, body); 620 return body; 621 }); 622 } 623 // we've sent four requests. Now, just send another request 624 // as each response is received. The idea is to ensure that 625 // only four sockets ever get used. 626 627 for (int i=0; i<100; i++) { 628 // block until response received 629 String body = takeQ(q); 630 if (!body.equals("OK")) { 631 throw new RuntimeException(body); 632 } 633 client.sendAsync(request, BodyHandlers.ofString()) 634 .thenApply((resp) -> { 635 if (resp.statusCode() == 200) 636 putQ(q, resp.body()); 637 else 638 putQ(q, "ERROR"); 639 return null; 640 }); 641 } 642 // should be four left 643 for (int i=0; i<4; i++) { 644 takeQ(q); 645 } 646 System.out.println(" OK"); 647 } 648 takeQ(BlockingQueue<String> q)649 static String takeQ(BlockingQueue<String> q) { 650 String r = null; 651 try { 652 r = q.take(); 653 } catch (InterruptedException e) {} 654 655 return r; 656 } 657 putQ(BlockingQueue<String> q, String o)658 static void putQ(BlockingQueue<String> q, String o) { 659 try { 660 q.put(o); 661 } catch (InterruptedException e) { 662 // can't happen 663 } 664 } 665 newStream()666 static FileInputStream newStream() { 667 try { 668 return new FileInputStream(smallFile.toFile()); 669 } catch (FileNotFoundException e) { 670 throw new UncheckedIOException(e); 671 } 672 } 673 // Chunked output stream test11(String target)674 static void test11(String target) throws Exception { 675 System.out.print("test11: " + target); 676 URI uri = new URI(target); 677 678 HttpRequest request = HttpRequest.newBuilder(uri) 679 .POST(BodyPublishers.ofInputStream(SmokeTest::newStream)) 680 .build(); 681 682 Path download = Paths.get("test11.txt"); 683 684 HttpResponse<Path> response = client.send(request, BodyHandlers.ofFile(download)); 685 686 if (response.statusCode() != 200) { 687 throw new RuntimeException("Wrong response code"); 688 } 689 690 download.toFile().delete(); 691 response.body(); 692 693 if (Files.size(download) != Files.size(smallFile)) { 694 System.out.println("Original size: " + Files.size(smallFile)); 695 System.out.println("Downloaded size: " + Files.size(download)); 696 throw new RuntimeException("Size mismatch"); 697 } 698 System.out.println(" OK"); 699 } 700 delay(int seconds)701 static void delay(int seconds) { 702 try { 703 Thread.sleep(seconds * 1000); 704 } catch (InterruptedException e) { 705 } 706 } 707 708 // Redirect loop: return an error after a certain number of redirects test10(String s)709 static void test10(String s) throws Exception { 710 System.out.print("test10: " + s); 711 URI uri = new URI(s); 712 RedirectErrorHandler handler = uri.getScheme().equals("https") 713 ? redirectErrorHandlerSecure : redirectErrorHandler; 714 715 HttpRequest request = HttpRequest.newBuilder(uri).GET().build(); 716 CompletableFuture<HttpResponse<String>> cf = 717 client.sendAsync(request, BodyHandlers.ofString()); 718 719 try { 720 HttpResponse<String> response = cf.join(); 721 throw new RuntimeException("Expected Completion Exception"); 722 } catch (CompletionException e) { 723 //System.out.println(e); 724 } 725 726 System.out.printf(" (Calls %d) ", handler.count()); 727 System.out.println(" OK"); 728 } 729 730 static final int NUM = 50; 731 732 static Random random = new Random(); 733 static final String alphabet = "ABCDEFGHIJKLMNOPQRST"; 734 randomChar()735 static char randomChar() { 736 return alphabet.charAt(random.nextInt(alphabet.length())); 737 } 738 generateString(int length)739 static String generateString(int length) { 740 StringBuilder sb = new StringBuilder(length); 741 for (int i=0; i<length; i++) { 742 sb.append(randomChar()); 743 } 744 return sb.toString(); 745 } 746 initServer()747 static void initServer() throws Exception { 748 749 Logger logger = Logger.getLogger("com.sun.net.httpserver"); 750 ConsoleHandler ch = new ConsoleHandler(); 751 logger.setLevel(Level.SEVERE); 752 ch.setLevel(Level.SEVERE); 753 logger.addHandler(ch); 754 755 String root = System.getProperty ("test.src", ".")+ "/docs"; 756 InetSocketAddress addr = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); 757 s1 = HttpServer.create (addr, 0); 758 if (s1 instanceof HttpsServer) { 759 throw new RuntimeException ("should not be httpsserver"); 760 } 761 s2 = HttpsServer.create (addr, 0); 762 HttpHandler h = new FileServerHandler(root); 763 764 HttpContext c1 = s1.createContext("/files", h); 765 HttpContext c2 = s2.createContext("/files", h); 766 HttpContext c3 = s1.createContext("/echo", new HttpEchoHandler()); 767 redirectHandler = new RedirectHandler("/redirect"); 768 redirectHandlerSecure = new RedirectHandler("/redirect"); 769 HttpContext c4 = s1.createContext("/redirect", redirectHandler); 770 HttpContext c41 = s2.createContext("/redirect", redirectHandlerSecure); 771 HttpContext c5 = s2.createContext("/echo", new HttpEchoHandler()); 772 HttpContext c6 = s1.createContext("/keepalive", new KeepAliveHandler()); 773 redirectErrorHandler = new RedirectErrorHandler("/redirecterror"); 774 redirectErrorHandlerSecure = new RedirectErrorHandler("/redirecterror"); 775 HttpContext c7 = s1.createContext("/redirecterror", redirectErrorHandler); 776 HttpContext c71 = s2.createContext("/redirecterror", redirectErrorHandlerSecure); 777 delayHandler = new DelayHandler(); 778 HttpContext c8 = s1.createContext("/delay", delayHandler); 779 HttpContext c81 = s2.createContext("/delay", delayHandler); 780 781 executor = Executors.newCachedThreadPool(); 782 s1.setExecutor(executor); 783 s2.setExecutor(executor); 784 ctx = new SimpleSSLContext().get(); 785 sslparams = ctx.getDefaultSSLParameters(); 786 //sslparams.setProtocols(new String[]{"TLSv1.2"}); 787 s2.setHttpsConfigurator(new Configurator(ctx)); 788 s1.start(); 789 s2.start(); 790 791 port = s1.getAddress().getPort(); 792 System.out.println("HTTP server port = " + port); 793 httpsport = s2.getAddress().getPort(); 794 System.out.println("HTTPS server port = " + httpsport); 795 httproot = "http://localhost:" + port + "/"; 796 httpsroot = "https://localhost:" + httpsport + "/"; 797 798 proxy = new ProxyServer(0, false); 799 proxyPort = proxy.getPort(); 800 System.out.println("Proxy port = " + proxyPort); 801 } 802 checkResponseContentLength(HttpHeaders responseHeaders, boolean fixedLen)803 static void checkResponseContentLength(HttpHeaders responseHeaders, boolean fixedLen) { 804 Optional<String> transferEncoding = responseHeaders.firstValue("transfer-encoding"); 805 Optional<String> contentLength = responseHeaders.firstValue("content-length"); 806 if (fixedLen) { 807 assert contentLength.isPresent(); 808 assert !transferEncoding.isPresent(); 809 } else { 810 assert !contentLength.isPresent(); 811 assert transferEncoding.isPresent(); 812 assert "chunked".equals(transferEncoding.get()); 813 } 814 } 815 816 static class RedirectHandler implements HttpHandler { 817 private final String root; 818 private volatile int count = 0; 819 RedirectHandler(String root)820 RedirectHandler(String root) { 821 this.root = root; 822 } 823 824 @Override handle(HttpExchange t)825 public synchronized void handle(HttpExchange t) throws IOException { 826 try (InputStream is = t.getRequestBody()) { 827 is.readAllBytes(); 828 } 829 830 Headers responseHeaders = t.getResponseHeaders(); 831 832 if (count++ < 1) { 833 responseHeaders.add("Location", root + "/foo/" + count); 834 } else { 835 responseHeaders.add("Location", SmokeTest.midSizedFilename); 836 } 837 t.sendResponseHeaders(301, 64 * 1024); 838 byte[] bb = new byte[1024]; 839 OutputStream os = t.getResponseBody(); 840 for (int i=0; i<64; i++) { 841 os.write(bb); 842 } 843 os.close(); 844 t.close(); 845 } 846 count()847 int count() { 848 return count; 849 } 850 reset()851 void reset() { 852 count = 0; 853 } 854 } 855 856 static class RedirectErrorHandler implements HttpHandler { 857 private final String root; 858 private volatile int count = 1; 859 RedirectErrorHandler(String root)860 RedirectErrorHandler(String root) { 861 this.root = root; 862 } 863 count()864 synchronized int count() { 865 return count; 866 } 867 increment()868 synchronized void increment() { 869 count++; 870 } 871 872 @Override handle(HttpExchange t)873 public synchronized void handle(HttpExchange t) throws IOException { 874 try (InputStream is = t.getRequestBody()) { 875 is.readAllBytes(); 876 } 877 878 Headers map = t.getResponseHeaders(); 879 String redirect = root + "/foo/" + Integer.toString(count); 880 increment(); 881 map.add("Location", redirect); 882 t.sendResponseHeaders(301, -1); 883 t.close(); 884 } 885 } 886 887 static class DelayHandler implements HttpHandler { 888 889 CyclicBarrier bar1 = new CyclicBarrier(2); 890 CyclicBarrier bar2 = new CyclicBarrier(2); 891 CyclicBarrier bar3 = new CyclicBarrier(2); 892 barrier1()893 CyclicBarrier barrier1() { 894 return bar1; 895 } 896 barrier2()897 CyclicBarrier barrier2() { 898 return bar2; 899 } 900 901 @Override handle(HttpExchange he)902 public synchronized void handle(HttpExchange he) throws IOException { 903 he.getRequestBody().readAllBytes(); 904 try { 905 bar1.await(); 906 bar2.await(); 907 } catch (Exception e) { } 908 he.sendResponseHeaders(200, -1); // will probably fail 909 he.close(); 910 } 911 } 912 913 static class Configurator extends HttpsConfigurator { Configurator(SSLContext ctx)914 public Configurator(SSLContext ctx) { 915 super(ctx); 916 } 917 configure(HttpsParameters params)918 public void configure (HttpsParameters params) { 919 SSLParameters p = getSSLContext().getDefaultSSLParameters(); 920 //p.setProtocols(new String[]{"TLSv1.2"}); 921 params.setSSLParameters (p); 922 } 923 } 924 925 static final Path CWD = Paths.get("."); 926 getTempFile(int size)927 static Path getTempFile(int size) throws IOException { 928 File f = Files.createTempFile(CWD, "test", "txt").toFile(); 929 f.deleteOnExit(); 930 byte[] buf = new byte[2048]; 931 for (int i = 0; i < buf.length; i++) 932 buf[i] = (byte) i; 933 934 FileOutputStream fos = new FileOutputStream(f); 935 while (size > 0) { 936 int amount = Math.min(size, buf.length); 937 fos.write(buf, 0, amount); 938 size -= amount; 939 } 940 fos.close(); 941 return f.toPath(); 942 } 943 } 944 945 // check for simple hardcoded sequence and use remote address 946 // to check. 947 // First 4 requests executed in sequence (should use same connection/address) 948 // Next 4 requests parallel (should use different addresses) 949 // Then send 4 requests in parallel x 100 times (same four addresses used all time) 950 951 class KeepAliveHandler implements HttpHandler { 952 final AtomicInteger counter = new AtomicInteger(0); 953 final AtomicInteger nparallel = new AtomicInteger(0); 954 955 final Set<Integer> portSet = Collections.synchronizedSet(new HashSet<>()); 956 957 final int[] ports = new int[8]; 958 sleep(int n)959 void sleep(int n) { 960 try { 961 Thread.sleep(n); 962 } catch (InterruptedException e) {} 963 } 964 setPort(int index, int value)965 synchronized void setPort(int index, int value) { 966 ports[index] = value; 967 } 968 getPort(int index)969 synchronized int getPort(int index) { 970 return ports[index]; 971 } 972 getPorts(int[] dest, int from)973 synchronized void getPorts(int[] dest, int from) { 974 dest[0] = ports[from+0]; 975 dest[1] = ports[from+1]; 976 dest[2] = ports[from+2]; 977 dest[3] = ports[from+3]; 978 } 979 980 static final CountDownLatch latch = new CountDownLatch(4); 981 static final CountDownLatch latch7 = new CountDownLatch(4); 982 static final CountDownLatch latch8 = new CountDownLatch(1); 983 984 @Override handle(HttpExchange t)985 public void handle (HttpExchange t) 986 throws IOException 987 { 988 int np = nparallel.incrementAndGet(); 989 int remotePort = t.getRemoteAddress().getPort(); 990 String result = "OK"; 991 int[] lports = new int[4]; 992 993 int n = counter.getAndIncrement(); 994 995 /// First test 996 if (n < 4) { 997 setPort(n, remotePort); 998 } 999 if (n == 3) { 1000 getPorts(lports, 0); 1001 // check all values in ports[] are the same 1002 if (lports[0] != lports[1] || lports[2] != lports[3] 1003 || lports[0] != lports[2]) { 1004 result = "Error " + Integer.toString(n); 1005 System.out.println(result); 1006 } 1007 } 1008 // Second test 1009 if (n >=4 && n < 8) { 1010 // delay so that this connection doesn't get reused 1011 // before all 4 requests sent 1012 setPort(n, remotePort); 1013 latch.countDown(); 1014 try {latch.await();} catch (InterruptedException e) {} 1015 latch7.countDown(); 1016 } 1017 if (n == 7) { 1018 // wait until all n <= 7 have called setPort(...) 1019 try {latch7.await();} catch (InterruptedException e) {} 1020 getPorts(lports, 4); 1021 // should be all different 1022 if (lports[0] == lports[1] || lports[2] == lports[3] 1023 || lports[0] == lports[2]) { 1024 result = "Error " + Integer.toString(n); 1025 System.out.println(result); 1026 } 1027 // setup for third test 1028 for (int i=0; i<4; i++) { 1029 portSet.add(lports[i]); 1030 } 1031 System.out.printf("Ports: %d, %d, %d, %d\n", lports[0], lports[1], lports[2], lports[3]); 1032 latch8.countDown(); 1033 } 1034 // Third test 1035 if (n > 7) { 1036 // wait until all n == 7 has updated portSet 1037 try {latch8.await();} catch (InterruptedException e) {} 1038 if (np > 4) { 1039 System.err.println("XXX np = " + np); 1040 } 1041 // just check that port is one of the ones in portSet 1042 if (!portSet.contains(remotePort)) { 1043 System.out.println ("UNEXPECTED REMOTE PORT " 1044 + remotePort + " not in " + portSet); 1045 result = "Error " + Integer.toString(n); 1046 System.out.println(result); 1047 } 1048 } 1049 1050 try (InputStream is = t.getRequestBody()) { 1051 is.readAllBytes(); 1052 } 1053 t.sendResponseHeaders(200, result.length()); 1054 OutputStream o = t.getResponseBody(); 1055 o.write(result.getBytes(StandardCharsets.UTF_8)); 1056 t.close(); 1057 nparallel.getAndDecrement(); 1058 } 1059 } 1060