Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/jdk/com/sun/net/httpserver/bugs/HandlerConnectionClose.java
41155 views
1
/*
2
* Copyright (c) 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
/**
25
* @test
26
* @bug 8218554
27
* @summary test that the handler can request a connection close.
28
* @library /test/lib
29
* @build jdk.test.lib.net.SimpleSSLContext
30
* @run main/othervm HandlerConnectionClose
31
*/
32
33
import com.sun.net.httpserver.HttpExchange;
34
import com.sun.net.httpserver.HttpHandler;
35
import com.sun.net.httpserver.HttpServer;
36
import com.sun.net.httpserver.HttpsConfigurator;
37
import com.sun.net.httpserver.HttpsServer;
38
39
import java.io.ByteArrayOutputStream;
40
import java.io.IOException;
41
import java.io.InputStream;
42
import java.io.OutputStream;
43
import java.net.HttpURLConnection;
44
import java.net.InetAddress;
45
import java.net.InetSocketAddress;
46
import java.net.Socket;
47
import java.net.URI;
48
import java.net.URL;
49
import java.nio.charset.StandardCharsets;
50
import java.util.List;
51
import java.util.Locale;
52
import java.util.logging.Handler;
53
import java.util.logging.Level;
54
import java.util.logging.Logger;
55
import java.util.logging.SimpleFormatter;
56
import java.util.logging.StreamHandler;
57
import jdk.test.lib.net.SimpleSSLContext;
58
import javax.net.ssl.HttpsURLConnection;
59
import javax.net.ssl.SSLContext;
60
import javax.net.ssl.SSLSession;
61
62
public class HandlerConnectionClose
63
{
64
static final int ONEK = 1024;
65
static final long POST_SIZE = ONEK * 1L;
66
SSLContext sslContext;
67
Logger logger;
68
69
void test(String[] args) throws Exception {
70
71
HttpServer httpServer = startHttpServer("http");
72
try {
73
testHttpURLConnection(httpServer, "http","/close/legacy/http/chunked");
74
testHttpURLConnection(httpServer, "http","/close/legacy/http/fixed");
75
testPlainSocket(httpServer, "http","/close/plain/http/chunked");
76
testPlainSocket(httpServer, "http","/close/plain/http/fixed");
77
} finally {
78
httpServer.stop(0);
79
}
80
sslContext = new SimpleSSLContext().get();
81
HttpServer httpsServer = startHttpServer("https");
82
try {
83
testHttpURLConnection(httpsServer, "https","/close/legacy/https/chunked");
84
testHttpURLConnection(httpsServer, "https","/close/legacy/https/fixed");
85
testPlainSocket(httpsServer, "https","/close/plain/https/chunked");
86
testPlainSocket(httpsServer, "https","/close/plain/https/fixed");
87
} finally{
88
httpsServer.stop(0);
89
}
90
}
91
92
void testHttpURLConnection(HttpServer httpServer, String protocol, String path) throws Exception {
93
int port = httpServer.getAddress().getPort();
94
String host = httpServer.getAddress().getHostString();
95
URL url = new URI(protocol, null, host, port, path, null, null).toURL();
96
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
97
if ("https".equalsIgnoreCase(protocol)) {
98
((HttpsURLConnection)uc).setSSLSocketFactory(sslContext.getSocketFactory());
99
((HttpsURLConnection)uc).setHostnameVerifier((String hostname, SSLSession session) -> true);
100
}
101
uc.setDoOutput(true);
102
uc.setRequestMethod("POST");
103
uc.setFixedLengthStreamingMode(POST_SIZE);
104
OutputStream os = uc.getOutputStream();
105
106
/* create a 1K byte array with data to POST */
107
byte[] ba = new byte[ONEK];
108
for (int i = 0; i < ONEK; i++)
109
ba[i] = (byte) i;
110
111
System.out.println("\n" + uc.getClass().getSimpleName() +": POST " + url + " HTTP/1.1");
112
long times = POST_SIZE / ONEK;
113
for (int i = 0; i < times; i++) {
114
os.write(ba);
115
}
116
117
os.close();
118
InputStream is = uc.getInputStream();
119
int read;
120
long count = 0;
121
while ((read = is.read(ba)) != -1) {
122
for (int i = 0; i < read; i++) {
123
byte expected = (byte) count++;
124
if (ba[i] != expected) {
125
throw new IOException("byte mismatch at "
126
+ (count - 1) + ": expected " + expected + " got " + ba[i]);
127
}
128
}
129
}
130
if (count != POST_SIZE) {
131
throw new IOException("Unexpected length: " + count + " expected " + POST_SIZE);
132
}
133
is.close();
134
135
pass();
136
}
137
138
void testPlainSocket(HttpServer httpServer, String protocol, String path) throws Exception {
139
int port = httpServer.getAddress().getPort();
140
String host = httpServer.getAddress().getHostString();
141
URL url = new URI(protocol, null, host, port, path, null, null).toURL();
142
Socket socket;
143
if ("https".equalsIgnoreCase(protocol)) {
144
socket = sslContext.getSocketFactory().createSocket(host, port);
145
} else {
146
socket = new Socket(host, port);
147
}
148
try (Socket sock = socket) {
149
OutputStream os = socket.getOutputStream();
150
151
// send request headers
152
String request = new StringBuilder()
153
.append("POST ").append(path).append(" HTTP/1.1").append("\r\n")
154
.append("host: ").append(host).append(':').append(port).append("\r\n")
155
.append("Content-Length: ").append(POST_SIZE).append("\r\n")
156
.append("\r\n")
157
.toString();
158
os.write(request.getBytes(StandardCharsets.US_ASCII));
159
160
/* create a 1K byte array with data to POST */
161
byte[] ba = new byte[ONEK];
162
for (int i = 0; i < ONEK; i++)
163
ba[i] = (byte) i;
164
165
// send request data
166
long times = POST_SIZE / ONEK;
167
for (int i = 0; i < times; i++) {
168
os.write(ba);
169
}
170
os.flush();
171
172
InputStream is = socket.getInputStream();
173
ByteArrayOutputStream bos = new ByteArrayOutputStream();
174
175
// read all response headers
176
int c;
177
int crlf = 0;
178
while ((c = is.read()) != -1) {
179
if (c == '\r') continue;
180
if (c == '\n') crlf++;
181
else crlf = 0;
182
bos.write(c);
183
if (crlf == 2) break;
184
}
185
String responseHeadersStr = bos.toString(StandardCharsets.US_ASCII);
186
List<String> responseHeaders = List.of(responseHeadersStr.split("\n"));
187
System.out.println("\nPOST " + url + " HTTP/1.1");
188
responseHeaders.stream().forEach(s -> System.out.println("[reply]\t" + s));
189
String statusLine = responseHeaders.get(0);
190
if (!statusLine.startsWith("HTTP/1.1 200 "))
191
throw new IOException("Unexpected status: " + statusLine);
192
String cl = responseHeaders.stream()
193
.map(s -> s.toLowerCase(Locale.ROOT))
194
.filter(s -> s.startsWith("content-length: "))
195
.findFirst()
196
.orElse(null);
197
String te = responseHeaders.stream()
198
.map(s -> s.toLowerCase(Locale.ROOT))
199
.filter(s -> s.startsWith("transfer-encoding: "))
200
.findFirst()
201
.orElse(null);
202
203
// check content-length and transfer-encoding are as expected
204
int read = 0;
205
long count = 0;
206
if (path.endsWith("/fixed")) {
207
if (!("content-length: " + POST_SIZE).equalsIgnoreCase(cl)) {
208
throw new IOException("Unexpected Content-Length: [" + cl + "]");
209
}
210
if (te != null) {
211
throw new IOException("Unexpected Transfer-Encoding: [" + te + "]");
212
}
213
// Got expected Content-Length: 1024 - read response data
214
while ((read = is.read()) != -1) {
215
int expected = (int) (count & 0xFF);
216
if ((read & 0xFF) != expected) {
217
throw new IOException("byte mismatch at "
218
+ (count - 1) + ": expected " + expected + " got " + read);
219
}
220
if (++count == POST_SIZE) break;
221
}
222
} else if (cl != null) {
223
throw new IOException("Unexpected Content-Length: [" + cl + "]");
224
} else {
225
if (!("transfer-encoding: chunked").equalsIgnoreCase(te)) {
226
throw new IOException("Unexpected Transfer-Encoding: [" + te + "]");
227
}
228
// This is a quick & dirty implementation of
229
// chunk decoding - no trailers - no extensions
230
StringBuilder chunks = new StringBuilder();
231
int cs = -1;
232
while (cs != 0) {
233
cs = 0;
234
chunks.setLength(0);
235
236
// read next chunk length
237
while ((read = is.read()) != -1) {
238
if (read == '\r') continue;
239
if (read == '\n') break;
240
chunks.append((char) read);
241
}
242
cs = Integer.parseInt(chunks.toString().trim(), 16);
243
System.out.println("Got chunk length: " + cs);
244
245
// If chunk size is 0, then we have read the last chunk.
246
if (cs == 0) break;
247
248
// Read the chunk data
249
while (--cs >= 0) {
250
read = is.read();
251
if (read == -1) break; // EOF
252
int expected = (int) (count & 0xFF);
253
if ((read & 0xFF) != expected) {
254
throw new IOException("byte mismatch at "
255
+ (count - 1) + ": expected " + expected + " got " + read);
256
}
257
// This is cheating: we know the size :-)
258
if (++count == POST_SIZE) break;
259
}
260
261
if (read == -1) {
262
throw new IOException("Unexpected EOF after " + count + " data bytes");
263
}
264
265
// read CRLF
266
if ((read = is.read()) != '\r') {
267
throw new IOException("Expected CR at " + count + "after chunk data - got " + read);
268
}
269
if ((read = is.read()) != '\n') {
270
throw new IOException("Expected LF at " + count + "after chunk data - got " + read);
271
}
272
273
if (cs == 0 && count == POST_SIZE) {
274
cs = -1;
275
}
276
277
if (cs != -1) {
278
// count == POST_SIZE, but some chunk data still to be read?
279
throw new IOException("Unexpected chunk size, "
280
+ cs + " bytes still to read after " + count +
281
" data bytes received.");
282
}
283
}
284
// Last CRLF?
285
for (int i = 0; i < 2; i++) {
286
if ((read = is.read()) == -1) break;
287
}
288
}
289
290
if (count != POST_SIZE) {
291
throw new IOException("Unexpected length: " + count + " expected " + POST_SIZE);
292
}
293
294
if (!sock.isClosed()) {
295
try {
296
// We send an end request to the server to verify that the
297
// connection is closed. If the server has not closed the
298
// connection, it will reply. If we receive a response,
299
// we should fail...
300
String endrequest = new StringBuilder()
301
.append("GET ").append("/close/end").append(" HTTP/1.1").append("\r\n")
302
.append("host: ").append(host).append(':').append(port).append("\r\n")
303
.append("Content-Length: ").append(0).append("\r\n")
304
.append("\r\n")
305
.toString();
306
os.write(endrequest.getBytes(StandardCharsets.US_ASCII));
307
os.flush();
308
StringBuilder resp = new StringBuilder();
309
crlf = 0;
310
311
// read all headers.
312
// If the server closed the connection as expected
313
// we should immediately read EOF
314
while ((read = is.read()) != -1) {
315
if (read == '\r') continue;
316
if (read == '\n') crlf++;
317
else crlf = 0;
318
if (crlf == 2) break;
319
resp.append((char) read);
320
}
321
322
List<String> lines = List.of(resp.toString().split("\n"));
323
if (read != -1 || resp.length() != 0) {
324
System.err.println("Connection not closed!");
325
System.err.println("Got: ");
326
lines.stream().forEach(s -> System.err.println("[end]\t" + s));
327
throw new AssertionError("EOF not received after " + count + " data bytes");
328
}
329
if (read != -1) {
330
throw new AssertionError("EOF was expected after " + count + " bytes, but got: " + read);
331
} else {
332
System.out.println("Got expected EOF (" + read + ")");
333
}
334
} catch (IOException x) {
335
// expected! all is well
336
System.out.println("Socket closed as expected, got exception writing to it.");
337
}
338
} else {
339
System.out.println("Socket closed as expected");
340
}
341
pass();
342
}
343
}
344
345
/**
346
* Http Server
347
*/
348
HttpServer startHttpServer(String protocol) throws IOException {
349
if (debug) {
350
logger = Logger.getLogger("com.sun.net.httpserver");
351
Handler outHandler = new StreamHandler(System.out,
352
new SimpleFormatter());
353
outHandler.setLevel(Level.FINEST);
354
logger.setLevel(Level.FINEST);
355
logger.addHandler(outHandler);
356
}
357
358
InetSocketAddress serverAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
359
HttpServer httpServer = null;
360
if ("http".equalsIgnoreCase(protocol)) {
361
httpServer = HttpServer.create(serverAddress, 0);
362
}
363
if ("https".equalsIgnoreCase(protocol)) {
364
HttpsServer httpsServer = HttpsServer.create(serverAddress, 0);
365
httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));
366
httpServer = httpsServer;
367
}
368
httpServer.createContext("/close/", new MyHandler(POST_SIZE));
369
System.out.println("Server created at: " + httpServer.getAddress());
370
httpServer.start();
371
return httpServer;
372
}
373
374
class MyHandler implements HttpHandler {
375
static final int BUFFER_SIZE = 512;
376
final long expected;
377
378
MyHandler(long expected){
379
this.expected = expected;
380
}
381
382
@Override
383
public void handle(HttpExchange t) throws IOException {
384
System.out.println("Server: serving " + t.getRequestURI());
385
boolean chunked = t.getRequestURI().getPath().endsWith("/chunked");
386
boolean fixed = t.getRequestURI().getPath().endsWith("/fixed");
387
boolean end = t.getRequestURI().getPath().endsWith("/end");
388
long responseLength = fixed ? POST_SIZE : 0;
389
responseLength = end ? -1 : responseLength;
390
responseLength = chunked ? 0 : responseLength;
391
392
if (!end) t.getResponseHeaders().add("connection", "CLose");
393
t.sendResponseHeaders(200, responseLength);
394
395
if (!end) {
396
OutputStream os = t.getResponseBody();
397
InputStream is = t.getRequestBody();
398
byte[] ba = new byte[BUFFER_SIZE];
399
int read;
400
long count = 0L;
401
while ((read = is.read(ba)) != -1) {
402
count += read;
403
os.write(ba, 0, read);
404
}
405
is.close();
406
407
check(count == expected, "Expected: " + expected + ", received "
408
+ count);
409
debug("Received " + count + " bytes");
410
os.close();
411
}
412
413
t.close();
414
}
415
}
416
417
//--------------------- Infrastructure ---------------------------
418
boolean debug = true;
419
volatile int passed = 0, failed = 0;
420
void pass() {passed++;}
421
void fail() {failed++; Thread.dumpStack();}
422
void fail(String msg) {System.err.println(msg); fail();}
423
void unexpected(Throwable t) {failed++; t.printStackTrace();}
424
void check(boolean cond) {if (cond) pass(); else fail();}
425
void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
426
void debug(String message) {if(debug) { System.out.println(message); } }
427
public static void main(String[] args) throws Throwable {
428
Class<?> k = new Object(){}.getClass().getEnclosingClass();
429
try {k.getMethod("instanceMain",String[].class)
430
.invoke( k.newInstance(), (Object) args);}
431
catch (Throwable e) {throw e.getCause();}}
432
public void instanceMain(String[] args) throws Throwable {
433
try {test(args);} catch (Throwable t) {unexpected(t);}
434
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
435
if (failed > 0) throw new AssertionError("Some tests failed");}
436
437
}
438
439