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