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.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 void runClient() throws IOException, InterruptedException { 63 URI uri = URI.create("http://127.0.0.1:" + Integer.toString(port) + "/foo"); 64 HttpClient client = HttpClient.newHttpClient(); 65 HttpRequest request = HttpRequest.newBuilder(uri) 66 .GET() 67 .build(); 68 HttpResponse<Long> response = client.send(request, new ClientHandler()); 69 System.out.println("Response code = " + response.statusCode()); 70 long blen = response.body(); 71 if (blen != CONTENT_LEN) 72 throw new RuntimeException("wrong content length"); 73 } 74 75 public static void main(String[] args) throws Exception { 76 System.out.println ("CONTENT_LEN = " + CONTENT_LEN); 77 System.out.println ("CLEN_STR = " + CLEN_STR); 78 LargeResponseContent test = new LargeResponseContent(); 79 test.runClient(); 80 } 81 82 static class ClientHandler implements HttpResponse.BodyHandler<Long> { 83 84 @Override 85 public HttpResponse.BodySubscriber<Long> apply(HttpResponse.ResponseInfo responseInfo) { 86 HttpHeaders headers = responseInfo.headers(); 87 headers.firstValue("content-length"); 88 long clen = headers.firstValueAsLong("content-length").orElse(-1); 89 if (clen != CONTENT_LEN) 90 return new Subscriber(new RuntimeException("Wrong content length received")); 91 return new Subscriber(null); 92 } 93 } 94 95 static class Subscriber implements HttpResponse.BodySubscriber<Long> { 96 final CompletableFuture<Long> cf = new CompletableFuture<>(); 97 volatile Flow.Subscription subscription; 98 volatile long counter = 0; 99 100 Subscriber(Throwable t) { 101 if (t != null) 102 cf.completeExceptionally(t); 103 } 104 105 @Override 106 public CompletionStage<Long> getBody() { 107 return cf; 108 } 109 110 @Override 111 public void onSubscribe(Flow.Subscription subscription) { 112 this.subscription = subscription; 113 subscription.request(Long.MAX_VALUE); 114 } 115 116 @Override 117 public void onNext(List<ByteBuffer> item) { 118 long v = 0; 119 for (ByteBuffer b : item) 120 v+= b.remaining(); 121 counter += v; 122 } 123 124 @Override 125 public void onError(Throwable throwable) { 126 throwable.printStackTrace(); 127 } 128 129 @Override 130 public void onComplete() { 131 cf.complete(counter); 132 } 133 } 134 135 static final long CONTENT_LEN = Integer.MAX_VALUE + 1000L; 136 static final String CLEN_STR = Long.valueOf(CONTENT_LEN).toString(); 137 138 static String RESPONSE = "HTTP/1.1 200 OK\r\n" + 139 "Content-length: " + CLEN_STR + "\r\n" + 140 "\r\n"; 141 142 143 void readHeaders(InputStream is) throws IOException { 144 String s = ""; 145 byte[] buf = new byte[128]; 146 while (!s.endsWith("\r\n\r\n")) { 147 int c = is.read(buf); 148 String f = new String(buf, 0, c, StandardCharsets.ISO_8859_1); 149 s = s + f; 150 } 151 } 152 153 public void handleConnection() { 154 long remaining = CONTENT_LEN; 155 try { 156 Socket socket = server.accept(); 157 InputStream is = socket.getInputStream(); 158 readHeaders(is); // read first byte 159 OutputStream os = socket.getOutputStream(); 160 os.write(RESPONSE.getBytes()); 161 byte[] buf = new byte[64 * 1024]; 162 while (remaining > 0) { 163 int amount = (int)Math.min(remaining, buf.length); 164 os.write(buf, 0, amount); 165 remaining -= amount; 166 } 167 System.out.println("Server: finished writing"); 168 os.close(); 169 170 } catch (IOException e) { 171 long sent = CONTENT_LEN - remaining; 172 System.out.println("Sent " + sent); 173 e.printStackTrace(); 174 } 175 } 176 } 177