48 import jdk.incubator.http.HttpClient; |
48 import jdk.incubator.http.HttpClient; |
49 import jdk.incubator.http.HttpClient.Version; |
49 import jdk.incubator.http.HttpClient.Version; |
50 import jdk.incubator.http.HttpRequest; |
50 import jdk.incubator.http.HttpRequest; |
51 import jdk.incubator.http.HttpResponse; |
51 import jdk.incubator.http.HttpResponse; |
52 import jdk.testlibrary.SimpleSSLContext; |
52 import jdk.testlibrary.SimpleSSLContext; |
|
53 import sun.net.NetProperties; |
53 import sun.net.www.HeaderParser; |
54 import sun.net.www.HeaderParser; |
54 import static java.lang.System.out; |
55 import static java.lang.System.out; |
55 import static java.lang.String.format; |
56 import static java.lang.String.format; |
56 import static jdk.incubator.http.HttpResponse.BodyHandler.asLines; |
57 import static jdk.incubator.http.HttpResponse.BodyHandler.asLines; |
57 |
58 |
62 * @bug 8087112 |
63 * @bug 8087112 |
63 * @library /lib/testlibrary |
64 * @library /lib/testlibrary |
64 * @build jdk.testlibrary.SimpleSSLContext DigestEchoServer DigestEchoClient |
65 * @build jdk.testlibrary.SimpleSSLContext DigestEchoServer DigestEchoClient |
65 * @modules jdk.incubator.httpclient |
66 * @modules jdk.incubator.httpclient |
66 * java.base/sun.net.www |
67 * java.base/sun.net.www |
|
68 * java.base/sun.net |
67 * @run main/othervm DigestEchoClient |
69 * @run main/othervm DigestEchoClient |
|
70 * @run main/othervm -Djdk.http.auth.proxying.disabledSchemes= |
|
71 * -Djdk.http.auth.tunneling.disabledSchemes= |
|
72 * DigestEchoClient |
68 */ |
73 */ |
69 |
74 |
70 public class DigestEchoClient { |
75 public class DigestEchoClient { |
71 |
76 |
72 static final String data[] = { |
77 static final String data[] = { |
134 } |
139 } |
135 } |
140 } |
136 |
141 |
137 private static final ConcurrentMap<String, EchoServers> servers = new ConcurrentHashMap<>(); |
142 private static final ConcurrentMap<String, EchoServers> servers = new ConcurrentHashMap<>(); |
138 } |
143 } |
|
144 |
|
145 final static String PROXY_DISABLED = NetProperties.get("jdk.http.auth.proxying.disabledSchemes"); |
|
146 final static String TUNNEL_DISABLED = NetProperties.get("jdk.http.auth.tunneling.disabledSchemes"); |
|
147 static { |
|
148 System.out.println("jdk.http.auth.proxying.disabledSchemes=" + PROXY_DISABLED); |
|
149 System.out.println("jdk.http.auth.tunneling.disabledSchemes=" + TUNNEL_DISABLED); |
|
150 } |
|
151 |
139 |
152 |
140 |
153 |
141 static final AtomicInteger NC = new AtomicInteger(); |
154 static final AtomicInteger NC = new AtomicInteger(); |
142 static final Random random = new Random(); |
155 static final Random random = new Random(); |
143 static final SSLContext context; |
156 static final SSLContext context; |
264 digestCount.get(), digests.get())); |
277 digestCount.get(), digests.get())); |
265 System.out.println(" ---------------------------------------------------------- "); |
278 System.out.println(" ---------------------------------------------------------- "); |
266 } |
279 } |
267 } |
280 } |
268 |
281 |
|
282 boolean isSchemeDisabled() { |
|
283 String disabledSchemes; |
|
284 if (isProxy(authType)) { |
|
285 disabledSchemes = useSSL |
|
286 ? TUNNEL_DISABLED |
|
287 : PROXY_DISABLED; |
|
288 } else return false; |
|
289 if (disabledSchemes == null |
|
290 || disabledSchemes.isEmpty()) { |
|
291 return false; |
|
292 } |
|
293 String scheme; |
|
294 switch (authScheme) { |
|
295 case DIGEST: |
|
296 scheme = "Digest"; |
|
297 break; |
|
298 case BASIC: |
|
299 scheme = "Basic"; |
|
300 break; |
|
301 case BASICSERVER: |
|
302 scheme = "Basic"; |
|
303 break; |
|
304 case NONE: |
|
305 return false; |
|
306 default: |
|
307 throw new InternalError("Unknown auth scheme: " + authScheme); |
|
308 } |
|
309 return Stream.of(disabledSchemes.split(",")) |
|
310 .map(String::trim) |
|
311 .filter(scheme::equalsIgnoreCase) |
|
312 .findAny() |
|
313 .isPresent(); |
|
314 } |
|
315 |
269 final static AtomicLong basics = new AtomicLong(); |
316 final static AtomicLong basics = new AtomicLong(); |
270 final static AtomicLong basicCount = new AtomicLong(); |
317 final static AtomicLong basicCount = new AtomicLong(); |
271 // @Test |
318 // @Test |
272 void testBasic(HttpClient.Version version, boolean async, |
319 void testBasic(HttpClient.Version version, boolean async, |
273 boolean expectContinue, boolean preemptive) |
320 boolean expectContinue, boolean preemptive) |
303 if (addHeaders) { |
350 if (addHeaders) { |
304 // handle authentication ourselves |
351 // handle authentication ourselves |
305 assert !client.authenticator().isPresent(); |
352 assert !client.authenticator().isPresent(); |
306 if (auth == null) auth = "Basic " + getBasicAuth("arthur"); |
353 if (auth == null) auth = "Basic " + getBasicAuth("arthur"); |
307 try { |
354 try { |
308 if ((i > 0 || preemptive) && (!isTunnel || i == 0)) { |
355 if ((i > 0 || preemptive) |
|
356 && (!isTunnel || i == 0 || isSchemeDisabled())) { |
309 // In case of a SSL tunnel through proxy then only the |
357 // In case of a SSL tunnel through proxy then only the |
310 // first request should require proxy authorization |
358 // first request should require proxy authorization |
311 // Though this might be invalidated if the server decides |
359 // Though this might be invalidated if the server decides |
312 // to close the connection... |
360 // to close the connection... |
313 out.println(String.format("%s adding %s: %s", |
361 out.println(String.format("%s adding %s: %s", |
344 t = t.getCause(); |
392 t = t.getCause(); |
345 } |
393 } |
346 throw new RuntimeException("Unexpected exception: " + t, t); |
394 throw new RuntimeException("Unexpected exception: " + t, t); |
347 } |
395 } |
348 |
396 |
349 if (addHeaders && !preemptive && i==0) { |
397 if (addHeaders && !preemptive && (i==0 || isSchemeDisabled())) { |
350 assert resp.statusCode() == 401 || resp.statusCode() == 407; |
398 assert resp.statusCode() == 401 || resp.statusCode() == 407; |
351 request = HttpRequest.newBuilder(uri).version(version) |
399 request = HttpRequest.newBuilder(uri).version(version) |
352 .POST(reqBody).header(authorizationKey(authType), auth).build(); |
400 .POST(reqBody).header(authorizationKey(authType), auth).build(); |
353 if (async) { |
401 if (async) { |
354 resp = client.sendAsync(request, asLines()).join(); |
402 resp = client.sendAsync(request, asLines()).join(); |
355 } else { |
403 } else { |
356 resp = client.send(request, asLines()); |
404 resp = client.send(request, asLines()); |
357 } |
405 } |
358 } |
406 } |
359 assert resp.statusCode() == 200; |
407 final List<String> respLines; |
360 List<String> respLines = resp.body().collect(Collectors.toList()); |
408 try { |
361 long stop = System.nanoTime(); |
409 if (isSchemeDisabled()) { |
362 synchronized (basicCount) { |
410 if (resp.statusCode() != 407) { |
363 long n = basicCount.getAndIncrement(); |
411 throw new RuntimeException("expected 407 not received"); |
364 basics.set((basics.get() * n + (stop - start)) / (n + 1)); |
412 } |
|
413 System.out.println("Scheme disabled for [" + authType |
|
414 + ", " + authScheme |
|
415 + ", " + (useSSL ? "HTTP" : "HTTPS") |
|
416 + "]: Received expected " + resp.statusCode()); |
|
417 continue; |
|
418 } else { |
|
419 System.out.println("Scheme enabled for [" + authType |
|
420 + ", " + authScheme |
|
421 + ", " + (useSSL ? "HTTPS" : "HTTP") |
|
422 + "]: Expecting 200"); |
|
423 assert resp.statusCode() == 200; |
|
424 respLines = resp.body().collect(Collectors.toList()); |
|
425 } |
|
426 } finally { |
|
427 long stop = System.nanoTime(); |
|
428 synchronized (basicCount) { |
|
429 long n = basicCount.getAndIncrement(); |
|
430 basics.set((basics.get() * n + (stop - start)) / (n + 1)); |
|
431 } |
365 } |
432 } |
366 if (!lines.equals(respLines)) { |
433 if (!lines.equals(respLines)) { |
367 throw new RuntimeException("Unexpected response: " + respLines); |
434 throw new RuntimeException("Unexpected response: " + respLines); |
368 } |
435 } |
369 } |
436 } |
416 String digestMethod = isTunnel ? "CONNECT" : "POST"; |
483 String digestMethod = isTunnel ? "CONNECT" : "POST"; |
417 |
484 |
418 // In case of a tunnel connection only the first request |
485 // In case of a tunnel connection only the first request |
419 // which establishes the tunnel needs to authenticate with |
486 // which establishes the tunnel needs to authenticate with |
420 // the proxy. |
487 // the proxy. |
421 if (challenge != null && !isTunnel) { |
488 if (challenge != null && (!isTunnel || isSchemeDisabled())) { |
422 assert cnonceStr != null; |
489 assert cnonceStr != null; |
423 String auth = digestResponse(uri, digestMethod, challenge, cnonceStr); |
490 String auth = digestResponse(uri, digestMethod, challenge, cnonceStr); |
424 try { |
491 try { |
425 reqBuilder = reqBuilder.header(authorizationKey(authType), auth); |
492 reqBuilder = reqBuilder.header(authorizationKey(authType), auth); |
426 } catch (IllegalArgumentException x) { |
493 } catch (IllegalArgumentException x) { |
440 assert challenge != null || resp.statusCode() == 401 || resp.statusCode() == 407; |
507 assert challenge != null || resp.statusCode() == 401 || resp.statusCode() == 407; |
441 if (resp.statusCode() == 401 || resp.statusCode() == 407) { |
508 if (resp.statusCode() == 401 || resp.statusCode() == 407) { |
442 // This assert may need to be relaxed if our server happened to |
509 // This assert may need to be relaxed if our server happened to |
443 // decide to close the tunnel connection, in which case we would |
510 // decide to close the tunnel connection, in which case we would |
444 // receive 407 again... |
511 // receive 407 again... |
445 assert challenge == null || !isTunnel |
512 assert challenge == null || !isTunnel || isSchemeDisabled() |
446 : "No proxy auth should be required after establishing an SSL tunnel"; |
513 : "No proxy auth should be required after establishing an SSL tunnel"; |
447 |
514 |
448 System.out.println("Received " + resp.statusCode() + " answering challenge..."); |
515 System.out.println("Received " + resp.statusCode() + " answering challenge..."); |
449 random.nextBytes(cnonce); |
516 random.nextBytes(cnonce); |
450 cnonceStr = new BigInteger(1, cnonce).toString(16); |
517 cnonceStr = new BigInteger(1, cnonce).toString(16); |
474 } else { |
541 } else { |
475 resp = client.send(request, asLines()); |
542 resp = client.send(request, asLines()); |
476 } |
543 } |
477 System.out.println(resp); |
544 System.out.println(resp); |
478 } |
545 } |
479 assert resp.statusCode() == 200; |
546 final List<String> respLines; |
480 List<String> respLines = resp.body().collect(Collectors.toList()); |
547 try { |
481 long stop = System.nanoTime(); |
548 if (isSchemeDisabled()) { |
482 synchronized (digestCount) { |
549 if (resp.statusCode() != 407) { |
483 long n = digestCount.getAndIncrement(); |
550 throw new RuntimeException("expected 407 not received"); |
484 digests.set((digests.get() * n + (stop - start)) / (n + 1)); |
551 } |
|
552 System.out.println("Scheme disabled for [" + authType |
|
553 + ", " + authScheme + |
|
554 ", " + (useSSL ? "HTTP" : "HTTPS") |
|
555 + "]: Received expected " + resp.statusCode()); |
|
556 continue; |
|
557 } else { |
|
558 assert resp.statusCode() == 200; |
|
559 respLines = resp.body().collect(Collectors.toList()); |
|
560 } |
|
561 } finally { |
|
562 long stop = System.nanoTime(); |
|
563 synchronized (basicCount) { |
|
564 long n = basicCount.getAndIncrement(); |
|
565 basics.set((basics.get() * n + (stop - start)) / (n + 1)); |
|
566 } |
485 } |
567 } |
486 if (!lines.equals(respLines)) { |
568 if (!lines.equals(respLines)) { |
487 throw new RuntimeException("Unexpected response: " + respLines); |
569 throw new RuntimeException("Unexpected response: " + respLines); |
488 } |
570 } |
489 } |
571 } |