1 /*
2  * Copyright (c) 2018, 2020, 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 import java.io.BufferedReader;
25 import java.io.ByteArrayOutputStream;
26 import java.io.IOException;
27 import java.io.PrintStream;
28 import java.io.StringReader;
29 import java.io.UncheckedIOException;
30 import java.math.BigInteger;
31 import java.net.InetAddress;
32 import java.net.InetSocketAddress;
33 import java.net.URI;
34 import java.net.http.HttpClient;
35 import java.net.http.HttpClient.Builder;
36 import java.net.http.HttpRequest;
37 import java.net.http.HttpRequest.BodyPublishers;
38 import java.net.http.HttpResponse;
39 import java.net.http.HttpResponse.BodyHandlers;
40 import java.net.http.HttpResponse.BodySubscribers;
41 import java.nio.charset.Charset;
42 import java.nio.charset.StandardCharsets;
43 import java.util.ArrayList;
44 import java.util.List;
45 import java.util.concurrent.CompletableFuture;
46 import java.util.concurrent.CopyOnWriteArrayList;
47 import java.util.concurrent.ExecutorService;
48 import java.util.concurrent.Executors;
49 import java.util.concurrent.Flow;
50 import java.util.concurrent.ThreadFactory;
51 import java.util.concurrent.atomic.AtomicInteger;
52 import java.util.function.Function;
53 import java.util.function.Supplier;
54 import java.util.stream.Collectors;
55 import java.util.stream.Stream;
56 import javax.net.ssl.SSLContext;
57 import com.sun.net.httpserver.HttpServer;
58 import com.sun.net.httpserver.HttpsConfigurator;
59 import com.sun.net.httpserver.HttpsServer;
60 import jdk.test.lib.net.SimpleSSLContext;
61 import org.testng.annotations.AfterTest;
62 import org.testng.annotations.BeforeTest;
63 import org.testng.annotations.DataProvider;
64 import org.testng.annotations.Test;
65 
66 import static java.nio.charset.StandardCharsets.UTF_16;
67 import static java.nio.charset.StandardCharsets.UTF_8;
68 import static java.net.http.HttpRequest.BodyPublishers.ofString;
69 import static org.testng.Assert.assertEquals;
70 import static org.testng.Assert.assertNotNull;
71 import static org.testng.Assert.assertThrows;
72 import static org.testng.Assert.assertTrue;
73 
74 /*
75  * @test
76  * @summary Basic tests for line adapter subscribers as created by
77  *          the BodyHandlers returned by BodyHandler::fromLineSubscriber
78  *          and BodyHandler::asLines
79  * @bug 8256459
80  * @modules java.base/sun.net.www.http
81  *          java.net.http/jdk.internal.net.http.common
82  *          java.net.http/jdk.internal.net.http.frame
83  *          java.net.http/jdk.internal.net.http.hpack
84  *          java.logging
85  *          jdk.httpserver
86  * @library /test/lib http2/server
87  * @build Http2TestServer LineBodyHandlerTest HttpServerAdapters
88  * @build jdk.test.lib.net.SimpleSSLContext
89  * @run testng/othervm LineBodyHandlerTest
90  */
91 
92 public class LineBodyHandlerTest implements HttpServerAdapters {
93 
94     SSLContext sslContext;
95     HttpTestServer httpTestServer;    // HTTP/1.1    [ 4 servers ]
96     HttpTestServer httpsTestServer;   // HTTPS/1.1
97     HttpTestServer http2TestServer;   // HTTP/2 ( h2c )
98     HttpTestServer https2TestServer;  // HTTP/2 ( h2  )
99     String httpURI;
100     String httpsURI;
101     String http2URI;
102     String https2URI;
103 
104     @DataProvider(name = "uris")
variants()105     public Object[][] variants() {
106         return new Object[][]{
107                 { httpURI   },
108                 { httpsURI  },
109                 { http2URI  },
110                 { https2URI },
111         };
112     }
113 
114     static final Class<NullPointerException> NPE = NullPointerException.class;
115     static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
116 
117     @Test
testNull()118     public void testNull() {
119         assertThrows(NPE, () -> BodyHandlers.fromLineSubscriber(null));
120         assertNotNull(BodyHandlers.fromLineSubscriber(new StringSubscriber()));
121         assertThrows(NPE, () -> BodyHandlers.fromLineSubscriber(null, Function.identity(), "\n"));
122         assertThrows(NPE, () -> BodyHandlers.fromLineSubscriber(new StringSubscriber(), null, "\n"));
123         assertNotNull(BodyHandlers.fromLineSubscriber(new StringSubscriber(), Function.identity(), null));
124         assertThrows(NPE, () -> BodyHandlers.fromLineSubscriber(null, null, "\n"));
125         assertThrows(NPE, () -> BodyHandlers.fromLineSubscriber(null, Function.identity(), null));
126         assertThrows(NPE, () -> BodyHandlers.fromLineSubscriber(new StringSubscriber(), null, null));
127 
128         assertThrows(NPE, () -> BodySubscribers.fromLineSubscriber(null));
129         assertThrows(NPE, () -> BodySubscribers.fromLineSubscriber(null, Function.identity(),
130                 Charset.defaultCharset(), System.lineSeparator()));
131         assertThrows(NPE, () -> BodySubscribers.fromLineSubscriber(new StringSubscriber(), null,
132                 Charset.defaultCharset(), System.lineSeparator()));
133         assertThrows(NPE, () -> BodySubscribers.fromLineSubscriber(new StringSubscriber(), Function.identity(),
134                 null, System.lineSeparator()));
135         assertNotNull(BodySubscribers.fromLineSubscriber(new StringSubscriber(), Function.identity(),
136                 Charset.defaultCharset(), null));
137         assertThrows(NPE, () -> BodySubscribers.fromLineSubscriber(null, null,
138                 Charset.defaultCharset(), System.lineSeparator()));
139         assertThrows(NPE, () -> BodySubscribers.fromLineSubscriber(null, Function.identity(),
140                 null, System.lineSeparator()));
141         assertThrows(NPE, () -> BodySubscribers.fromLineSubscriber(null, Function.identity(),
142                 Charset.defaultCharset(), null));
143         assertThrows(NPE, () -> BodySubscribers.fromLineSubscriber(new StringSubscriber(), null,
144                 null, System.lineSeparator()));
145         assertThrows(NPE, () -> BodySubscribers.fromLineSubscriber(new StringSubscriber(), null,
146                 Charset.defaultCharset(), null));
147         assertThrows(NPE, () -> BodySubscribers.fromLineSubscriber(new StringSubscriber(), Function.identity(),
148                 null, null));
149         assertThrows(NPE, () -> BodySubscribers.fromLineSubscriber(new StringSubscriber(), null, null, null));
150         assertThrows(NPE, () -> BodySubscribers.fromLineSubscriber(null, Function.identity(),
151                 null, null));
152         assertThrows(NPE, () -> BodySubscribers.fromLineSubscriber(null, null,
153                 Charset.defaultCharset(), null));
154         assertThrows(NPE, () -> BodySubscribers.fromLineSubscriber(null, null,
155                 null, System.lineSeparator()));
156         assertThrows(NPE, () -> BodySubscribers.fromLineSubscriber(null, null, null, null));
157     }
158 
159     @Test
testIAE()160     public void testIAE() {
161         assertThrows(IAE, () -> BodyHandlers.fromLineSubscriber(new StringSubscriber(), Function.identity(),""));
162         assertThrows(IAE, () -> BodyHandlers.fromLineSubscriber(new CharSequenceSubscriber(), Function.identity(),""));
163         assertThrows(IAE, () -> BodyHandlers.fromLineSubscriber(new ObjectSubscriber(), Function.identity(), ""));
164         assertThrows(IAE, () -> BodySubscribers.fromLineSubscriber(new StringSubscriber(), Function.identity(),
165                     StandardCharsets.UTF_8, ""));
166         assertThrows(IAE, () -> BodySubscribers.fromLineSubscriber(new CharSequenceSubscriber(), Function.identity(),
167                     StandardCharsets.UTF_16, ""));
168         assertThrows(IAE, () -> BodySubscribers.fromLineSubscriber(new ObjectSubscriber(), Function.identity(),
169                     StandardCharsets.US_ASCII, ""));
170     }
171 
lines(String text, String eol)172     private static final List<String> lines(String text, String eol) {
173         if (eol == null) {
174             return new BufferedReader(new StringReader(text)).lines().collect(Collectors.toList());
175         } else {
176             String replaced = text.replace(eol, "|");
177             int i=0;
178             while(replaced.endsWith("||")) {
179                 replaced = replaced.substring(0,replaced.length()-1);
180                 i++;
181             }
182             List<String> res = List.of(replaced.split("\\|"));
183             if (i > 0) {
184                 res = new ArrayList<>(res);
185                 for (int j=0; j<i; j++) res.add("");
186             }
187             return res;
188         }
189     }
190 
newClient()191     HttpClient newClient() {
192         return HttpClient.newBuilder()
193                 .sslContext(sslContext)
194                 .proxy(Builder.NO_PROXY)
195                 .build();
196     }
197 
198     @Test(dataProvider = "uris")
testStringWithFinisher(String url)199     void testStringWithFinisher(String url) {
200         String body = "May the luck of the Irish be with you!";
201         HttpClient client = newClient();
202         HttpRequest request = HttpRequest.newBuilder(URI.create(url))
203                 .POST(BodyPublishers.ofString(body))
204                 .build();
205 
206         StringSubscriber subscriber = new StringSubscriber();
207         CompletableFuture<HttpResponse<String>> cf
208                 = client.sendAsync(request, BodyHandlers.fromLineSubscriber(
209                         subscriber, Supplier::get, "\n"));
210         assertNoObtrusion(cf);
211         HttpResponse<String> response = cf.join();
212         String text = response.body();
213         System.out.println(text);
214         assertEquals(response.statusCode(), 200);
215         assertEquals(text, body);
216         assertEquals(subscriber.list, lines(body, "\n"));
217     }
218 
219     @Test(dataProvider = "uris")
testAsStream(String url)220     void testAsStream(String url) {
221         String body = "May the luck of the Irish be with you!";
222         HttpClient client = newClient();
223         HttpRequest request = HttpRequest.newBuilder(URI.create(url))
224                 .POST(BodyPublishers.ofString(body))
225                 .build();
226 
227         CompletableFuture<HttpResponse<Stream<String>>> cf
228                 = client.sendAsync(request, BodyHandlers.ofLines());
229         assertNoObtrusion(cf);
230         HttpResponse<Stream<String>> response = cf.join();
231         Stream<String> stream = response.body();
232         List<String> list = stream.collect(Collectors.toList());
233         String text = list.stream().collect(Collectors.joining("|"));
234         System.out.println(text);
235         assertEquals(response.statusCode(), 200);
236         assertEquals(text, body);
237         assertEquals(list, List.of(body));
238         assertEquals(list, lines(body, null));
239     }
240 
241     @Test(dataProvider = "uris")
testStringWithFinisher2(String url)242     void testStringWithFinisher2(String url) {
243         String body = "May the luck\r\n\r\n of the Irish be with you!";
244         HttpClient client = newClient();
245 
246         HttpRequest request = HttpRequest.newBuilder(URI.create(url))
247                 .POST(BodyPublishers.ofString(body))
248                 .build();
249 
250         StringSubscriber subscriber = new StringSubscriber();
251         CompletableFuture<HttpResponse<Void>> cf
252                 = client.sendAsync(request,
253                                    BodyHandlers.fromLineSubscriber(subscriber));
254         assertNoObtrusion(cf);
255         HttpResponse<Void> response = cf.join();
256         String text = subscriber.get();
257         System.out.println(text);
258         assertEquals(response.statusCode(), 200);
259         assertEquals(text, body.replace("\r\n", "\n"));
260         assertEquals(subscriber.list, lines(body, null));
261     }
262 
263     @Test(dataProvider = "uris")
testAsStreamWithCRLF(String url)264     void testAsStreamWithCRLF(String url) {
265         String body = "May the luck\r\n\r\n of the Irish be with you!";
266         HttpClient client = newClient();
267         HttpRequest request = HttpRequest.newBuilder(URI.create(url))
268                 .POST(BodyPublishers.ofString(body))
269                 .build();
270 
271         CompletableFuture<HttpResponse<Stream<String>>> cf
272                 = client.sendAsync(request, BodyHandlers.ofLines());
273         assertNoObtrusion(cf);
274         HttpResponse<Stream<String>> response = cf.join();
275         Stream<String> stream = response.body();
276         List<String> list = stream.collect(Collectors.toList());
277         String text = list.stream().collect(Collectors.joining("|"));
278         System.out.println(text);
279         assertEquals(response.statusCode(), 200);
280         assertEquals(text, "May the luck|| of the Irish be with you!");
281         assertEquals(list, List.of("May the luck",
282                                    "",
283                                    " of the Irish be with you!"));
284         assertEquals(list, lines(body, null));
285     }
286 
287     @Test(dataProvider = "uris")
testStringWithFinisherBlocking(String url)288     void testStringWithFinisherBlocking(String url) throws Exception {
289         String body = "May the luck of the Irish be with you!";
290         HttpClient client = newClient();
291         HttpRequest request = HttpRequest.newBuilder(URI.create(url))
292                 .POST(BodyPublishers.ofString(body)).build();
293 
294         StringSubscriber subscriber = new StringSubscriber();
295         HttpResponse<String> response = client.send(request,
296                 BodyHandlers.fromLineSubscriber(subscriber, Supplier::get, "\n"));
297         String text = response.body();
298         System.out.println(text);
299         assertEquals(response.statusCode(), 200);
300         assertEquals(text, "May the luck of the Irish be with you!");
301         assertEquals(subscriber.list, lines(body, "\n"));
302     }
303 
304     @Test(dataProvider = "uris")
testStringWithoutFinisherBlocking(String url)305     void testStringWithoutFinisherBlocking(String url) throws Exception {
306         String body = "May the luck of the Irish be with you!";
307         HttpClient client = newClient();
308         HttpRequest request = HttpRequest.newBuilder(URI.create(url))
309                 .POST(BodyPublishers.ofString(body)).build();
310 
311         StringSubscriber subscriber = new StringSubscriber();
312         HttpResponse<Void> response = client.send(request,
313                 BodyHandlers.fromLineSubscriber(subscriber));
314         String text = subscriber.get();
315         System.out.println(text);
316         assertEquals(response.statusCode(), 200);
317         assertEquals(text, "May the luck of the Irish be with you!");
318         assertEquals(subscriber.list, lines(body, null));
319     }
320 
321     // Subscriber<Object>
322 
323     @Test(dataProvider = "uris")
testAsStreamWithMixedCRLF(String url)324     void testAsStreamWithMixedCRLF(String url) {
325         String body = "May\r\n the wind\r\n always be\rat your back.\r\r";
326         HttpClient client = newClient();
327         HttpRequest request = HttpRequest.newBuilder(URI.create(url))
328                 .POST(BodyPublishers.ofString(body))
329                 .build();
330 
331         CompletableFuture<HttpResponse<Stream<String>>> cf
332                 = client.sendAsync(request, BodyHandlers.ofLines());
333         assertNoObtrusion(cf);
334         HttpResponse<Stream<String>> response = cf.join();
335         Stream<String> stream = response.body();
336         List<String> list = stream.collect(Collectors.toList());
337         String text = list.stream().collect(Collectors.joining("|"));
338         System.out.println(text);
339         assertEquals(response.statusCode(), 200);
340         assertTrue(text.length() != 0);  // what else can be asserted!
341         assertEquals(text, "May| the wind| always be|at your back.|");
342         assertEquals(list, List.of("May",
343                                    " the wind",
344                                    " always be",
345                                    "at your back.",
346                                    ""));
347         assertEquals(list, lines(body, null));
348     }
349 
350     @Test(dataProvider = "uris")
testAsStreamWithMixedCRLF_UTF8(String url)351     void testAsStreamWithMixedCRLF_UTF8(String url) {
352         String body = "May\r\n the wind\r\n always be\rat your back.\r\r";
353         HttpClient client = newClient();
354         HttpRequest request = HttpRequest.newBuilder(URI.create(url))
355                 .header("Content-type", "text/text; charset=UTF-8")
356                 .POST(BodyPublishers.ofString(body, UTF_8)).build();
357 
358         CompletableFuture<HttpResponse<Stream<String>>> cf
359                 = client.sendAsync(request, BodyHandlers.ofLines());
360         assertNoObtrusion(cf);
361         HttpResponse<Stream<String>> response = cf.join();
362         Stream<String> stream = response.body();
363         List<String> list = stream.collect(Collectors.toList());
364         String text = list.stream().collect(Collectors.joining("|"));
365         System.out.println(text);
366         assertEquals(response.statusCode(), 200);
367         assertTrue(text.length() != 0);  // what else can be asserted!
368         assertEquals(text, "May| the wind| always be|at your back.|");
369         assertEquals(list, List.of("May",
370                                    " the wind",
371                                    " always be",
372                                    "at your back.", ""));
373         assertEquals(list, lines(body, null));
374     }
375 
376     @Test(dataProvider = "uris")
testAsStreamWithMixedCRLF_UTF16(String url)377     void testAsStreamWithMixedCRLF_UTF16(String url) {
378         String body = "May\r\n the wind\r\n always be\rat your back.\r\r";
379         HttpClient client = newClient();
380         HttpRequest request = HttpRequest.newBuilder(URI.create(url))
381                 .header("Content-type", "text/text; charset=UTF-16")
382                 .POST(BodyPublishers.ofString(body, UTF_16)).build();
383 
384         CompletableFuture<HttpResponse<Stream<String>>> cf
385                 = client.sendAsync(request, BodyHandlers.ofLines());
386         assertNoObtrusion(cf);
387         HttpResponse<Stream<String>> response = cf.join();
388         Stream<String> stream = response.body();
389         List<String> list = stream.collect(Collectors.toList());
390         String text = list.stream().collect(Collectors.joining("|"));
391         System.out.println(text);
392         assertEquals(response.statusCode(), 200);
393         assertTrue(text.length() != 0);  // what else can be asserted!
394         assertEquals(text, "May| the wind| always be|at your back.|");
395         assertEquals(list, List.of("May",
396                                    " the wind",
397                                    " always be",
398                                    "at your back.",
399                                    ""));
400         assertEquals(list, lines(body, null));
401     }
402 
403     @Test(dataProvider = "uris")
testObjectWithFinisher(String url)404     void testObjectWithFinisher(String url) {
405         String body = "May\r\n the wind\r\n always be\rat your back.";
406         HttpClient client = newClient();
407         HttpRequest request = HttpRequest.newBuilder(URI.create(url))
408                 .POST(BodyPublishers.ofString(body))
409                 .build();
410 
411         ObjectSubscriber subscriber = new ObjectSubscriber();
412         CompletableFuture<HttpResponse<String>> cf
413                 = client.sendAsync(request, BodyHandlers.fromLineSubscriber(
414                         subscriber, ObjectSubscriber::get, "\r\n"));
415         assertNoObtrusion(cf);
416         HttpResponse<String> response = cf.join();
417         String text = response.body();
418         System.out.println(text);
419         assertEquals(response.statusCode(), 200);
420         assertTrue(text.length() != 0);  // what else can be asserted!
421         assertEquals(text, "May\n the wind\n always be\rat your back.");
422         assertEquals(subscriber.list, List.of("May",
423                                               " the wind",
424                                               " always be\rat your back."));
425         assertEquals(subscriber.list, lines(body, "\r\n"));
426     }
427 
428     @Test(dataProvider = "uris")
testObjectWithFinisher_UTF16(String url)429     void testObjectWithFinisher_UTF16(String url) {
430         String body = "May\r\n the wind\r\n always be\rat your back.\r\r";
431         HttpClient client = newClient();
432         HttpRequest request = HttpRequest.newBuilder(URI.create(url))
433                 .header("Content-type", "text/text; charset=UTF-16")
434                 .POST(BodyPublishers.ofString(body, UTF_16)).build();
435         ObjectSubscriber subscriber = new ObjectSubscriber();
436         CompletableFuture<HttpResponse<String>> cf
437                 = client.sendAsync(request, BodyHandlers.fromLineSubscriber(
438                         subscriber, ObjectSubscriber::get, null));
439         assertNoObtrusion(cf);
440         HttpResponse<String> response = cf.join();
441         String text = response.body();
442         System.out.println(text);
443         assertEquals(response.statusCode(), 200);
444         assertTrue(text.length() != 0);  // what else can be asserted!
445         assertEquals(text, "May\n the wind\n always be\nat your back.\n");
446         assertEquals(subscriber.list, List.of("May",
447                                               " the wind",
448                                               " always be",
449                                               "at your back.",
450                                               ""));
451         assertEquals(subscriber.list, lines(body, null));
452     }
453 
454     @Test(dataProvider = "uris")
testObjectWithoutFinisher(String url)455     void testObjectWithoutFinisher(String url) {
456         String body = "May\r\n the wind\r\n always be\rat your back.";
457         HttpClient client = newClient();
458         HttpRequest request = HttpRequest.newBuilder(URI.create(url))
459                 .POST(BodyPublishers.ofString(body))
460                 .build();
461 
462         ObjectSubscriber subscriber = new ObjectSubscriber();
463         CompletableFuture<HttpResponse<Void>> cf
464                 = client.sendAsync(request,
465                                    BodyHandlers.fromLineSubscriber(subscriber));
466         assertNoObtrusion(cf);
467         HttpResponse<Void> response = cf.join();
468         String text = subscriber.get();
469         System.out.println(text);
470         assertEquals(response.statusCode(), 200);
471         assertTrue(text.length() != 0);  // what else can be asserted!
472         assertEquals(text, "May\n the wind\n always be\nat your back.");
473         assertEquals(subscriber.list, List.of("May",
474                                               " the wind",
475                                               " always be",
476                                               "at your back."));
477         assertEquals(subscriber.list, lines(body, null));
478     }
479 
480     @Test(dataProvider = "uris")
testObjectWithFinisherBlocking(String url)481     void testObjectWithFinisherBlocking(String url) throws Exception {
482         String body = "May\r\n the wind\r\n always be\nat your back.";
483         HttpClient client = newClient();
484         HttpRequest request = HttpRequest.newBuilder(URI.create(url))
485                 .POST(BodyPublishers.ofString(body))
486                 .build();
487 
488         ObjectSubscriber subscriber = new ObjectSubscriber();
489         HttpResponse<String> response = client.send(request,
490                 BodyHandlers.fromLineSubscriber(subscriber,
491                                                ObjectSubscriber::get,
492                                    "\r\n"));
493         String text = response.body();
494         System.out.println(text);
495         assertEquals(response.statusCode(), 200);
496         assertTrue(text.length() != 0);  // what else can be asserted!
497         assertEquals(text, "May\n the wind\n always be\nat your back.");
498         assertEquals(subscriber.list, List.of("May",
499                                               " the wind",
500                                               " always be\nat your back."));
501         assertEquals(subscriber.list, lines(body, "\r\n"));
502     }
503 
504     @Test(dataProvider = "uris")
testObjectWithoutFinisherBlocking(String url)505     void testObjectWithoutFinisherBlocking(String url) throws Exception {
506         String body = "May\r\n the wind\r\n always be\nat your back.";
507         HttpClient client = newClient();
508         HttpRequest request = HttpRequest.newBuilder(URI.create(url))
509                 .POST(BodyPublishers.ofString(body))
510                 .build();
511 
512         ObjectSubscriber subscriber = new ObjectSubscriber();
513         HttpResponse<Void> response = client.send(request,
514                 BodyHandlers.fromLineSubscriber(subscriber));
515         String text = subscriber.get();
516         System.out.println(text);
517         assertEquals(response.statusCode(), 200);
518         assertTrue(text.length() != 0);  // what else can be asserted!
519         assertEquals(text, "May\n the wind\n always be\nat your back.");
520         assertEquals(subscriber.list, List.of("May",
521                                               " the wind",
522                                               " always be",
523                                               "at your back."));
524         assertEquals(subscriber.list, lines(body, null));
525     }
526 
527     static private final String LINE = "Bient\u00f4t nous plongerons dans les" +
528             " fr\u00f4\ud801\udc00des t\u00e9n\u00e8bres, ";
529 
bigtext()530     static private final String bigtext() {
531         StringBuilder res = new StringBuilder((LINE.length() + 1) * 50);
532         for (int i = 0; i<50; i++) {
533             res.append(LINE);
534             if (i%2 == 0) res.append("\r\n");
535         }
536         return res.toString();
537     }
538 
539     @Test(dataProvider = "uris")
testBigTextFromLineSubscriber(String url)540     void testBigTextFromLineSubscriber(String url) {
541         HttpClient client = newClient();
542         String bigtext = bigtext();
543         HttpRequest request = HttpRequest.newBuilder(URI.create(url))
544                 .POST(BodyPublishers.ofString(bigtext))
545                 .build();
546 
547         StringSubscriber subscriber = new StringSubscriber();
548         CompletableFuture<HttpResponse<String>> cf
549                 = client.sendAsync(request, BodyHandlers.fromLineSubscriber(
550                         subscriber, Supplier::get, "\r\n"));
551         assertNoObtrusion(cf);
552         HttpResponse<String> response = cf.join();
553         String text = response.body();
554         System.out.println(text);
555         assertEquals(response.statusCode(), 200);
556         assertEquals(text, bigtext.replace("\r\n", "\n"));
557         assertEquals(subscriber.list, lines(bigtext, "\r\n"));
558     }
559 
560     @Test(dataProvider = "uris")
testBigTextAsStream(String url)561     void testBigTextAsStream(String url) {
562         HttpClient client = newClient();
563         String bigtext = bigtext();
564         HttpRequest request = HttpRequest.newBuilder(URI.create(url))
565                 .POST(BodyPublishers.ofString(bigtext))
566                 .build();
567 
568         CompletableFuture<HttpResponse<Stream<String>>> cf
569                 = client.sendAsync(request, BodyHandlers.ofLines());
570         assertNoObtrusion(cf);
571         HttpResponse<Stream<String>> response = cf.join();
572         Stream<String> stream = response.body();
573         List<String> list = stream.collect(Collectors.toList());
574         String text = list.stream().collect(Collectors.joining("|"));
575         System.out.println(text);
576         assertEquals(response.statusCode(), 200);
577         assertEquals(text, bigtext.replace("\r\n", "|"));
578         assertEquals(list, List.of(bigtext.split("\r\n")));
579         assertEquals(list, lines(bigtext, null));
580     }
581 
582     /** An abstract Subscriber that converts all received data into a String. */
583     static abstract class AbstractSubscriber implements Supplier<String> {
584         protected volatile Flow.Subscription subscription;
585         protected final StringBuilder baos = new StringBuilder();
586         protected volatile String text;
587         protected volatile RuntimeException error;
588         protected final List<Object> list = new CopyOnWriteArrayList<>();
589 
onSubscribe(Flow.Subscription subscription)590         public void onSubscribe(Flow.Subscription subscription) {
591             this.subscription = subscription;
592             subscription.request(Long.MAX_VALUE);
593         }
onError(Throwable throwable)594         public void onError(Throwable throwable) {
595             System.out.println(this + " onError: " + throwable);
596             error = new RuntimeException(throwable);
597         }
onComplete()598         public void onComplete() {
599             System.out.println(this + " onComplete");
600             text = baos.toString();
601         }
get()602         @Override public String get() {
603             if (error != null) throw error;
604             return text;
605         }
606     }
607 
608     static class StringSubscriber extends AbstractSubscriber
609             implements Flow.Subscriber<String>, Supplier<String>
610     {
onNext(String item)611         @Override public void onNext(String item) {
612             System.out.print(this + " onNext: \"" + item + "\"");
613             if (baos.length() != 0) baos.append('\n');
614             baos.append(item);
615             list.add(item);
616         }
617     }
618 
619     static class CharSequenceSubscriber extends AbstractSubscriber
620             implements Flow.Subscriber<CharSequence>, Supplier<String>
621     {
onNext(CharSequence item)622         @Override public void onNext(CharSequence item) {
623             System.out.print(this + " onNext: " + item);
624             if (baos.length() != 0) baos.append('\n');
625             baos.append(item);
626             list.add(item);
627         }
628     }
629 
630     static class ObjectSubscriber extends AbstractSubscriber
631             implements Flow.Subscriber<Object>, Supplier<String>
632     {
onNext(Object item)633         @Override public void onNext(Object item) {
634             System.out.print(this + " onNext: " + item);
635             if (baos.length() != 0) baos.append('\n');
636             baos.append(item);
637             list.add(item);
638         }
639     }
640 
641 
uncheckedWrite(ByteArrayOutputStream baos, byte[] ba)642     static void uncheckedWrite(ByteArrayOutputStream baos, byte[] ba) {
643         try {
644             baos.write(ba);
645         } catch (IOException e) {
646             throw new UncheckedIOException(e);
647         }
648     }
649 
executorFor(String serverThreadName)650     private static ExecutorService executorFor(String serverThreadName) {
651         ThreadFactory factory = new ThreadFactory() {
652             final AtomicInteger counter = new AtomicInteger();
653             @Override
654             public Thread newThread(Runnable r) {
655                 Thread thread = new Thread(r);
656                 thread.setName(serverThreadName + "#" + counter.incrementAndGet());
657                 return thread;
658             }
659         };
660         return Executors.newCachedThreadPool(factory);
661     }
662 
663     @BeforeTest
setup()664     public void setup() throws Exception {
665         sslContext = new SimpleSSLContext().get();
666         if (sslContext == null)
667             throw new AssertionError("Unexpected null sslContext");
668 
669         InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
670         httpTestServer = HttpTestServer.of(HttpServer.create(sa, 0),
671                 executorFor("HTTP/1.1 Server Thread"));
672         httpTestServer.addHandler(new HttpTestEchoHandler(), "/http1/echo");
673         httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/echo";
674 
675         HttpsServer httpsServer = HttpsServer.create(sa, 0);
676         httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));
677         httpsTestServer = HttpTestServer.of(httpsServer,
678                 executorFor("HTTPS/1.1 Server Thread"));
679         httpsTestServer.addHandler(new HttpTestEchoHandler(),"/https1/echo");
680         httpsURI = "https://" + httpsTestServer.serverAuthority() + "/https1/echo";
681 
682         http2TestServer = HttpTestServer.of(new Http2TestServer("localhost", false, 0));
683         http2TestServer.addHandler(new HttpTestEchoHandler(), "/http2/echo");
684         http2URI = "http://" + http2TestServer.serverAuthority() + "/http2/echo";
685 
686         https2TestServer = HttpTestServer.of(new Http2TestServer("localhost", true, sslContext));
687         https2TestServer.addHandler(new HttpTestEchoHandler(), "/https2/echo");
688         https2URI = "https://" + https2TestServer.serverAuthority() + "/https2/echo";
689 
690         httpTestServer.start();
691         httpsTestServer.start();
692         http2TestServer.start();
693         https2TestServer.start();
694     }
695 
696     @AfterTest
teardown()697     public void teardown() throws Exception {
698         httpTestServer.stop();
699         httpsTestServer.stop();
700         http2TestServer.stop();
701         https2TestServer.stop();
702     }
703 
printBytes(PrintStream out, String prefix, byte[] bytes)704     static void printBytes(PrintStream out, String prefix, byte[] bytes) {
705         int padding = 4 + 4 - (bytes.length % 4);
706         padding = padding > 4 ? padding - 4 : 4;
707         byte[] bigbytes = new byte[bytes.length + padding];
708         System.arraycopy(bytes, 0, bigbytes, padding, bytes.length);
709         out.println(prefix + bytes.length + " "
710                     + new BigInteger(bigbytes).toString(16));
711     }
712 
assertNoObtrusion(CompletableFuture<?> cf)713     private static void assertNoObtrusion(CompletableFuture<?> cf) {
714         assertThrows(UnsupportedOperationException.class,
715                      () -> cf.obtrudeException(new RuntimeException()));
716         assertThrows(UnsupportedOperationException.class,
717                      () -> cf.obtrudeValue(null));
718     }
719 }
720