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 }