/* * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpHeaders; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import java.nio.charset.Charset; import java.util.Locale; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.stream.Stream; import static java.lang.System.err; /* * @test * @bug 8187503 * @summary An example on how to read a response body with InputStream. * @run main/othervm/manual -Dtest.debug=true BodyProcessorInputStreamTest * @author daniel fuchs */ public class BodyProcessorInputStreamTest { public static boolean DEBUG = Boolean.getBoolean("test.debug"); /** * Examine the response headers to figure out the charset used to * encode the body content. * If the content type is not textual, returns an empty Optional. * Otherwise, returns the body content's charset, defaulting to * ISO-8859-1 if none is explicitly specified. * @param headers The response headers. * @return The charset to use for decoding the response body, if * the response body content is text/... */ public static Optional getCharset(HttpHeaders headers) { Optional contentType = headers.firstValue("Content-Type"); Optional charset = Optional.empty(); if (contentType.isPresent()) { final String[] values = contentType.get().split(";"); if (values[0].startsWith("text/")) { charset = Optional.of(Stream.of(values) .map(x -> x.toLowerCase(Locale.ROOT)) .map(String::trim) .filter(x -> x.startsWith("charset=")) .map(x -> x.substring("charset=".length())) .findFirst() .orElse("ISO-8859-1")) .map(Charset::forName); } } return charset; } public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest .newBuilder(new URI("https://hg.openjdk.org/jdk9/sandbox/jdk/shortlog/http-client-branch/")) .GET() .build(); // This example shows how to return an InputStream that can be used to // start reading the response body before the response is fully received. // In comparison, the snipet below (which uses // HttpResponse.BodyHandlers.ofString()) obviously will not return before the // response body is fully read: // // System.out.println( // client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).get().body()); CompletableFuture> handle = client.sendAsync(request, BodyHandlers.ofInputStream()); if (DEBUG) err.println("Request sent"); HttpResponse pending = handle.get(); // At this point, the response headers have been received, but the // response body may not have arrived yet. This comes from // the implementation of HttpResponseInputStream::getBody above, // which returns an already completed completion stage, without // waiting for any data. // We can therefore access the headers - and the body, which // is our live InputStream, without waiting... HttpHeaders responseHeaders = pending.headers(); // Get the charset declared in the response headers. // The optional will be empty if the content type is not // of type text/... Optional charset = getCharset(responseHeaders); try (InputStream is = pending.body(); // We assume a textual content type. Construct an InputStream // Reader with the appropriate Charset. // charset.get() will throw NPE if the content is not textual. Reader r = new InputStreamReader(is, charset.get())) { char[] buff = new char[32]; int off=0, n=0; if (DEBUG) err.println("Start receiving response body"); if (DEBUG) err.println("Charset: " + charset.get()); // Start consuming the InputStream as the data arrives. // Will block until there is something to read... while ((n = r.read(buff, off, buff.length - off)) > 0) { assert (buff.length - off) > 0; assert n <= (buff.length - off); if (n == (buff.length - off)) { System.out.print(buff); off = 0; } else { off += n; } assert off < buff.length; } // last call to read may not have filled 'buff' completely. // flush out the remaining characters. assert off >= 0 && off < buff.length; for (int i=0; i < off; i++) { System.out.print(buff[i]); } // We're done! System.out.println("Done!"); } } }