1 /* 2 * Copyright (c) 2018, 2019, 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.OutputStream; 27 import java.net.InetAddress; 28 import java.net.InetSocketAddress; 29 import java.net.ServerSocket; 30 import java.net.Socket; 31 import java.net.URI; 32 import java.net.http.HttpClient; 33 import java.net.http.HttpHeaders; 34 import java.net.http.HttpRequest; 35 import java.net.http.HttpResponse; 36 import java.nio.ByteBuffer; 37 import java.nio.charset.StandardCharsets; 38 import java.util.List; 39 import java.util.concurrent.CompletableFuture; 40 import java.util.concurrent.CompletionStage; 41 import java.util.concurrent.Flow; 42 43 /** 44 * @test 45 * @bug 8212926 46 * @summary Basic tests for response timeouts 47 * @run main/othervm LargeResponseContent 48 */ 49 50 public class LargeResponseContent { 51 final ServerSocket server; 52 final int port; 53 54 public LargeResponseContent() throws Exception { 55 server = new ServerSocket(0, 10, InetAddress.getLoopbackAddress()); 56 Thread serverThread = new Thread(this::handleConnection); 57 serverThread.setDaemon(false); 58 port = server.getLocalPort(); 59 serverThread.start(); 60 } 61 62 final String serverAuthority() { 63 return InetAddress.getLoopbackAddress().getHostName() + ":" 64 + server.getLocalPort(); 65 } 66 67 void runClient() throws IOException, InterruptedException { 68 URI uri = URI.create("http://" + serverAuthority() + "/foo"); 69 HttpClient client = HttpClient.newHttpClient(); 70 HttpRequest request = HttpRequest.newBuilder(uri) 71 .GET() 72 .build(); 73 HttpResponse<Long> response = client.send(request, new ClientHandler()); 74 System.out.println("Response code = " + response.statusCode()); 75 long blen = response.body(); 76 if (blen != CONTENT_LEN) 77 throw new RuntimeException("wrong content length"); 78 } 79 80 public static void main(String[] args) throws Exception { 81 System.out.println ("CONTENT_LEN = " + CONTENT_LEN); 82 System.out.println ("CLEN_STR = " + CLEN_STR); 83 LargeResponseContent test = new LargeResponseContent(); 84 test.runClient(); 85 } 86 87 static class ClientHandler implements HttpResponse.BodyHandler<Long> { 88 89 @Override 90 public HttpResponse.BodySubscriber<Long> apply(HttpResponse.ResponseInfo responseInfo) { 91 HttpHeaders headers = responseInfo.headers(); 92 headers.firstValue("content-length"); 93 long clen = headers.firstValueAsLong("content-length").orElse(-1); 94 if (clen != CONTENT_LEN) 95 return new Subscriber(new RuntimeException("Wrong content length received")); 96 return new Subscriber(null); 97 } 98 } 99 100 static class Subscriber implements HttpResponse.BodySubscriber<Long> { 101 final CompletableFuture<Long> cf = new CompletableFuture<>(); 102 volatile Flow.Subscription subscription; 103 volatile long counter = 0; 104 105 Subscriber(Throwable t) { 106 if (t != null) 107 cf.completeExceptionally(t); 108 } 109 110 @Override 111 public CompletionStage<Long> getBody() { 112 return cf; 113 } 114 115 @Override 116 public void onSubscribe(Flow.Subscription subscription) { 117 this.subscription = subscription; 118 subscription.request(Long.MAX_VALUE); 119 } 120 121 @Override 122 public void onNext(List<ByteBuffer> item) { 123 long v = 0; 124 for (ByteBuffer b : item) 125 v+= b.remaining(); 126 counter += v; 127 } 128 129 @Override 130 public void onError(Throwable throwable) { 131 throwable.printStackTrace(); 132 } 133 134 @Override 135 public void onComplete() { 136 cf.complete(counter); 137 } 138 } 139 140 static final long CONTENT_LEN = Integer.MAX_VALUE + 1000L; 141 static final String CLEN_STR = Long.valueOf(CONTENT_LEN).toString(); 142 143 static String RESPONSE = "HTTP/1.1 200 OK\r\n" + 144 "Content-length: " + CLEN_STR + "\r\n" + 145 "\r\n"; 146 147 148 void readHeaders(InputStream is) throws IOException { 149 String s = ""; 150 byte[] buf = new byte[128]; 151 while (!s.endsWith("\r\n\r\n")) { 152 int c = is.read(buf); 153 String f = new String(buf, 0, c, StandardCharsets.ISO_8859_1); 154 s = s + f; 155 } 156 } 157 158 public void handleConnection() { 159 long remaining = CONTENT_LEN; 160 try { 161 Socket socket = server.accept(); 162 InputStream is = socket.getInputStream(); 163 readHeaders(is); // read first byte 164 OutputStream os = socket.getOutputStream(); 165 os.write(RESPONSE.getBytes()); 166 byte[] buf = new byte[64 * 1024]; 167 while (remaining > 0) { 168 int amount = (int)Math.min(remaining, buf.length); 169 os.write(buf, 0, amount); 170 remaining -= amount; 171 } 172 System.out.println("Server: finished writing"); 173 os.close(); 174 175 } catch (IOException e) { 176 long sent = CONTENT_LEN - remaining; 177 System.out.println("Sent " + sent); 178 e.printStackTrace(); 179 } 180 } 181 } 182