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