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