59 /** |
59 /** |
60 * @test |
60 * @test |
61 * @summary this test verifies that a client may provides authorization |
61 * @summary this test verifies that a client may provides authorization |
62 * headers directly when connecting with a server. |
62 * headers directly when connecting with a server. |
63 * @bug 8087112 |
63 * @bug 8087112 |
64 * @library /lib/testlibrary |
64 * @library /lib/testlibrary http2/server |
65 * @build jdk.testlibrary.SimpleSSLContext DigestEchoServer DigestEchoClient |
65 * @build jdk.testlibrary.SimpleSSLContext HttpServerAdapters DigestEchoServer DigestEchoClient |
66 * @modules jdk.incubator.httpclient |
66 * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common |
|
67 * jdk.incubator.httpclient/jdk.incubator.http.internal.frame |
|
68 * jdk.incubator.httpclient/jdk.incubator.http.internal.hpack |
|
69 * java.logging |
|
70 * java.base/sun.net.www.http |
67 * java.base/sun.net.www |
71 * java.base/sun.net.www |
68 * java.base/sun.net |
72 * java.base/sun.net |
69 * @run main/othervm DigestEchoClient |
73 * @run main/othervm DigestEchoClient |
70 * @run main/othervm -Djdk.http.auth.proxying.disabledSchemes= |
74 * @run main/othervm -Djdk.http.auth.proxying.disabledSchemes= |
71 * -Djdk.http.auth.tunneling.disabledSchemes= |
75 * -Djdk.http.auth.tunneling.disabledSchemes= |
92 final DigestEchoServer.HttpAuthType authType; |
96 final DigestEchoServer.HttpAuthType authType; |
93 final DigestEchoServer.HttpAuthSchemeType authScheme; |
97 final DigestEchoServer.HttpAuthSchemeType authScheme; |
94 final String protocolScheme; |
98 final String protocolScheme; |
95 final String key; |
99 final String key; |
96 final DigestEchoServer server; |
100 final DigestEchoServer server; |
|
101 final Version serverVersion; |
97 |
102 |
98 private EchoServers(DigestEchoServer server, |
103 private EchoServers(DigestEchoServer server, |
|
104 Version version, |
99 String protocolScheme, |
105 String protocolScheme, |
100 DigestEchoServer.HttpAuthType authType, |
106 DigestEchoServer.HttpAuthType authType, |
101 DigestEchoServer.HttpAuthSchemeType authScheme) { |
107 DigestEchoServer.HttpAuthSchemeType authScheme) { |
102 this.authType = authType; |
108 this.authType = authType; |
103 this.authScheme = authScheme; |
109 this.authScheme = authScheme; |
104 this.protocolScheme = protocolScheme; |
110 this.protocolScheme = protocolScheme; |
105 this.key = key(protocolScheme, authType, authScheme); |
111 this.key = key(version, protocolScheme, authType, authScheme); |
106 this.server = server; |
112 this.server = server; |
107 } |
113 this.serverVersion = version; |
108 |
114 } |
109 static String key(String protocolScheme, |
115 |
|
116 static String key(Version version, |
|
117 String protocolScheme, |
110 DigestEchoServer.HttpAuthType authType, |
118 DigestEchoServer.HttpAuthType authType, |
111 DigestEchoServer.HttpAuthSchemeType authScheme) { |
119 DigestEchoServer.HttpAuthSchemeType authScheme) { |
112 return String.format("%s:%s:%s", protocolScheme, authType, authScheme); |
120 return String.format("%s:%s:%s:%s", version, protocolScheme, authType, authScheme); |
113 } |
121 } |
114 |
122 |
115 private static EchoServers create(String protocolScheme, |
123 private static EchoServers create(Version version, |
|
124 String protocolScheme, |
116 DigestEchoServer.HttpAuthType authType, |
125 DigestEchoServer.HttpAuthType authType, |
117 DigestEchoServer.HttpAuthSchemeType authScheme) { |
126 DigestEchoServer.HttpAuthSchemeType authScheme) { |
118 try { |
127 try { |
119 serverCount.incrementAndGet(); |
128 serverCount.incrementAndGet(); |
120 DigestEchoServer server = |
129 DigestEchoServer server = |
121 DigestEchoServer.create(protocolScheme, authType, authScheme); |
130 DigestEchoServer.create(version, protocolScheme, authType, authScheme); |
122 return new EchoServers(server, protocolScheme, authType, authScheme); |
131 return new EchoServers(server, version, protocolScheme, authType, authScheme); |
123 } catch (IOException x) { |
132 } catch (IOException x) { |
124 throw new UncheckedIOException(x); |
133 throw new UncheckedIOException(x); |
125 } |
134 } |
126 } |
135 } |
127 |
136 |
128 public static DigestEchoServer of(String protocolScheme, |
137 public static DigestEchoServer of(Version version, |
|
138 String protocolScheme, |
129 DigestEchoServer.HttpAuthType authType, |
139 DigestEchoServer.HttpAuthType authType, |
130 DigestEchoServer.HttpAuthSchemeType authScheme) { |
140 DigestEchoServer.HttpAuthSchemeType authScheme) { |
131 String key = key(protocolScheme, authType, authScheme); |
141 String key = key(version, protocolScheme, authType, authScheme); |
132 return servers.computeIfAbsent(key, (k) -> |
142 return servers.computeIfAbsent(key, (k) -> |
133 create(protocolScheme, authType, authScheme)).server; |
143 create(version, protocolScheme, authType, authScheme)).server; |
134 } |
144 } |
135 |
145 |
136 public static void stop() { |
146 public static void stop() { |
137 for (EchoServers s : servers.values()) { |
147 for (EchoServers s : servers.values()) { |
138 s.server.stop(); |
148 s.server.stop(); |
212 break; |
222 break; |
213 } |
223 } |
214 return builder.build(); |
224 return builder.build(); |
215 } |
225 } |
216 |
226 |
|
227 public static List<Version> serverVersions(Version clientVersion) { |
|
228 if (clientVersion == Version.HTTP_1_1) { |
|
229 return List.of(clientVersion); |
|
230 } else { |
|
231 return List.of(Version.values()); |
|
232 } |
|
233 } |
|
234 |
|
235 public static List<Version> clientVersions() { |
|
236 return List.of(Version.values()); |
|
237 } |
|
238 |
|
239 public static List<Boolean> expectContinue(Version serverVersion) { |
|
240 if (serverVersion == Version.HTTP_1_1) { |
|
241 return BOOLEANS; |
|
242 } else { |
|
243 // our test HTTP/2 server does not support Expect: 100-Continue |
|
244 return List.of(Boolean.FALSE); |
|
245 } |
|
246 } |
|
247 |
217 public static void main(String[] args) throws Exception { |
248 public static void main(String[] args) throws Exception { |
218 boolean useSSL = false; |
249 boolean useSSL = false; |
219 EnumSet<DigestEchoServer.HttpAuthType> types = |
250 EnumSet<DigestEchoServer.HttpAuthType> types = |
220 EnumSet.complementOf(EnumSet.of(DigestEchoServer.HttpAuthType.PROXY305)); |
251 EnumSet.complementOf(EnumSet.of(DigestEchoServer.HttpAuthType.PROXY305)); |
221 if (args != null && args.length >= 1) { |
252 if (args != null && args.length >= 1) { |
237 DigestEchoServer.HttpAuthSchemeType.BASIC); |
268 DigestEchoServer.HttpAuthSchemeType.BASIC); |
238 for (DigestEchoServer.HttpAuthSchemeType authScheme : basics) { |
269 for (DigestEchoServer.HttpAuthSchemeType authScheme : basics) { |
239 DigestEchoClient dec = new DigestEchoClient(useSSL, |
270 DigestEchoClient dec = new DigestEchoClient(useSSL, |
240 authScheme, |
271 authScheme, |
241 authType); |
272 authType); |
242 for (Version version : HttpClient.Version.values()) { |
273 for (Version clientVersion : clientVersions()) { |
243 for (boolean expectContinue : BOOLEANS) { |
274 for (Version serverVersion : serverVersions(clientVersion)) { |
244 for (boolean async : BOOLEANS) { |
275 for (boolean expectContinue : expectContinue(serverVersion)) { |
245 for (boolean preemptive : BOOLEANS) { |
276 for (boolean async : BOOLEANS) { |
246 dec.testBasic(version, async, |
277 for (boolean preemptive : BOOLEANS) { |
247 expectContinue, preemptive); |
278 dec.testBasic(clientVersion, |
|
279 serverVersion, async, |
|
280 expectContinue, preemptive); |
|
281 } |
248 } |
282 } |
249 } |
283 } |
250 } |
284 } |
251 } |
285 } |
252 } |
286 } |
254 EnumSet.of(DigestEchoServer.HttpAuthSchemeType.DIGEST); |
288 EnumSet.of(DigestEchoServer.HttpAuthSchemeType.DIGEST); |
255 for (DigestEchoServer.HttpAuthSchemeType authScheme : digests) { |
289 for (DigestEchoServer.HttpAuthSchemeType authScheme : digests) { |
256 DigestEchoClient dec = new DigestEchoClient(useSSL, |
290 DigestEchoClient dec = new DigestEchoClient(useSSL, |
257 authScheme, |
291 authScheme, |
258 authType); |
292 authType); |
259 for (Version version : HttpClient.Version.values()) { |
293 for (Version clientVersion : clientVersions()) { |
260 for (boolean expectContinue : BOOLEANS) { |
294 for (Version serverVersion : serverVersions(clientVersion)) { |
261 for (boolean async : BOOLEANS) { |
295 for (boolean expectContinue : expectContinue(serverVersion)) { |
262 dec.testDigest(version, async, expectContinue); |
296 for (boolean async : BOOLEANS) { |
|
297 dec.testDigest(clientVersion, serverVersion, |
|
298 async, expectContinue); |
|
299 } |
263 } |
300 } |
264 } |
301 } |
265 } |
302 } |
266 } |
303 } |
267 } |
304 } |
|
305 } catch(Throwable t) { |
|
306 System.out.println("Unexpected exception: exiting: " + t); |
|
307 t.printStackTrace(); |
|
308 throw t; |
268 } finally { |
309 } finally { |
269 EchoServers.stop(); |
310 EchoServers.stop(); |
270 System.out.println(" ---------------------------------------------------------- "); |
311 System.out.println(" ---------------------------------------------------------- "); |
271 System.out.println(String.format("DigestEchoClient %s %s", useSSL ? "SSL" : "CLEAR", types)); |
312 System.out.println(String.format("DigestEchoClient %s %s", useSSL ? "SSL" : "CLEAR", types)); |
272 System.out.println(String.format("Created %d clients and %d servers", |
313 System.out.println(String.format("Created %d clients and %d servers", |
314 } |
355 } |
315 |
356 |
316 final static AtomicLong basics = new AtomicLong(); |
357 final static AtomicLong basics = new AtomicLong(); |
317 final static AtomicLong basicCount = new AtomicLong(); |
358 final static AtomicLong basicCount = new AtomicLong(); |
318 // @Test |
359 // @Test |
319 void testBasic(HttpClient.Version version, boolean async, |
360 void testBasic(Version clientVersion, Version serverVersion, boolean async, |
320 boolean expectContinue, boolean preemptive) |
361 boolean expectContinue, boolean preemptive) |
321 throws Exception |
362 throws Exception |
322 { |
363 { |
323 final boolean addHeaders = authScheme == DigestEchoServer.HttpAuthSchemeType.BASICSERVER; |
364 final boolean addHeaders = authScheme == DigestEchoServer.HttpAuthSchemeType.BASICSERVER; |
324 // !preemptive has no meaning if we don't handle the authorization |
365 // !preemptive has no meaning if we don't handle the authorization |
325 // headers ourselves |
366 // headers ourselves |
326 if (!preemptive && !addHeaders) return; |
367 if (!preemptive && !addHeaders) return; |
327 |
368 |
328 out.println(format("*** testBasic: version: %s, async: %s, useSSL: %s, " + |
369 out.println(format("*** testBasic: client: %s, server: %s, async: %s, useSSL: %s, " + |
329 "authScheme: %s, authType: %s, expectContinue: %s preemptive: %s***", |
370 "authScheme: %s, authType: %s, expectContinue: %s preemptive: %s***", |
330 version, async, useSSL, authScheme, authType, expectContinue, preemptive)); |
371 clientVersion, serverVersion, async, useSSL, authScheme, authType, |
331 |
372 expectContinue, preemptive)); |
332 DigestEchoServer server = EchoServers.of(useSSL ? "https" : "http", authType, authScheme); |
373 |
333 URI uri = DigestEchoServer.uri(useSSL ? "https" : "http", server.getServerAddress(), "/foo/"); |
374 DigestEchoServer server = EchoServers.of(serverVersion, |
|
375 useSSL ? "https" : "http", authType, authScheme); |
|
376 URI uri = DigestEchoServer.uri(useSSL ? "https" : "http", |
|
377 server.getServerAddress(), "/foo/"); |
334 |
378 |
335 HttpClient client = newHttpClient(server); |
379 HttpClient client = newHttpClient(server); |
336 HttpResponse<String> r; |
380 HttpResponse<String> r; |
337 CompletableFuture<HttpResponse<String>> cf1; |
381 CompletableFuture<HttpResponse<String>> cf1; |
338 String auth = null; |
382 String auth = null; |
342 out.println(DigestEchoServer.now() + " ----- iteration " + i + " -----"); |
386 out.println(DigestEchoServer.now() + " ----- iteration " + i + " -----"); |
343 List<String> lines = List.of(Arrays.copyOfRange(data, 0, i+1)); |
387 List<String> lines = List.of(Arrays.copyOfRange(data, 0, i+1)); |
344 assert lines.size() == i + 1; |
388 assert lines.size() == i + 1; |
345 String body = lines.stream().collect(Collectors.joining("\r\n")); |
389 String body = lines.stream().collect(Collectors.joining("\r\n")); |
346 HttpRequest.BodyPublisher reqBody = HttpRequest.BodyPublisher.fromString(body); |
390 HttpRequest.BodyPublisher reqBody = HttpRequest.BodyPublisher.fromString(body); |
347 HttpRequest.Builder builder = HttpRequest.newBuilder(uri).version(version) |
391 HttpRequest.Builder builder = HttpRequest.newBuilder(uri).version(clientVersion) |
348 .POST(reqBody).expectContinue(expectContinue); |
392 .POST(reqBody).expectContinue(expectContinue); |
349 boolean isTunnel = isProxy(authType) && useSSL; |
393 boolean isTunnel = isProxy(authType) && useSSL; |
350 if (addHeaders) { |
394 if (addHeaders) { |
351 // handle authentication ourselves |
395 // handle authentication ourselves |
352 assert !client.authenticator().isPresent(); |
396 assert !client.authenticator().isPresent(); |
394 throw new RuntimeException("Unexpected exception: " + t, t); |
438 throw new RuntimeException("Unexpected exception: " + t, t); |
395 } |
439 } |
396 |
440 |
397 if (addHeaders && !preemptive && (i==0 || isSchemeDisabled())) { |
441 if (addHeaders && !preemptive && (i==0 || isSchemeDisabled())) { |
398 assert resp.statusCode() == 401 || resp.statusCode() == 407; |
442 assert resp.statusCode() == 401 || resp.statusCode() == 407; |
399 request = HttpRequest.newBuilder(uri).version(version) |
443 System.out.println(String.format("%s received: adding header %s: %s", |
|
444 resp.statusCode(), authorizationKey(authType), auth)); |
|
445 request = HttpRequest.newBuilder(uri).version(clientVersion) |
400 .POST(reqBody).header(authorizationKey(authType), auth).build(); |
446 .POST(reqBody).header(authorizationKey(authType), auth).build(); |
401 if (async) { |
447 if (async) { |
402 resp = client.sendAsync(request, asLines()).join(); |
448 resp = client.sendAsync(request, asLines()).join(); |
403 } else { |
449 } else { |
404 resp = client.send(request, asLines()); |
450 resp = client.send(request, asLines()); |
449 } |
495 } |
450 |
496 |
451 final static AtomicLong digests = new AtomicLong(); |
497 final static AtomicLong digests = new AtomicLong(); |
452 final static AtomicLong digestCount = new AtomicLong(); |
498 final static AtomicLong digestCount = new AtomicLong(); |
453 // @Test |
499 // @Test |
454 void testDigest(HttpClient.Version version, boolean async, boolean expectContinue) |
500 void testDigest(Version clientVersion, Version serverVersion, |
|
501 boolean async, boolean expectContinue) |
455 throws Exception |
502 throws Exception |
456 { |
503 { |
457 out.println(format("*** testDigest: version: %s, async: %s, useSSL: %s, " + |
504 out.println(format("*** testDigest: client: %s, server: %s, async: %s, useSSL: %s, " + |
458 "authScheme: %s, authType: %s, expectContinue: %s ***", |
505 "authScheme: %s, authType: %s, expectContinue: %s ***", |
459 version, async, useSSL, authScheme, authType, expectContinue)); |
506 clientVersion, serverVersion, async, useSSL, |
460 DigestEchoServer server = EchoServers.of(useSSL ? "https" : "http", authType, authScheme); |
507 authScheme, authType, expectContinue)); |
461 |
508 DigestEchoServer server = EchoServers.of(serverVersion, |
462 URI uri = DigestEchoServer.uri(useSSL ? "https" : "http", server.getServerAddress(), "/foo/"); |
509 useSSL ? "https" : "http", authType, authScheme); |
|
510 |
|
511 URI uri = DigestEchoServer.uri(useSSL ? "https" : "http", |
|
512 server.getServerAddress(), "/foo/"); |
463 |
513 |
464 HttpClient client = newHttpClient(server); |
514 HttpClient client = newHttpClient(server); |
465 HttpResponse<String> r; |
515 HttpResponse<String> r; |
466 CompletableFuture<HttpResponse<String>> cf1; |
516 CompletableFuture<HttpResponse<String>> cf1; |
467 byte[] cnonce = new byte[16]; |
517 byte[] cnonce = new byte[16]; |
474 List<String> lines = List.of(Arrays.copyOfRange(data, 0, i+1)); |
524 List<String> lines = List.of(Arrays.copyOfRange(data, 0, i+1)); |
475 assert lines.size() == i + 1; |
525 assert lines.size() == i + 1; |
476 String body = lines.stream().collect(Collectors.joining("\r\n")); |
526 String body = lines.stream().collect(Collectors.joining("\r\n")); |
477 HttpRequest.BodyPublisher reqBody = HttpRequest.BodyPublisher.fromString(body); |
527 HttpRequest.BodyPublisher reqBody = HttpRequest.BodyPublisher.fromString(body); |
478 HttpRequest.Builder reqBuilder = HttpRequest |
528 HttpRequest.Builder reqBuilder = HttpRequest |
479 .newBuilder(uri).version(version).POST(reqBody) |
529 .newBuilder(uri).version(clientVersion).POST(reqBody) |
480 .expectContinue(expectContinue); |
530 .expectContinue(expectContinue); |
481 |
531 |
482 boolean isTunnel = isProxy(authType) && useSSL; |
532 boolean isTunnel = isProxy(authType) && useSSL; |
483 String digestMethod = isTunnel ? "CONNECT" : "POST"; |
533 String digestMethod = isTunnel ? "CONNECT" : "POST"; |
484 |
534 |