Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/jdk/java/net/httpclient/CookieHeaderTest.java
41149 views
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
/*
25
* @test
26
* @bug 8199851
27
* @summary Test for multiple vs single cookie header for HTTP/2 vs HTTP/1.1
28
* @modules java.base/sun.net.www.http
29
* java.net.http/jdk.internal.net.http.common
30
* java.net.http/jdk.internal.net.http.frame
31
* java.net.http/jdk.internal.net.http.hpack
32
* java.logging
33
* jdk.httpserver
34
* @library /test/lib http2/server
35
* @build Http2TestServer
36
* @build jdk.test.lib.net.SimpleSSLContext
37
* @run testng/othervm
38
* -Djdk.tls.acknowledgeCloseNotify=true
39
* -Djdk.httpclient.HttpClient.log=trace,headers,requests
40
* CookieHeaderTest
41
*/
42
43
import com.sun.net.httpserver.HttpServer;
44
import com.sun.net.httpserver.HttpsConfigurator;
45
import com.sun.net.httpserver.HttpsServer;
46
import jdk.test.lib.net.SimpleSSLContext;
47
import org.testng.annotations.AfterTest;
48
import org.testng.annotations.BeforeTest;
49
import org.testng.annotations.DataProvider;
50
import org.testng.annotations.Test;
51
52
import javax.net.ServerSocketFactory;
53
import javax.net.ssl.SSLContext;
54
import java.io.IOException;
55
import java.io.InputStream;
56
import java.io.OutputStream;
57
import java.io.OutputStreamWriter;
58
import java.io.PrintWriter;
59
import java.io.Writer;
60
import java.net.CookieHandler;
61
import java.net.CookieManager;
62
import java.net.InetAddress;
63
import java.net.InetSocketAddress;
64
import java.net.ServerSocket;
65
import java.net.Socket;
66
import java.net.URI;
67
import java.net.http.HttpClient;
68
import java.net.http.HttpClient.Redirect;
69
import java.net.http.HttpRequest;
70
import java.net.http.HttpResponse;
71
import java.net.http.HttpResponse.BodyHandlers;
72
import java.util.ArrayList;
73
import java.util.Arrays;
74
import java.util.Collections;
75
import java.util.HashMap;
76
import java.util.List;
77
import java.util.Locale;
78
import java.util.Map;
79
import java.util.StringTokenizer;
80
import java.util.concurrent.ConcurrentHashMap;
81
import java.util.concurrent.ConcurrentLinkedQueue;
82
import java.util.concurrent.atomic.AtomicLong;
83
import java.util.stream.Collectors;
84
import java.util.stream.Stream;
85
86
import static java.lang.System.out;
87
import static java.nio.charset.StandardCharsets.UTF_8;
88
import static org.testng.Assert.assertEquals;
89
import static org.testng.Assert.assertTrue;
90
91
public class CookieHeaderTest implements HttpServerAdapters {
92
93
SSLContext sslContext;
94
HttpTestServer httpTestServer; // HTTP/1.1 [ 6 servers ]
95
HttpTestServer httpsTestServer; // HTTPS/1.1
96
HttpTestServer http2TestServer; // HTTP/2 ( h2c )
97
HttpTestServer https2TestServer; // HTTP/2 ( h2 )
98
DummyServer httpDummyServer;
99
DummyServer httpsDummyServer;
100
String httpURI;
101
String httpsURI;
102
String http2URI;
103
String https2URI;
104
String httpDummy;
105
String httpsDummy;
106
107
static final String MESSAGE = "Basic CookieHeaderTest message body";
108
static final int ITERATIONS = 3;
109
static final long start = System.nanoTime();
110
public static String now() {
111
long now = System.nanoTime() - start;
112
long secs = now / 1000_000_000;
113
long mill = (now % 1000_000_000) / 1000_000;
114
long nan = now % 1000_000;
115
return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan);
116
}
117
118
@DataProvider(name = "positive")
119
public Object[][] positive() {
120
return new Object[][] {
121
{ httpURI, HttpClient.Version.HTTP_1_1 },
122
{ httpsURI, HttpClient.Version.HTTP_1_1 },
123
{ httpDummy, HttpClient.Version.HTTP_1_1 },
124
{ httpsDummy, HttpClient.Version.HTTP_1_1 },
125
{ httpURI, HttpClient.Version.HTTP_2 },
126
{ httpsURI, HttpClient.Version.HTTP_2 },
127
{ httpDummy, HttpClient.Version.HTTP_2 },
128
{ httpsDummy, HttpClient.Version.HTTP_2 },
129
{ http2URI, null },
130
{ https2URI, null },
131
};
132
}
133
134
static final AtomicLong requestCounter = new AtomicLong();
135
136
@Test(dataProvider = "positive")
137
void test(String uriString, HttpClient.Version version) throws Exception {
138
out.printf("%n---- starting (%s) ----%n", uriString);
139
ConcurrentHashMap<String, List<String>> cookieHeaders
140
= new ConcurrentHashMap<>();
141
CookieHandler cookieManager = new TestCookieHandler(cookieHeaders);
142
HttpClient client = HttpClient.newBuilder()
143
.followRedirects(Redirect.ALWAYS)
144
.cookieHandler(cookieManager)
145
.sslContext(sslContext)
146
.build();
147
assert client.cookieHandler().isPresent();
148
149
URI uri = URI.create(uriString);
150
List<String> cookies = new ArrayList<>();
151
cookies.add("CUSTOMER=ARTHUR_DENT");
152
cookies.add("LOCATION=TR\u0100IN_STATION");
153
cookies.add("LOC\u0100TION=TRAIN_STATION");
154
cookies.add("ORDER=BISCUITS");
155
cookieHeaders.put("Cookie", cookies);
156
157
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(uri)
158
.header("X-uuid", "uuid-" + requestCounter.incrementAndGet());
159
if (version != null) {
160
requestBuilder.version(version);
161
}
162
HttpRequest request = requestBuilder.build();
163
out.println("Initial request: " + request.uri());
164
165
for (int i=0; i< ITERATIONS; i++) {
166
out.println("iteration: " + i);
167
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
168
169
out.println(" Got response: " + response);
170
out.println(" Got body Path: " + response.body());
171
172
assertEquals(response.statusCode(), 200);
173
assertEquals(response.body(), MESSAGE);
174
assertEquals(response.headers().allValues("X-Request-Cookie"),
175
cookies.stream()
176
.filter(s -> !s.startsWith("LOC"))
177
.collect(Collectors.toList()));
178
requestBuilder = HttpRequest.newBuilder(uri)
179
.header("X-uuid", "uuid-" + requestCounter.incrementAndGet());
180
if (version != null) {
181
requestBuilder.version(version);
182
}
183
request = requestBuilder.build();
184
}
185
}
186
187
// -- Infrastructure
188
189
@BeforeTest
190
public void setup() throws Exception {
191
sslContext = new SimpleSSLContext().get();
192
if (sslContext == null)
193
throw new AssertionError("Unexpected null sslContext");
194
195
InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
196
197
httpTestServer = HttpTestServer.of(HttpServer.create(sa, 0));
198
httpTestServer.addHandler(new CookieValidationHandler(), "/http1/cookie/");
199
httpURI = "http://" + httpTestServer.serverAuthority() + "/http1/cookie/retry";
200
HttpsServer httpsServer = HttpsServer.create(sa, 0);
201
httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));
202
httpsTestServer = HttpTestServer.of(httpsServer);
203
httpsTestServer.addHandler(new CookieValidationHandler(),"/https1/cookie/");
204
httpsURI = "https://" + httpsTestServer.serverAuthority() + "/https1/cookie/retry";
205
206
http2TestServer = HttpTestServer.of(new Http2TestServer("localhost", false, 0));
207
http2TestServer.addHandler(new CookieValidationHandler(), "/http2/cookie/");
208
http2URI = "http://" + http2TestServer.serverAuthority() + "/http2/cookie/retry";
209
https2TestServer = HttpTestServer.of(new Http2TestServer("localhost", true, sslContext));
210
https2TestServer.addHandler(new CookieValidationHandler(), "/https2/cookie/");
211
https2URI = "https://" + https2TestServer.serverAuthority() + "/https2/cookie/retry";
212
213
214
// DummyServer
215
httpDummyServer = DummyServer.create(sa);
216
httpsDummyServer = DummyServer.create(sa, sslContext);
217
httpDummy = "http://" + httpDummyServer.serverAuthority() + "/http1/dummy/x";
218
httpsDummy = "https://" + httpsDummyServer.serverAuthority() + "/https1/dummy/x";
219
220
httpTestServer.start();
221
httpsTestServer.start();
222
http2TestServer.start();
223
https2TestServer.start();
224
httpDummyServer.start();
225
httpsDummyServer.start();
226
}
227
228
@AfterTest
229
public void teardown() throws Exception {
230
httpTestServer.stop();
231
httpsTestServer.stop();
232
http2TestServer.stop();
233
https2TestServer.stop();
234
httpsDummyServer.stopServer();
235
httpsDummyServer.stopServer();
236
}
237
238
static class TestCookieHandler extends CookieHandler {
239
240
final ConcurrentHashMap<String, List<String>> cookies;
241
TestCookieHandler(ConcurrentHashMap<String, List<String>> map) {
242
this.cookies = map;
243
}
244
245
@Override
246
public Map<String, List<String>> get(URI uri, Map<String, List<String>> requestHeaders)
247
throws IOException
248
{
249
return cookies;
250
}
251
252
@Override
253
public void put(URI uri, Map<String, List<String>> responseHeaders)
254
throws IOException
255
{
256
// do nothing
257
}
258
}
259
260
static class CookieValidationHandler implements HttpTestHandler {
261
ConcurrentHashMap<String,String> closedRequests = new ConcurrentHashMap<>();
262
263
@Override
264
public void handle(HttpTestExchange t) throws IOException {
265
System.out.println("CookieValidationHandler for: " + t.getRequestURI());
266
267
List<String> uuids = t.getRequestHeaders().get("X-uuid");
268
if (uuids == null || uuids.size() != 1) {
269
readAllRequestData(t);
270
try (OutputStream os = t.getResponseBody()) {
271
String msg = "Incorrect uuid header values:[" + uuids + "]";
272
(new RuntimeException(msg)).printStackTrace();
273
t.sendResponseHeaders(500, -1);
274
os.write(msg.getBytes(UTF_8));
275
}
276
return;
277
}
278
279
String uuid = uuids.get(0);
280
// retrying
281
if (closedRequests.putIfAbsent(uuid, t.getRequestURI().toString()) == null) {
282
if (t.getExchangeVersion() == HttpClient.Version.HTTP_1_1) {
283
// Throwing an exception here only causes a retry
284
// with HTTP_1_1 - where it forces the server to close
285
// the connection.
286
// For HTTP/2 then throwing an IOE would cause the server
287
// to close the stream, and throwing anything else would
288
// cause it to close the connection, but neither would
289
// cause the client to retry.
290
// So we simply do not try to retry with HTTP/2 and just verify
291
// we have received the expected cookie
292
throw new IOException("Closing on first request");
293
}
294
}
295
296
// Check whether this request was upgraded.
297
// An upgraded request will have a version of HTTP_2 and
298
// an Upgrade: h2c header
299
HttpClient.Version version = t.getExchangeVersion();
300
List<String> upgrade = t.getRequestHeaders().get("Upgrade");
301
if (upgrade == null) upgrade = List.of();
302
boolean upgraded = version == HttpClient.Version.HTTP_2
303
&& upgrade.stream().anyMatch("h2c"::equalsIgnoreCase);
304
305
// not retrying
306
readAllRequestData(t);
307
try (OutputStream os = t.getResponseBody()) {
308
List<String> cookie = t.getRequestHeaders().get("Cookie");
309
if (cookie != null) {
310
if (version == HttpClient.Version.HTTP_1_1 || upgraded) {
311
if (cookie.size() == 1) {
312
cookie = List.of(cookie.get(0).split("; "));
313
} else if (cookie.size() > 1) {
314
String msg = "Found multiple 'Cookie:' lines for version=%s (upgraded=%s): %s";
315
msg = String.format(msg, version, upgraded, cookie);
316
(new RuntimeException(msg)).printStackTrace();
317
t.sendResponseHeaders(500, -1);
318
os.write(msg.getBytes(UTF_8));
319
return;
320
}
321
}
322
Collections.sort(cookie = new ArrayList<String>(cookie));
323
}
324
if (cookie == null || cookie.size() == 0) {
325
String msg = "No cookie header present";
326
(new RuntimeException(msg)).printStackTrace();
327
t.sendResponseHeaders(500, -1);
328
os.write(msg.getBytes(UTF_8));
329
} else if (!cookie.get(0).equals("CUSTOMER=ARTHUR_DENT")) {
330
String msg = "Incorrect cookie header value:[" + cookie.get(0) + "]";
331
(new RuntimeException(msg)).printStackTrace();
332
t.sendResponseHeaders(500, -1);
333
os.write(msg.getBytes(UTF_8));
334
} else if (cookie.size() == 2 && !cookie.get(1).equals("ORDER=BISCUITS")) {
335
String msg = "Incorrect cookie header value:[" + cookie.get(0) + "]";
336
(new RuntimeException(msg)).printStackTrace();
337
t.sendResponseHeaders(500, -1);
338
os.write(msg.getBytes(UTF_8));
339
} else if (cookie.size() != 2) {
340
String msg = "Incorrect cookie header values:[" + cookie + "]";
341
(new RuntimeException(msg)).printStackTrace();
342
t.sendResponseHeaders(500, -1);
343
os.write(msg.getBytes(UTF_8));
344
} else {
345
assert cookie.get(0).equals("CUSTOMER=ARTHUR_DENT");
346
byte[] bytes = MESSAGE.getBytes(UTF_8);
347
for (String value : cookie) {
348
t.getResponseHeaders().addHeader("X-Request-Cookie", value);
349
}
350
t.sendResponseHeaders(200, bytes.length);
351
os.write(bytes);
352
}
353
} finally {
354
closedRequests.remove(uuid);
355
}
356
357
}
358
}
359
360
static void readAllRequestData(HttpTestExchange t) throws IOException {
361
try (InputStream is = t.getRequestBody()) {
362
is.readAllBytes();
363
}
364
}
365
366
static class DummyServer extends Thread {
367
final ServerSocket ss;
368
final boolean secure;
369
ConcurrentLinkedQueue<Socket> connections = new ConcurrentLinkedQueue<>();
370
volatile boolean stopped;
371
DummyServer(ServerSocket ss, boolean secure) {
372
super("DummyServer[" + ss.getLocalPort()+"]");
373
this.secure = secure;
374
this.ss = ss;
375
}
376
377
// This is a bit shaky. It doesn't handle continuation
378
// lines, but our client shouldn't send any.
379
// Read a line from the input stream, swallowing the final
380
// \r\n sequence. Stops at the first \n, doesn't complain
381
// if it wasn't preceded by '\r'.
382
//
383
String readLine(InputStream r) throws IOException {
384
StringBuilder b = new StringBuilder();
385
int c;
386
while ((c = r.read()) != -1) {
387
if (c == '\n') break;
388
b.appendCodePoint(c);
389
}
390
if (b.codePointAt(b.length() -1) == '\r') {
391
b.delete(b.length() -1, b.length());
392
}
393
return b.toString();
394
}
395
396
@Override
397
public void run() {
398
try {
399
while(!stopped) {
400
Socket clientConnection = ss.accept();
401
connections.add(clientConnection);
402
System.out.println(now() + getName() + ": Client accepted");
403
StringBuilder headers = new StringBuilder();
404
Socket targetConnection = null;
405
InputStream ccis = clientConnection.getInputStream();
406
OutputStream ccos = clientConnection.getOutputStream();
407
Writer w = new OutputStreamWriter(
408
clientConnection.getOutputStream(), "UTF-8");
409
PrintWriter pw = new PrintWriter(w);
410
System.out.println(now() + getName() + ": Reading request line");
411
String requestLine = readLine(ccis);
412
System.out.println(now() + getName() + ": Request line: " + requestLine);
413
414
StringTokenizer tokenizer = new StringTokenizer(requestLine);
415
String method = tokenizer.nextToken();
416
assert method.equalsIgnoreCase("POST") || method.equalsIgnoreCase("GET");
417
String path = tokenizer.nextToken();
418
URI uri;
419
try {
420
String hostport = serverAuthority();
421
uri = new URI((secure ? "https" : "http") +"://" + hostport + path);
422
} catch (Throwable x) {
423
System.err.printf("Bad target address: \"%s\" in \"%s\"%n",
424
path, requestLine);
425
clientConnection.close();
426
continue;
427
}
428
429
// Read all headers until we find the empty line that
430
// signals the end of all headers.
431
String line = requestLine;
432
String cookies = null;
433
while (!line.equals("")) {
434
System.out.println(now() + getName() + ": Reading header: "
435
+ (line = readLine(ccis)));
436
if (line.startsWith("Cookie:")) {
437
if (cookies == null) cookies = line;
438
else cookies = cookies + "\n" + line;
439
}
440
headers.append(line).append("\r\n");
441
}
442
443
StringBuilder response = new StringBuilder();
444
StringBuilder xheaders = new StringBuilder();
445
446
int index = headers.toString()
447
.toLowerCase(Locale.US)
448
.indexOf("content-length: ");
449
if (index >= 0) {
450
index = index + "content-length: ".length();
451
String cl = headers.toString().substring(index);
452
StringTokenizer tk = new StringTokenizer(cl);
453
int len = Integer.parseInt(tk.nextToken());
454
System.out.println(now() + getName()
455
+ ": received body: "
456
+ new String(ccis.readNBytes(len), UTF_8));
457
}
458
String resp = MESSAGE;
459
String status = "200 OK";
460
if (cookies == null) {
461
resp = "No cookies found in headers";
462
status = "500 Internal Server Error";
463
} else if (cookies.contains("\n")) {
464
resp = "More than one 'Cookie:' line found: "
465
+ Arrays.asList(cookies.split("\n"));
466
status = "500 Internal Server Error";
467
} else {
468
List<String> values =
469
Stream.of(cookies.substring("Cookie:".length()).trim().split("; "))
470
.map(String::trim)
471
.collect(Collectors.toList());
472
Collections.sort(values);
473
if (values.size() != 2) {
474
resp = "Bad cookie list: " + values;
475
status = "500 Internal Server Error";
476
} else if (!values.get(0).equals("CUSTOMER=ARTHUR_DENT")) {
477
resp = "Unexpected cookie: " + values.get(0) + " in " + values;
478
status = "500 Internal Server Error";
479
} else if (!values.get(1).equals("ORDER=BISCUITS")) {
480
resp = "Unexpected cookie: " + values.get(1) + " in " + values;
481
status = "500 Internal Server Error";
482
} else {
483
for (String cookie : values) {
484
xheaders.append("X-Request-Cookie: ")
485
.append(cookie)
486
.append("\r\n");
487
}
488
}
489
}
490
byte[] b = resp.getBytes(UTF_8);
491
System.out.println(now()
492
+ getName() + ": sending back " + uri);
493
494
response.append("HTTP/1.1 ")
495
.append(status)
496
.append("\r\nContent-Length: ")
497
.append(b.length)
498
.append("\r\n")
499
.append(xheaders)
500
.append("\r\n");
501
502
// Then send the 200 OK response to the client
503
System.out.println(now() + getName() + ": Sending "
504
+ response);
505
pw.print(response);
506
pw.flush();
507
ccos.write(b);
508
ccos.flush();
509
ccos.close();
510
connections.remove(clientConnection);
511
clientConnection.close();
512
}
513
} catch (Throwable t) {
514
if (!stopped) {
515
System.out.println(now() + getName() + ": failed: " + t);
516
t.printStackTrace();
517
try {
518
stopServer();
519
} catch (Throwable e) {
520
521
}
522
}
523
} finally {
524
System.out.println(now() + getName() + ": exiting");
525
}
526
}
527
528
void close(Socket s) {
529
try {
530
s.close();
531
} catch(Throwable t) {
532
533
}
534
}
535
public void stopServer() throws IOException {
536
stopped = true;
537
ss.close();
538
connections.forEach(this::close);
539
}
540
541
public String serverAuthority() {
542
return InetAddress.getLoopbackAddress().getHostName() + ":"
543
+ ss.getLocalPort();
544
}
545
546
static DummyServer create(InetSocketAddress sa) throws IOException {
547
ServerSocket ss = ServerSocketFactory.getDefault()
548
.createServerSocket(sa.getPort(), -1, sa.getAddress());
549
return new DummyServer(ss, false);
550
}
551
552
static DummyServer create(InetSocketAddress sa, SSLContext sslContext) throws IOException {
553
ServerSocket ss = sslContext.getServerSocketFactory()
554
.createServerSocket(sa.getPort(), -1, sa.getAddress());
555
return new DummyServer(ss, true);
556
}
557
558
559
}
560
}
561
562