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