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
  27  * @library /lib/testlibrary server
  28  * @build jdk.testlibrary.SimpleSSLContext
  29  * @modules java.base/sun.net.www.http
  30  *          jdk.incubator.httpclient/jdk.incubator.http.internal.common
  31  *          jdk.incubator.httpclient/jdk.incubator.http.internal.frame
  32  *          jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
  33  * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors BasicTest
  34  */
  35 
  36 import java.io.IOException;
  37 import java.net.*;
  38 import jdk.incubator.http.*;
  39 import static jdk.incubator.http.HttpClient.Version.HTTP_2;
  40 import javax.net.ssl.*;
  41 import java.nio.file.*;
  42 import java.util.concurrent.*;
  43 import java.util.concurrent.atomic.AtomicReference;
  44 import java.util.Collections;
  45 import java.util.LinkedList;
  46 import java.util.List;
  47 import jdk.testlibrary.SimpleSSLContext;
  48 import static jdk.incubator.http.HttpRequest.BodyPublisher.fromFile;
  49 import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
  50 import static jdk.incubator.http.HttpResponse.BodyHandler.asFile;
  51 import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
  52 
  53 import org.testng.annotations.Test;
  54 
  55 @Test
  56 public class BasicTest {
  57     static int httpPort, httpsPort;
  58     static Http2TestServer httpServer, httpsServer;
  59     static HttpClient client = null;
  60     static ExecutorService clientExec;
  61     static ExecutorService serverExec;
  62     static SSLContext sslContext;
  63 
  64     static String pingURIString, httpURIString, httpsURIString;
  65 
  66     static void initialize() throws Exception {
  67         try {
  68             SimpleSSLContext sslct = new SimpleSSLContext();
  69             sslContext = sslct.get();
  70             client = getClient();
  71             httpServer = new Http2TestServer(false, 0, serverExec, sslContext);
  72             httpServer.addHandler(new Http2EchoHandler(), "/");
  73             httpServer.addHandler(new EchoWithPingHandler(), "/ping");
  74             httpPort = httpServer.getAddress().getPort();
  75 
  76             httpsServer = new Http2TestServer(true, 0, serverExec, sslContext);
  77             httpsServer.addHandler(new Http2EchoHandler(), "/");
  78 
  79             httpsPort = httpsServer.getAddress().getPort();
  80             httpURIString = "http://127.0.0.1:" + httpPort + "/foo/";
  81             pingURIString = "http://127.0.0.1:" + httpPort + "/ping/";
  82             httpsURIString = "https://127.0.0.1:" + httpsPort + "/bar/";
  83 
  84             httpServer.start();
  85             httpsServer.start();
  86         } catch (Throwable e) {
  87             System.err.println("Throwing now");
  88             e.printStackTrace();
  89             throw e;
  90         }
  91     }
  92 
  93     static List<CompletableFuture<Long>> cfs = Collections
  94         .synchronizedList( new LinkedList<>());
  95 
  96     static CompletableFuture<Long> currentCF;
  97 
  98     static class EchoWithPingHandler extends Http2EchoHandler {
  99         private final Object lock = new Object();
 100 
 101         @Override
 102         public void handle(Http2TestExchange exchange) throws IOException {
 103             // for now only one ping active at a time. don't want to saturate
 104             synchronized(lock) {
 105                 CompletableFuture<Long> cf = currentCF;
 106                 if (cf == null || cf.isDone()) {
 107                     cf = exchange.sendPing();
 108                     assert cf != null;
 109                     cfs.add(cf);
 110                     currentCF = cf;
 111                 }
 112             }
 113             super.handle(exchange);
 114         }
 115     }
 116 
 117     @Test
 118     public static void test() throws Exception {
 119         try {
 120             initialize();
 121             simpleTest(false, false);
 122             simpleTest(false, true);
 123             simpleTest(true, false);
 124             streamTest(false);
 125             streamTest(true);
 126             paramsTest();
 127             CompletableFuture.allOf(cfs.toArray(new CompletableFuture[0])).join();
 128             synchronized (cfs) {
 129                 for (CompletableFuture<Long> cf : cfs) {
 130                     System.out.printf("Ping ack received in %d millisec\n", cf.get());
 131                 }
 132             }
 133         } catch (Throwable tt) {
 134             System.err.println("tt caught");
 135             tt.printStackTrace();
 136             throw tt;
 137         } finally {
 138             httpServer.stop();
 139             httpsServer.stop();
 140             //clientExec.shutdown();
 141         }
 142     }
 143 
 144     static HttpClient getClient() {
 145         if (client == null) {
 146             serverExec = Executors.newCachedThreadPool();
 147             clientExec = Executors.newCachedThreadPool();
 148             client = HttpClient.newBuilder()
 149                                .executor(clientExec)
 150                                .sslContext(sslContext)
 151                                .version(HTTP_2)
 152                                .build();
 153         }
 154         return client;
 155     }
 156 
 157     static URI getURI(boolean secure) {
 158         return getURI(secure, false);
 159     }
 160 
 161     static URI getURI(boolean secure, boolean ping) {
 162         if (secure)
 163             return URI.create(httpsURIString);
 164         else
 165             return URI.create(ping ? pingURIString: httpURIString);
 166     }
 167 
 168     static void checkStatus(int expected, int found) throws Exception {
 169         if (expected != found) {
 170             System.err.printf ("Test failed: wrong status code %d/%d\n",
 171                 expected, found);
 172             throw new RuntimeException("Test failed");
 173         }
 174     }
 175 
 176     static void checkStrings(String expected, String found) throws Exception {
 177         if (!expected.equals(found)) {
 178             System.err.printf ("Test failed: wrong string %s/%s\n",
 179                 expected, found);
 180             throw new RuntimeException("Test failed");
 181         }
 182     }
 183 
 184     static Void compareFiles(Path path1, Path path2) {
 185         return TestUtil.compareFiles(path1, path2);
 186     }
 187 
 188     static Path tempFile() {
 189         return TestUtil.tempFile();
 190     }
 191 
 192     static final String SIMPLE_STRING = "Hello world Goodbye world";
 193 
 194     static final int LOOPS = 13;
 195     static final int FILESIZE = 64 * 1024 + 200;
 196 
 197     static void streamTest(boolean secure) throws Exception {
 198         URI uri = getURI(secure);
 199         System.err.printf("streamTest %b to %s\n" , secure, uri);
 200 
 201         HttpClient client = getClient();
 202         Path src = TestUtil.getAFile(FILESIZE * 4);
 203         HttpRequest req = HttpRequest.newBuilder(uri)
 204                                      .POST(fromFile(src))
 205                                      .build();
 206 
 207         Path dest = Paths.get("streamtest.txt");
 208         dest.toFile().delete();
 209         CompletableFuture<Path> response = client.sendAsync(req, asFile(dest))
 210                 .thenApply(resp -> {
 211                     if (resp.statusCode() != 200)
 212                         throw new RuntimeException();
 213                     return resp.body();
 214                 });
 215         response.join();
 216         compareFiles(src, dest);
 217         System.err.println("streamTest: DONE");
 218     }
 219 
 220     static void paramsTest() throws Exception {
 221         Http2TestServer server = new Http2TestServer(true, 0, serverExec, sslContext);
 222         server.addHandler((t -> {
 223             SSLSession s = t.getSSLSession();
 224             String prot = s.getProtocol();
 225             if (prot.equals("TLSv1.2")) {
 226                 t.sendResponseHeaders(200, -1);
 227             } else {
 228                 System.err.printf("Protocols =%s\n", prot);
 229                 t.sendResponseHeaders(500, -1);
 230             }
 231         }), "/");
 232         server.start();
 233         int port = server.getAddress().getPort();
 234         URI u = new URI("https://127.0.0.1:"+port+"/foo");
 235         HttpClient client = getClient();
 236         HttpRequest req = HttpRequest.newBuilder(u).build();
 237         HttpResponse<String> resp = client.send(req, asString());
 238         int stat = resp.statusCode();
 239         if (stat != 200) {
 240             throw new RuntimeException("paramsTest failed "
 241                 + Integer.toString(stat));
 242         }
 243         System.err.println("paramsTest: DONE");
 244     }
 245 
 246     static void simpleTest(boolean secure, boolean ping) throws Exception {
 247         URI uri = getURI(secure, ping);
 248         System.err.println("Request to " + uri);
 249 
 250         // Do a simple warmup request
 251 
 252         HttpClient client = getClient();
 253         HttpRequest req = HttpRequest.newBuilder(uri)
 254                                      .POST(fromString(SIMPLE_STRING))
 255                                      .build();
 256         HttpResponse<String> response = client.send(req, asString());
 257         HttpHeaders h = response.headers();
 258 
 259         checkStatus(200, response.statusCode());
 260 
 261         String responseBody = response.body();
 262         checkStrings(SIMPLE_STRING, responseBody);
 263 
 264         checkStrings(h.firstValue("x-hello").get(), "world");
 265         checkStrings(h.firstValue("x-bye").get(), "universe");
 266 
 267         // Do loops asynchronously
 268 
 269         CompletableFuture[] responses = new CompletableFuture[LOOPS];
 270         final Path source = TestUtil.getAFile(FILESIZE);
 271         HttpRequest request = HttpRequest.newBuilder(uri)
 272                                          .POST(fromFile(source))
 273                                          .build();
 274         for (int i = 0; i < LOOPS; i++) {
 275             responses[i] = client.sendAsync(request, asFile(tempFile()))
 276                 //.thenApply(resp -> compareFiles(resp.body(), source));
 277                 .thenApply(resp -> {
 278                     System.out.printf("Resp status %d body size %d\n",
 279                                       resp.statusCode(), resp.body().toFile().length());
 280                     return compareFiles(resp.body(), source);
 281                 });
 282             Thread.sleep(100);
 283         }
 284         CompletableFuture.allOf(responses).join();
 285         System.err.println("simpleTest: DONE");
 286     }
 287 }