1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.io.IOException;
  25 import java.io.InputStream;
  26 import java.io.InputStreamReader;
  27 import java.io.Reader;
  28 import java.net.URI;
  29 import jdk.incubator.http.HttpClient;
  30 import jdk.incubator.http.HttpHeaders;
  31 import jdk.incubator.http.HttpRequest;
  32 import jdk.incubator.http.HttpResponse;
  33 import java.nio.ByteBuffer;
  34 import java.nio.charset.Charset;
  35 import java.util.ArrayList;
  36 import java.util.Iterator;
  37 import java.util.List;
  38 import java.util.Locale;
  39 import java.util.Optional;
  40 import java.util.concurrent.ArrayBlockingQueue;
  41 import java.util.concurrent.BlockingQueue;
  42 import java.util.concurrent.CompletableFuture;
  43 import java.util.concurrent.CompletionStage;
  44 import java.util.concurrent.Flow;
  45 import java.util.stream.Stream;
  46 import static java.lang.System.err;
  47 
  48 /*
  49  * @test
  50  * @bug 8187503
  51  * @summary An example on how to read a response body with InputStream...
  52  * @run main/othervm -Dtest.debug=true BodyProcessorInputStreamTest
  53  * @author daniel fuchs
  54  */
  55 public class BodyProcessorInputStreamTest {
  56 
  57     public static boolean DEBUG = Boolean.getBoolean("test.debug");
  58 
  59     /**
  60      * Examine the response headers to figure out the charset used to
  61      * encode the body content.
  62      * If the content type is not textual, returns an empty Optional.
  63      * Otherwise, returns the body content's charset, defaulting to
  64      * ISO-8859-1 if none is explicitly specified.
  65      * @param headers The response headers.
  66      * @return The charset to use for decoding the response body, if
  67      *         the response body content is text/...
  68      */
  69     public static Optional<Charset> getCharset(HttpHeaders headers) {
  70         Optional<String> contentType = headers.firstValue("Content-Type");
  71         Optional<Charset> charset = Optional.empty();
  72         if (contentType.isPresent()) {
  73             final String[] values = contentType.get().split(";");
  74             if (values[0].startsWith("text/")) {
  75                 charset = Optional.of(Stream.of(values)
  76                     .map(x -> x.toLowerCase(Locale.ROOT))
  77                     .map(String::trim)
  78                     .filter(x -> x.startsWith("charset="))
  79                     .map(x -> x.substring("charset=".length()))
  80                     .findFirst()
  81                     .orElse("ISO-8859-1"))
  82                     .map(Charset::forName);
  83             }
  84         }
  85         return charset;
  86     }
  87 
  88     public static void main(String[] args) throws Exception {
  89         HttpClient client = HttpClient.newHttpClient();
  90         HttpRequest request = HttpRequest
  91             .newBuilder(new URI("http://hg.openjdk.java.net/jdk9/sandbox/jdk/shortlog/http-client-branch/"))
  92             .GET()
  93             .build();
  94 
  95         // This example shows how to return an InputStream that can be used to
  96         // start reading the response body before the response is fully received.
  97         // In comparison, the snipet below (which uses
  98         // HttpResponse.BodyHandler.asString()) obviously will not return before the
  99         // response body is fully read:
 100         //
 101         // System.out.println(
 102         //    client.sendAsync(request, HttpResponse.BodyHandler.asString()).get().body());
 103 
 104         CompletableFuture<HttpResponse<InputStream>> handle =
 105             client.sendAsync(request, HttpResponse.BodyHandler.asInputStream());
 106         if (DEBUG) err.println("Request sent");
 107 
 108         HttpResponse<InputStream> pending = handle.get();
 109 
 110         // At this point, the response headers have been received, but the
 111         // response body may not have arrived yet. This comes from
 112         // the implementation of HttpResponseInputStream::getBody above,
 113         // which returns an already completed completion stage, without
 114         // waiting for any data.
 115         // We can therefore access the headers - and the body, which
 116         // is our live InputStream, without waiting...
 117         HttpHeaders responseHeaders = pending.headers();
 118 
 119         // Get the charset declared in the response headers.
 120         // The optional will be empty if the content type is not
 121         // of type text/...
 122         Optional<Charset> charset = getCharset(responseHeaders);
 123 
 124         try (InputStream is = pending.body();
 125             // We assume a textual content type. Construct an InputStream
 126             // Reader with the appropriate Charset.
 127             // charset.get() will throw NPE if the content is not textual.
 128             Reader r = new InputStreamReader(is, charset.get())) {
 129 
 130             char[] buff = new char[32];
 131             int off=0, n=0;
 132             if (DEBUG) err.println("Start receiving response body");
 133             if (DEBUG) err.println("Charset: " + charset.get());
 134 
 135             // Start consuming the InputStream as the data arrives.
 136             // Will block until there is something to read...
 137             while ((n = r.read(buff, off, buff.length - off)) > 0) {
 138                 assert (buff.length - off) > 0;
 139                 assert n <= (buff.length - off);
 140                 if (n == (buff.length - off)) {
 141                     System.out.print(buff);
 142                     off = 0;
 143                 } else {
 144                     off += n;
 145                 }
 146                 assert off < buff.length;
 147             }
 148 
 149             // last call to read may not have filled 'buff' completely.
 150             // flush out the remaining characters.
 151             assert off >= 0 && off < buff.length;
 152             for (int i=0; i < off; i++) {
 153                 System.out.print(buff[i]);
 154             }
 155 
 156             // We're done!
 157             System.out.println("Done!");
 158         }
 159     }
 160 
 161 }