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