167 System.out.println("stopping server"); |
173 System.out.println("stopping server"); |
168 server.stop(); |
174 server.stop(); |
169 } |
175 } |
170 } |
176 } |
171 |
177 |
172 private static String toString(Headers headers) { |
178 private static String toString(HttpTestHeaders headers) { |
173 return headers.entrySet().stream() |
179 return headers.entrySet().stream() |
174 .map((e) -> e.getKey() + ": " + e.getValue()) |
180 .map((e) -> e.getKey() + ": " + e.getValue()) |
175 .collect(Collectors.joining("\n")); |
181 .collect(Collectors.joining("\n")); |
176 } |
182 } |
177 |
183 |
178 public static DigestEchoServer create(String protocol, |
184 public static DigestEchoServer create(Version version, |
|
185 String protocol, |
179 HttpAuthType authType, |
186 HttpAuthType authType, |
180 HttpAuthSchemeType schemeType) |
187 HttpAuthSchemeType schemeType) |
181 throws IOException { |
188 throws IOException { |
182 return create(protocol, authType, AUTHENTICATOR, schemeType); |
189 return create(version, protocol, authType, AUTHENTICATOR, schemeType); |
183 } |
190 } |
184 |
191 |
185 public static DigestEchoServer create(String protocol, |
192 public static DigestEchoServer create(Version version, |
|
193 String protocol, |
186 HttpAuthType authType, |
194 HttpAuthType authType, |
187 HttpTestAuthenticator auth, |
195 HttpTestAuthenticator auth, |
188 HttpAuthSchemeType schemeType) |
196 HttpAuthSchemeType schemeType) |
189 throws IOException { |
197 throws IOException { |
190 return create(protocol, authType, auth, schemeType, null); |
198 return create(version, protocol, authType, auth, schemeType, null); |
191 } |
199 } |
192 |
200 |
193 public static DigestEchoServer create(String protocol, |
201 public static DigestEchoServer create(Version version, |
|
202 String protocol, |
194 HttpAuthType authType, |
203 HttpAuthType authType, |
195 HttpTestAuthenticator auth, |
204 HttpTestAuthenticator auth, |
196 HttpAuthSchemeType schemeType, |
205 HttpAuthSchemeType schemeType, |
197 HttpHandler delegate) |
206 HttpTestHandler delegate) |
198 throws IOException { |
207 throws IOException { |
199 Objects.requireNonNull(authType); |
208 Objects.requireNonNull(authType); |
200 Objects.requireNonNull(auth); |
209 Objects.requireNonNull(auth); |
201 switch(authType) { |
210 switch(authType) { |
202 // A server that performs Server Digest authentication. |
211 // A server that performs Server Digest authentication. |
203 case SERVER: return createServer(protocol, authType, auth, |
212 case SERVER: return createServer(version, protocol, authType, auth, |
204 schemeType, delegate, "/"); |
213 schemeType, delegate, "/"); |
205 // A server that pretends to be a Proxy and performs |
214 // A server that pretends to be a Proxy and performs |
206 // Proxy Digest authentication. If protocol is HTTPS, |
215 // Proxy Digest authentication. If protocol is HTTPS, |
207 // then this will create a HttpsProxyTunnel that will |
216 // then this will create a HttpsProxyTunnel that will |
208 // handle the CONNECT request for tunneling. |
217 // handle the CONNECT request for tunneling. |
209 case PROXY: return createProxy(protocol, authType, auth, |
218 case PROXY: return createProxy(version, protocol, authType, auth, |
210 schemeType, delegate, "/"); |
219 schemeType, delegate, "/"); |
211 // A server that sends 307 redirect to a server that performs |
220 // A server that sends 307 redirect to a server that performs |
212 // Digest authentication. |
221 // Digest authentication. |
213 // Note: 301 doesn't work here because it transforms POST into GET. |
222 // Note: 301 doesn't work here because it transforms POST into GET. |
214 case SERVER307: return createServerAndRedirect(protocol, |
223 case SERVER307: return createServerAndRedirect(version, |
|
224 protocol, |
215 HttpAuthType.SERVER, |
225 HttpAuthType.SERVER, |
216 auth, schemeType, |
226 auth, schemeType, |
217 delegate, 307); |
227 delegate, 307); |
218 // A server that sends 305 redirect to a proxy that performs |
228 // A server that sends 305 redirect to a proxy that performs |
219 // Digest authentication. |
229 // Digest authentication. |
220 // Note: this is not correctly stubbed/implemented in this test. |
230 // Note: this is not correctly stubbed/implemented in this test. |
221 case PROXY305: return createServerAndRedirect(protocol, |
231 case PROXY305: return createServerAndRedirect(version, |
|
232 protocol, |
222 HttpAuthType.PROXY, |
233 HttpAuthType.PROXY, |
223 auth, schemeType, |
234 auth, schemeType, |
224 delegate, 305); |
235 delegate, 305); |
225 default: |
236 default: |
226 throw new InternalError("Unknown server type: " + authType); |
237 throw new InternalError("Unknown server type: " + authType); |
328 * Returns a HttpServer or a HttpsServer in different subclasses. |
339 * Returns a HttpServer or a HttpsServer in different subclasses. |
329 */ |
340 */ |
330 protected abstract S newHttpServer() throws IOException; |
341 protected abstract S newHttpServer() throws IOException; |
331 } |
342 } |
332 |
343 |
333 private static final class HttpServerFactory extends WebServerFactory<HttpServer> { |
344 /* |
334 private static final HttpServerFactory instance = new HttpServerFactory(); |
345 * Used to create Http2TestServer |
|
346 */ |
|
347 private static abstract class H2ServerFactory<S extends Http2TestServer> |
|
348 extends SocketBindableFactory<S> { |
|
349 @Override |
|
350 protected S createBindable() throws IOException { |
|
351 final S server; |
|
352 try { |
|
353 server = newHttpServer(); |
|
354 } catch (IOException io) { |
|
355 throw io; |
|
356 } catch (Exception x) { |
|
357 throw new IOException(x); |
|
358 } |
|
359 return server; |
|
360 } |
|
361 |
|
362 @Override |
|
363 protected InetSocketAddress getAddress(S server) { |
|
364 return server.getAddress(); |
|
365 } |
|
366 |
|
367 @Override |
|
368 protected void close(S server) throws IOException { |
|
369 server.stop(); |
|
370 } |
|
371 |
|
372 /* |
|
373 * Returns a HttpServer or a HttpsServer in different subclasses. |
|
374 */ |
|
375 protected abstract S newHttpServer() throws Exception; |
|
376 } |
|
377 |
|
378 private static final class Http2ServerFactory extends H2ServerFactory<Http2TestServer> { |
|
379 private static final Http2ServerFactory instance = new Http2ServerFactory(); |
|
380 |
|
381 static Http2TestServer create() throws IOException { |
|
382 return instance.createInternal(); |
|
383 } |
|
384 |
|
385 @Override |
|
386 protected Http2TestServer newHttpServer() throws Exception { |
|
387 return new Http2TestServer("127.0.0.1", false, 0); |
|
388 } |
|
389 } |
|
390 |
|
391 private static final class Https2ServerFactory extends H2ServerFactory<Http2TestServer> { |
|
392 private static final Https2ServerFactory instance = new Https2ServerFactory(); |
|
393 |
|
394 static Http2TestServer create() throws IOException { |
|
395 return instance.createInternal(); |
|
396 } |
|
397 |
|
398 @Override |
|
399 protected Http2TestServer newHttpServer() throws Exception { |
|
400 return new Http2TestServer("127.0.0.1", true, 0); |
|
401 } |
|
402 } |
|
403 |
|
404 private static final class Http1ServerFactory extends H1ServerFactory<HttpServer> { |
|
405 private static final Http1ServerFactory instance = new Http1ServerFactory(); |
335 |
406 |
336 static HttpServer create() throws IOException { |
407 static HttpServer create() throws IOException { |
337 return instance.createInternal(); |
408 return instance.createInternal(); |
338 } |
409 } |
339 |
410 |
391 } |
487 } |
392 }; |
488 }; |
393 ctxt.setAuthenticator(authenticator); |
489 ctxt.setAuthenticator(authenticator); |
394 } |
490 } |
395 |
491 |
396 public static DigestEchoServer createServer(String protocol, |
492 public static DigestEchoServer createServer(Version version, |
|
493 String protocol, |
397 HttpAuthType authType, |
494 HttpAuthType authType, |
398 HttpTestAuthenticator auth, |
495 HttpTestAuthenticator auth, |
399 HttpAuthSchemeType schemeType, |
496 HttpAuthSchemeType schemeType, |
400 HttpHandler delegate, |
497 HttpTestHandler delegate, |
401 String path) |
498 String path) |
402 throws IOException { |
499 throws IOException { |
403 Objects.requireNonNull(authType); |
500 Objects.requireNonNull(authType); |
404 Objects.requireNonNull(auth); |
501 Objects.requireNonNull(auth); |
405 |
502 |
406 HttpServer impl = createHttpServer(protocol); |
503 HttpTestServer impl = createHttpServer(version, protocol); |
407 final DigestEchoServer server = new DigestEchoServer(impl, null, delegate); |
504 String key = String.format("DigestEchoServer[PID=%s,PORT=%s]:%s:%s:%s:%s", |
408 final HttpHandler hh = server.createHandler(schemeType, auth, authType, false); |
505 ProcessHandle.current().pid(), |
409 HttpContext ctxt = impl.createContext(path, hh); |
506 impl.getAddress().getPort(), |
410 server.configureAuthentication(ctxt, schemeType, auth, authType); |
507 version, protocol, authType, schemeType); |
|
508 final DigestEchoServer server = new DigestEchoServer(key, impl, null, delegate); |
|
509 final HttpTestHandler handler = |
|
510 server.createHandler(schemeType, auth, authType, false); |
|
511 HttpTestContext context = impl.addHandler(handler, path); |
|
512 server.configureAuthentication(context, schemeType, auth, authType); |
411 impl.start(); |
513 impl.start(); |
412 return server; |
514 return server; |
413 } |
515 } |
414 |
516 |
415 public static DigestEchoServer createProxy(String protocol, |
517 public static DigestEchoServer createProxy(Version version, |
|
518 String protocol, |
416 HttpAuthType authType, |
519 HttpAuthType authType, |
417 HttpTestAuthenticator auth, |
520 HttpTestAuthenticator auth, |
418 HttpAuthSchemeType schemeType, |
521 HttpAuthSchemeType schemeType, |
419 HttpHandler delegate, |
522 HttpTestHandler delegate, |
420 String path) |
523 String path) |
421 throws IOException { |
524 throws IOException { |
422 Objects.requireNonNull(authType); |
525 Objects.requireNonNull(authType); |
423 Objects.requireNonNull(auth); |
526 Objects.requireNonNull(auth); |
424 |
527 |
425 HttpServer impl = createHttpServer(protocol); |
528 if (version == Version.HTTP_2 && protocol.equalsIgnoreCase("http")) { |
|
529 System.out.println("WARNING: can't use HTTP/1.1 proxy with unsecure HTTP/2 server"); |
|
530 version = Version.HTTP_1_1; |
|
531 } |
|
532 HttpTestServer impl = createHttpServer(version, protocol); |
|
533 String key = String.format("DigestEchoServer[PID=%s,PORT=%s]:%s:%s:%s:%s", |
|
534 ProcessHandle.current().pid(), |
|
535 impl.getAddress().getPort(), |
|
536 version, protocol, authType, schemeType); |
426 final DigestEchoServer server = "https".equalsIgnoreCase(protocol) |
537 final DigestEchoServer server = "https".equalsIgnoreCase(protocol) |
427 ? new HttpsProxyTunnel(impl, null, delegate) |
538 ? new HttpsProxyTunnel(key, impl, null, delegate) |
428 : new DigestEchoServer(impl, null, delegate); |
539 : new DigestEchoServer(key, impl, null, delegate); |
429 |
540 |
430 final HttpHandler hh = server.createHandler(HttpAuthSchemeType.NONE, |
541 final HttpTestHandler hh = server.createHandler(HttpAuthSchemeType.NONE, |
431 null, HttpAuthType.SERVER, |
542 null, HttpAuthType.SERVER, |
432 server instanceof HttpsProxyTunnel); |
543 server instanceof HttpsProxyTunnel); |
433 HttpContext ctxt = impl.createContext(path, hh); |
544 HttpTestContext ctxt = impl.addHandler(hh, path); |
434 server.configureAuthentication(ctxt, schemeType, auth, authType); |
545 server.configureAuthentication(ctxt, schemeType, auth, authType); |
435 impl.start(); |
546 impl.start(); |
436 |
547 |
437 return server; |
548 return server; |
438 } |
549 } |
439 |
550 |
440 public static DigestEchoServer createServerAndRedirect( |
551 public static DigestEchoServer createServerAndRedirect( |
|
552 Version version, |
441 String protocol, |
553 String protocol, |
442 HttpAuthType targetAuthType, |
554 HttpAuthType targetAuthType, |
443 HttpTestAuthenticator auth, |
555 HttpTestAuthenticator auth, |
444 HttpAuthSchemeType schemeType, |
556 HttpAuthSchemeType schemeType, |
445 HttpHandler targetDelegate, |
557 HttpTestHandler targetDelegate, |
446 int code300) |
558 int code300) |
447 throws IOException { |
559 throws IOException { |
448 Objects.requireNonNull(targetAuthType); |
560 Objects.requireNonNull(targetAuthType); |
449 Objects.requireNonNull(auth); |
561 Objects.requireNonNull(auth); |
450 |
562 |
454 String targetProtocol = targetAuthType == HttpAuthType.PROXY |
566 String targetProtocol = targetAuthType == HttpAuthType.PROXY |
455 ? "http" |
567 ? "http" |
456 : protocol; |
568 : protocol; |
457 DigestEchoServer redirectTarget = |
569 DigestEchoServer redirectTarget = |
458 (targetAuthType == HttpAuthType.PROXY) |
570 (targetAuthType == HttpAuthType.PROXY) |
459 ? createProxy(protocol, targetAuthType, |
571 ? createProxy(version, protocol, targetAuthType, |
460 auth, schemeType, targetDelegate, "/") |
572 auth, schemeType, targetDelegate, "/") |
461 : createServer(targetProtocol, targetAuthType, |
573 : createServer(version, targetProtocol, targetAuthType, |
462 auth, schemeType, targetDelegate, "/"); |
574 auth, schemeType, targetDelegate, "/"); |
463 HttpServer impl = createHttpServer(protocol); |
575 HttpTestServer impl = createHttpServer(version, protocol); |
|
576 String key = String.format("RedirectingServer[PID=%s,PORT=%s]:%s:%s:%s:%s", |
|
577 ProcessHandle.current().pid(), |
|
578 impl.getAddress().getPort(), |
|
579 version, protocol, |
|
580 HttpAuthType.SERVER, code300) |
|
581 + "->" + redirectTarget.key; |
464 final DigestEchoServer redirectingServer = |
582 final DigestEchoServer redirectingServer = |
465 new DigestEchoServer(impl, redirectTarget, null); |
583 new DigestEchoServer(key, impl, redirectTarget, null); |
466 InetSocketAddress redirectAddr = redirectTarget.getAddress(); |
584 InetSocketAddress redirectAddr = redirectTarget.getAddress(); |
467 URL locationURL = url(targetProtocol, redirectAddr, "/"); |
585 URL locationURL = url(targetProtocol, redirectAddr, "/"); |
468 final HttpHandler hh = redirectingServer.create300Handler(locationURL, |
586 final HttpTestHandler hh = redirectingServer.create300Handler(key, locationURL, |
469 HttpAuthType.SERVER, code300); |
587 HttpAuthType.SERVER, code300); |
470 impl.createContext("/", hh); |
588 impl.addHandler(hh,"/"); |
471 impl.start(); |
589 impl.start(); |
472 return redirectingServer; |
590 return redirectingServer; |
473 } |
591 } |
474 |
592 |
475 public InetSocketAddress getAddress() { |
593 public InetSocketAddress getAddress() { |
476 return serverImpl.getAddress(); |
594 return new InetSocketAddress("127.0.0.1", |
|
595 serverImpl.getAddress().getPort()); |
477 } |
596 } |
478 |
597 |
479 public InetSocketAddress getServerAddress() { |
598 public InetSocketAddress getServerAddress() { |
480 return serverImpl.getAddress(); |
599 return new InetSocketAddress("127.0.0.1", |
|
600 serverImpl.getAddress().getPort()); |
481 } |
601 } |
482 |
602 |
483 public InetSocketAddress getProxyAddress() { |
603 public InetSocketAddress getProxyAddress() { |
484 return serverImpl.getAddress(); |
604 return new InetSocketAddress("127.0.0.1", |
485 } |
605 serverImpl.getAddress().getPort()); |
|
606 } |
|
607 |
|
608 public Version getServerVersion() { return serverImpl.getVersion(); } |
486 |
609 |
487 public void stop() { |
610 public void stop() { |
488 serverImpl.stop(0); |
611 serverImpl.stop(); |
489 if (redirect != null) { |
612 if (redirect != null) { |
490 redirect.stop(); |
613 redirect.stop(); |
491 } |
614 } |
492 } |
615 } |
493 |
616 |
494 protected void writeResponse(HttpExchange he) throws IOException { |
617 protected void writeResponse(HttpTestExchange he) throws IOException { |
495 if (delegate == null) { |
618 if (delegate == null) { |
496 he.sendResponseHeaders(HttpURLConnection.HTTP_OK, 0); |
619 he.sendResponseHeaders(HttpURLConnection.HTTP_OK, 0); |
497 he.getResponseBody().write(he.getRequestBody().readAllBytes()); |
620 he.getResponseBody().write(he.getRequestBody().readAllBytes()); |
498 } else { |
621 } else { |
499 delegate.handle(he); |
622 delegate.handle(he); |
500 } |
623 } |
501 } |
624 } |
502 |
625 |
503 private HttpHandler createHandler(HttpAuthSchemeType schemeType, |
626 private HttpTestHandler createHandler(HttpAuthSchemeType schemeType, |
504 HttpTestAuthenticator auth, |
627 HttpTestAuthenticator auth, |
505 HttpAuthType authType, |
628 HttpAuthType authType, |
506 boolean tunelled) { |
629 boolean tunelled) { |
507 return new HttpNoAuthHandler(authType, tunelled); |
630 return new HttpNoAuthHandler(key, authType, tunelled); |
508 } |
631 } |
509 |
632 |
510 void configureAuthentication(HttpContext ctxt, |
633 void configureAuthentication(HttpTestContext ctxt, |
511 HttpAuthSchemeType schemeType, |
634 HttpAuthSchemeType schemeType, |
512 HttpTestAuthenticator auth, |
635 HttpTestAuthenticator auth, |
513 HttpAuthType authType) { |
636 HttpAuthType authType) { |
514 switch(schemeType) { |
637 switch(schemeType) { |
515 case DIGEST: |
638 case DIGEST: |
516 // DIGEST authentication is handled by the handler. |
639 // DIGEST authentication is handled by the handler. |
517 ctxt.getFilters().add(new HttpDigestFilter(auth, authType)); |
640 ctxt.addFilter(new HttpDigestFilter(key, auth, authType)); |
518 break; |
641 break; |
519 case BASIC: |
642 case BASIC: |
520 // BASIC authentication is handled by the filter. |
643 // BASIC authentication is handled by the filter. |
521 ctxt.getFilters().add(new HttpBasicFilter(auth, authType)); |
644 ctxt.addFilter(new HttpBasicFilter(key, auth, authType)); |
522 break; |
645 break; |
523 case BASICSERVER: |
646 case BASICSERVER: |
524 switch(authType) { |
647 switch(authType) { |
525 case PROXY: case PROXY305: |
648 case PROXY: case PROXY305: |
526 // HttpServer can't support Proxy-type authentication |
649 // HttpServer can't support Proxy-type authentication |
527 // => we do as if BASIC had been specified, and we will |
650 // => we do as if BASIC had been specified, and we will |
528 // handle authentication in the handler. |
651 // handle authentication in the handler. |
529 ctxt.getFilters().add(new HttpBasicFilter(auth, authType)); |
652 ctxt.addFilter(new HttpBasicFilter(key, auth, authType)); |
530 break; |
653 break; |
531 case SERVER: case SERVER307: |
654 case SERVER: case SERVER307: |
532 // Basic authentication is handled by HttpServer |
655 if (ctxt.getVersion() == Version.HTTP_1_1) { |
533 // directly => the filter should not perform |
656 // Basic authentication is handled by HttpServer |
534 // authentication again. |
657 // directly => the filter should not perform |
535 setContextAuthenticator(ctxt, auth); |
658 // authentication again. |
536 ctxt.getFilters().add(new HttpNoAuthFilter(authType)); |
659 setContextAuthenticator(ctxt, auth); |
|
660 ctxt.addFilter(new HttpNoAuthFilter(key, authType)); |
|
661 } else { |
|
662 ctxt.addFilter(new HttpBasicFilter(key, auth, authType)); |
|
663 } |
537 break; |
664 break; |
538 default: |
665 default: |
539 throw new InternalError("Invalid combination scheme=" |
666 throw new InternalError(key + ": Invalid combination scheme=" |
540 + schemeType + " authType=" + authType); |
667 + schemeType + " authType=" + authType); |
541 } |
668 } |
542 case NONE: |
669 case NONE: |
543 // No authentication at all. |
670 // No authentication at all. |
544 ctxt.getFilters().add(new HttpNoAuthFilter(authType)); |
671 ctxt.addFilter(new HttpNoAuthFilter(key, authType)); |
545 break; |
672 break; |
546 default: |
673 default: |
547 throw new InternalError("No such scheme: " + schemeType); |
674 throw new InternalError(key + ": No such scheme: " + schemeType); |
548 } |
675 } |
549 } |
676 } |
550 |
677 |
551 private HttpHandler create300Handler(URL proxyURL, |
678 private HttpTestHandler create300Handler(String key, URL proxyURL, |
552 HttpAuthType type, int code300) throws MalformedURLException { |
679 HttpAuthType type, int code300) |
553 return new Http3xxHandler(proxyURL, type, code300); |
680 throws MalformedURLException |
|
681 { |
|
682 return new Http3xxHandler(key, proxyURL, type, code300); |
554 } |
683 } |
555 |
684 |
556 // Abstract HTTP filter class. |
685 // Abstract HTTP filter class. |
557 private abstract static class AbstractHttpFilter extends Filter { |
686 private abstract static class AbstractHttpFilter extends HttpTestFilter { |
558 |
687 |
559 final HttpAuthType authType; |
688 final HttpAuthType authType; |
560 final String type; |
689 final String type; |
561 public AbstractHttpFilter(HttpAuthType authType, String type) { |
690 public AbstractHttpFilter(HttpAuthType authType, String type) { |
562 this.authType = authType; |
691 this.authType = authType; |
794 } |
928 } |
795 |
929 |
796 } |
930 } |
797 |
931 |
798 // An HTTP Filter that performs Basic authentication |
932 // An HTTP Filter that performs Basic authentication |
799 private class HttpBasicFilter extends AbstractHttpFilter { |
933 private static class HttpBasicFilter extends AbstractHttpFilter { |
|
934 |
|
935 static String type(String key, HttpAuthType authType) { |
|
936 String type = authType == HttpAuthType.SERVER |
|
937 ? "Basic Server Filter" : "Basic Proxy Filter"; |
|
938 return "["+type+"]:"+key; |
|
939 } |
800 |
940 |
801 private final HttpTestAuthenticator auth; |
941 private final HttpTestAuthenticator auth; |
802 public HttpBasicFilter(HttpTestAuthenticator auth, HttpAuthType authType) { |
942 public HttpBasicFilter(String key, HttpTestAuthenticator auth, |
803 super(authType, authType == HttpAuthType.SERVER |
943 HttpAuthType authType) { |
804 ? "Basic Server" : "Basic Proxy"); |
944 super(authType, type(key, authType)); |
805 this.auth = auth; |
945 this.auth = auth; |
806 } |
946 } |
807 |
947 |
808 @Override |
948 @Override |
809 protected void requestAuthentication(HttpExchange he) |
949 protected void requestAuthentication(HttpTestExchange he) |
810 throws IOException { |
950 throws IOException { |
811 he.getResponseHeaders().add(getAuthenticate(), |
951 he.getResponseHeaders().addHeader(getAuthenticate(), |
812 "Basic realm=\"" + auth.getRealm() + "\""); |
952 "Basic realm=\"" + auth.getRealm() + "\""); |
813 System.out.println(type + ": Requesting Basic Authentication " |
953 System.out.println(type + ": Requesting Basic Authentication " |
814 + he.getResponseHeaders().getFirst(getAuthenticate())); |
954 + he.getResponseHeaders().firstValue(getAuthenticate())); |
815 } |
955 } |
816 |
956 |
817 @Override |
957 @Override |
818 protected boolean isAuthentified(HttpExchange he) { |
958 protected boolean isAuthentified(HttpTestExchange he) { |
819 if (he.getRequestHeaders().containsKey(getAuthorization())) { |
959 if (he.getRequestHeaders().containsKey(getAuthorization())) { |
820 List<String> authorization = |
960 List<String> authorization = |
821 he.getRequestHeaders().get(getAuthorization()); |
961 he.getRequestHeaders().get(getAuthorization()); |
822 for (String a : authorization) { |
962 for (String a : authorization) { |
823 System.out.println(type + ": processing " + a); |
963 System.out.println(type + ": processing " + a); |
852 new String(auth.getPassword(uname)).equals(pass); |
992 new String(auth.getPassword(uname)).equals(pass); |
853 } |
993 } |
854 |
994 |
855 @Override |
995 @Override |
856 public String description() { |
996 public String description() { |
857 return "Filter for " + type; |
997 return "Filter for BASIC authentication: " + type; |
858 } |
998 } |
859 |
999 |
860 } |
1000 } |
861 |
1001 |
862 |
1002 |
863 // An HTTP Filter that performs Digest authentication |
1003 // An HTTP Filter that performs Digest authentication |
864 // WARNING: This is not a full fledged implementation of DIGEST. |
1004 // WARNING: This is not a full fledged implementation of DIGEST. |
865 // It does contain bugs and inaccuracy. |
1005 // It does contain bugs and inaccuracy. |
866 private class HttpDigestFilter extends AbstractHttpFilter { |
1006 private static class HttpDigestFilter extends AbstractHttpFilter { |
|
1007 |
|
1008 static String type(String key, HttpAuthType authType) { |
|
1009 String type = authType == HttpAuthType.SERVER |
|
1010 ? "Digest Server Filter" : "Digest Proxy Filter"; |
|
1011 return "["+type+"]:"+key; |
|
1012 } |
867 |
1013 |
868 // This is a very basic DIGEST - used only for the purpose of testing |
1014 // This is a very basic DIGEST - used only for the purpose of testing |
869 // the client implementation. Therefore we can get away with never |
1015 // the client implementation. Therefore we can get away with never |
870 // updating the server nonce as it makes the implementation of the |
1016 // updating the server nonce as it makes the implementation of the |
871 // server side digest simpler. |
1017 // server side digest simpler. |
872 private final HttpTestAuthenticator auth; |
1018 private final HttpTestAuthenticator auth; |
873 private final byte[] nonce; |
1019 private final byte[] nonce; |
874 private final String ns; |
1020 private final String ns; |
875 public HttpDigestFilter(HttpTestAuthenticator auth, HttpAuthType authType) { |
1021 public HttpDigestFilter(String key, HttpTestAuthenticator auth, HttpAuthType authType) { |
876 super(authType, authType == HttpAuthType.SERVER |
1022 super(authType, type(key, authType)); |
877 ? "Digest Server" : "Digest Proxy"); |
|
878 this.auth = auth; |
1023 this.auth = auth; |
879 nonce = new byte[16]; |
1024 nonce = new byte[16]; |
880 new Random(Instant.now().toEpochMilli()).nextBytes(nonce); |
1025 new Random(Instant.now().toEpochMilli()).nextBytes(nonce); |
881 ns = new BigInteger(1, nonce).toString(16); |
1026 ns = new BigInteger(1, nonce).toString(16); |
882 } |
1027 } |
883 |
1028 |
884 @Override |
1029 @Override |
885 protected void requestAuthentication(HttpExchange he) |
1030 protected void requestAuthentication(HttpTestExchange he) |
886 throws IOException { |
1031 throws IOException { |
887 he.getResponseHeaders().add(getAuthenticate(), |
1032 he.getResponseHeaders().addHeader(getAuthenticate(), |
888 "Digest realm=\"" + auth.getRealm() + "\"," |
1033 "Digest realm=\"" + auth.getRealm() + "\"," |
889 + "\r\n qop=\"auth\"," |
1034 + "\r\n qop=\"auth\"," |
890 + "\r\n nonce=\"" + ns +"\""); |
1035 + "\r\n nonce=\"" + ns +"\""); |
891 System.out.println(type + ": Requesting Digest Authentication " |
1036 System.out.println(type + ": Requesting Digest Authentication " |
892 + he.getResponseHeaders().getFirst(getAuthenticate())); |
1037 + he.getResponseHeaders() |
893 } |
1038 .firstValue(getAuthenticate()) |
894 |
1039 .orElse("null")); |
895 @Override |
1040 } |
896 protected boolean isAuthentified(HttpExchange he) { |
1041 |
|
1042 @Override |
|
1043 protected boolean isAuthentified(HttpTestExchange he) { |
897 if (he.getRequestHeaders().containsKey(getAuthorization())) { |
1044 if (he.getRequestHeaders().containsKey(getAuthorization())) { |
898 List<String> authorization = he.getRequestHeaders().get(getAuthorization()); |
1045 List<String> authorization = he.getRequestHeaders().get(getAuthorization()); |
899 for (String a : authorization) { |
1046 for (String a : authorization) { |
900 System.out.println(type + ": processing " + a); |
1047 System.out.println(type + ": processing " + a); |
901 int sp = a.indexOf(' '); |
1048 int sp = a.indexOf(' '); |
1043 // by sending a back 3xx response code (301, 305, 307 etc..) |
1196 // by sending a back 3xx response code (301, 305, 307 etc..) |
1044 private class Http3xxHandler extends AbstractHttpHandler { |
1197 private class Http3xxHandler extends AbstractHttpHandler { |
1045 |
1198 |
1046 private final URL redirectTargetURL; |
1199 private final URL redirectTargetURL; |
1047 private final int code3XX; |
1200 private final int code3XX; |
1048 public Http3xxHandler(URL proxyURL, HttpAuthType authType, int code300) { |
1201 public Http3xxHandler(String key, URL proxyURL, HttpAuthType authType, int code300) { |
1049 super(authType, "Server" + code300); |
1202 super(authType, stype("Server" + code300, key, authType, false)); |
1050 this.redirectTargetURL = proxyURL; |
1203 this.redirectTargetURL = proxyURL; |
1051 this.code3XX = code300; |
1204 this.code3XX = code300; |
1052 } |
1205 } |
1053 |
1206 |
1054 int get3XX() { |
1207 int get3XX() { |
1055 return code3XX; |
1208 return code3XX; |
1056 } |
1209 } |
1057 |
1210 |
1058 @Override |
1211 @Override |
1059 public void sendResponse(HttpExchange he) throws IOException { |
1212 public void sendResponse(HttpTestExchange he) throws IOException { |
1060 System.out.println(type + ": Got " + he.getRequestMethod() |
1213 System.out.println(type + ": Got " + he.getRequestMethod() |
1061 + ": " + he.getRequestURI() |
1214 + ": " + he.getRequestURI() |
1062 + "\n" + DigestEchoServer.toString(he.getRequestHeaders())); |
1215 + "\n" + DigestEchoServer.toString(he.getRequestHeaders())); |
1063 System.out.println(type + ": Redirecting to " |
1216 System.out.println(type + ": Redirecting to " |
1064 + (authType == HttpAuthType.PROXY305 |
1217 + (authType == HttpAuthType.PROXY305 |
1065 ? "proxy" : "server")); |
1218 ? "proxy" : "server")); |
1066 he.getResponseHeaders().add(getLocation(), |
1219 he.getResponseHeaders().addHeader(getLocation(), |
1067 redirectTargetURL.toExternalForm().toString()); |
1220 redirectTargetURL.toExternalForm().toString()); |
1068 he.sendResponseHeaders(get3XX(), 0); |
1221 he.sendResponseHeaders(get3XX(), 0); |
1069 System.out.println(type + ": Sent back " + get3XX() + " " |
1222 System.out.println(type + ": Sent back " + get3XX() + " " |
1070 + getLocation() + ": " + redirectTargetURL.toExternalForm().toString()); |
1223 + getLocation() + ": " + redirectTargetURL.toExternalForm().toString()); |
1071 } |
1224 } |
1266 final ServerSocket ss; |
1421 final ServerSocket ss; |
1267 final CopyOnWriteArrayList<CompletableFuture<Void>> connectionCFs |
1422 final CopyOnWriteArrayList<CompletableFuture<Void>> connectionCFs |
1268 = new CopyOnWriteArrayList<>(); |
1423 = new CopyOnWriteArrayList<>(); |
1269 volatile ProxyAuthorization authorization; |
1424 volatile ProxyAuthorization authorization; |
1270 volatile boolean stopped; |
1425 volatile boolean stopped; |
1271 public HttpsProxyTunnel(HttpServer server, DigestEchoServer target, |
1426 public HttpsProxyTunnel(String key, HttpTestServer server, DigestEchoServer target, |
1272 HttpHandler delegate) |
1427 HttpTestHandler delegate) |
1273 throws IOException { |
1428 throws IOException { |
1274 super(server, target, delegate); |
1429 this(key, server, target, delegate, ServerSocketFactory.create()); |
|
1430 } |
|
1431 private HttpsProxyTunnel(String key, HttpTestServer server, DigestEchoServer target, |
|
1432 HttpTestHandler delegate, ServerSocket ss) |
|
1433 throws IOException { |
|
1434 super("HttpsProxyTunnel:" + ss.getLocalPort() + ":" + key, |
|
1435 server, target, delegate); |
1275 System.out.flush(); |
1436 System.out.flush(); |
1276 System.err.println("WARNING: HttpsProxyTunnel is an experimental test class"); |
1437 System.err.println("WARNING: HttpsProxyTunnel is an experimental test class"); |
1277 ss = ServerSocketFactory.create(); |
1438 this.ss = ss; |
1278 start(); |
1439 start(); |
1279 } |
1440 } |
1280 |
1441 |
1281 final void start() throws IOException { |
1442 final void start() throws IOException { |
1282 Thread t = new Thread(this, "ProxyThread"); |
1443 Thread t = new Thread(this, "ProxyThread"); |