1 /* 2 * Copyright (c) 2015, 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.net.URI; 26 import java.util.concurrent.CompletableFuture; 27 import javax.net.ssl.SSLContext; 28 import javax.net.ServerSocketFactory; 29 import javax.net.ssl.SSLServerSocketFactory; 30 import jdk.incubator.http.HttpClient; 31 import jdk.incubator.http.HttpClient.Version; 32 import jdk.incubator.http.HttpRequest; 33 import jdk.incubator.http.HttpResponse; 34 import jdk.testlibrary.SimpleSSLContext; 35 import static java.lang.System.out; 36 import static java.lang.String.format; 37 import static jdk.incubator.http.HttpResponse.BodyHandler.asString; 38 39 /** 40 * @test 41 * @bug 8087112 42 * @library /lib/testlibrary 43 * @build jdk.testlibrary.SimpleSSLContext 44 * @build MockServer 45 * @run main/othervm -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=all SplitResponse 46 */ 47 48 /** 49 * Similar test to QuickResponses except that each byte of the response 50 * is sent in a separate packet, which tests the stability of the implementation 51 * for receiving unusual packet sizes. Additionally, tests scenarios there 52 * connections that are retrieved from the connection pool may reach EOF before 53 * being reused. 54 */ 55 public class SplitResponse { 56 57 static String response(String body, boolean serverKeepalive) { 58 StringBuilder sb = new StringBuilder(); 59 sb.append("HTTP/1.1 200 OK\r\n"); 60 if (!serverKeepalive) 61 sb.append("Connection: Close\r\n"); 62 63 sb.append("Content-length: ").append(body.length()).append("\r\n"); 64 sb.append("\r\n"); 65 sb.append(body); 66 return sb.toString(); 67 } 68 69 static final String responses[] = { 70 "Lorem ipsum", 71 "dolor sit amet", 72 "consectetur adipiscing elit, sed do eiusmod tempor", 73 "quis nostrud exercitation ullamco", 74 "laboris nisi", 75 "ut", 76 "aliquip ex ea commodo consequat." + 77 "Duis aute irure dolor in reprehenderit in voluptate velit esse" + 78 "cillum dolore eu fugiat nulla pariatur.", 79 "Excepteur sint occaecat cupidatat non proident." 80 }; 81 82 final ServerSocketFactory factory; 83 final SSLContext context; 84 final boolean useSSL; 85 SplitResponse(boolean useSSL) throws IOException { 86 this.useSSL = useSSL; 87 context = new SimpleSSLContext().get(); 88 SSLContext.setDefault(context); 89 factory = useSSL ? SSLServerSocketFactory.getDefault() 90 : ServerSocketFactory.getDefault(); 91 } 92 93 public HttpClient newHttpClient() { 94 HttpClient client; 95 if (useSSL) { 96 client = HttpClient.newBuilder() 97 .sslContext(context) 98 .build(); 99 } else { 100 client = HttpClient.newHttpClient(); 101 } 102 return client; 103 } 104 105 public static void main(String[] args) throws Exception { 106 boolean useSSL = false; 107 if (args != null && args.length == 1) { 108 useSSL = "SSL".equals(args[0]); 109 } 110 SplitResponse sp = new SplitResponse(useSSL); 111 112 for (Version version : Version.values()) { 113 for (boolean serverKeepalive : new boolean[]{ true, false }) { 114 // Note: the mock server doesn't support Keep-Alive, but 115 // pretending that it might exercises code paths in and out of 116 // the connection pool, and retry logic 117 for (boolean async : new boolean[]{ true, false }) { 118 sp.test(version, serverKeepalive, async); 119 } 120 } 121 } 122 } 123 124 // @Test 125 void test(Version version, boolean serverKeepalive, boolean async) 126 throws Exception 127 { 128 out.println(format("*** version %s, serverKeepAlive: %s, async: %s ***", 129 version, serverKeepalive, async)); 130 MockServer server = new MockServer(0, factory); 131 URI uri = new URI(server.getURL()); 132 out.println("server is: " + uri); 133 server.start(); 134 135 HttpClient client = newHttpClient(); 136 HttpRequest request = HttpRequest.newBuilder(uri).version(version).build(); 137 HttpResponse<String> r; 138 CompletableFuture<HttpResponse<String>> cf1; 139 140 try { 141 for (int i=0; i<responses.length; i++) { 142 out.println("----- iteration " + i + " -----"); 143 String body = responses[i]; 144 Thread t = sendSplitResponse(response(body, serverKeepalive), server); 145 146 if (async) { 147 out.println("send async: " + request); 148 cf1 = client.sendAsync(request, asString()); 149 r = cf1.get(); 150 } else { // sync 151 out.println("send sync: " + request); 152 r = client.send(request, asString()); 153 } 154 155 if (r.statusCode() != 200) 156 throw new RuntimeException("Failed"); 157 158 String rxbody = r.body(); 159 out.println("received " + rxbody); 160 if (!rxbody.equals(body)) 161 throw new RuntimeException(format("Expected:%s, got:%s", body, rxbody)); 162 163 t.join(); 164 conn.close(); 165 } 166 } finally { 167 server.close(); 168 } 169 System.out.println("OK"); 170 } 171 172 // required for cleanup 173 volatile MockServer.Connection conn; 174 175 // Sends the response, mostly, one byte at a time with a small delay 176 // between bytes, to encourage that each byte is read in a separate read 177 Thread sendSplitResponse(String s, MockServer server) { 178 System.out.println("Sending: "); 179 Thread t = new Thread(() -> { 180 System.out.println("Waiting for server to receive headers"); 181 conn = server.activity(); 182 System.out.println("Start sending response"); 183 184 try { 185 int len = s.length(); 186 out.println("sending " + s); 187 for (int i = 0; i < len; i++) { 188 String onechar = s.substring(i, i + 1); 189 conn.send(onechar); 190 Thread.sleep(10); 191 } 192 out.println("sent " + s); 193 } catch (IOException | InterruptedException e) { 194 throw new RuntimeException(e); 195 } 196 }); 197 t.setDaemon(true); 198 t.start(); 199 return t; 200 } 201 }