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