test/jdk/java/net/httpclient/DigestEchoServer.java
branchhttp-client-branch
changeset 56070 66a9c3185028
parent 56054 352e845ae744
child 56089 42208b2f224e
equal deleted inserted replaced
56069:ffaea9a1eed5 56070:66a9c3185028
    69 import java.util.concurrent.atomic.AtomicInteger;
    69 import java.util.concurrent.atomic.AtomicInteger;
    70 import java.util.stream.Collectors;
    70 import java.util.stream.Collectors;
    71 import java.util.stream.Stream;
    71 import java.util.stream.Stream;
    72 import javax.net.ssl.SSLContext;
    72 import javax.net.ssl.SSLContext;
    73 import sun.net.www.HeaderParser;
    73 import sun.net.www.HeaderParser;
       
    74 import jdk.incubator.http.HttpClient.Version;
    74 
    75 
    75 /**
    76 /**
    76  * A simple HTTP server that supports Basic or Digest authentication.
    77  * A simple HTTP server that supports Basic or Digest authentication.
    77  * By default this server will echo back whatever is present
    78  * By default this server will echo back whatever is present
    78  * in the request body. Note that the Digest authentication is
    79  * in the request body. Note that the Digest authentication is
    79  * a test implementation implemented only for tests purposes.
    80  * a test implementation implemented only for tests purposes.
    80  * @author danielfuchs
    81  * @author danielfuchs
    81  */
    82  */
    82 public class DigestEchoServer {
    83 public class DigestEchoServer implements HttpServerAdapters {
    83 
    84 
    84     public static final boolean DEBUG =
    85     public static final boolean DEBUG =
    85             Boolean.parseBoolean(System.getProperty("test.debug", "false"));
    86             Boolean.parseBoolean(System.getProperty("test.debug", "false"));
    86     public enum HttpAuthType {
    87     public enum HttpAuthType {
    87         SERVER, PROXY, SERVER307, PROXY305
    88         SERVER, PROXY, SERVER307, PROXY305
   139     static {
   140     static {
   140         AUTHENTICATOR = new HttpTestAuthenticator("earth", "arthur");
   141         AUTHENTICATOR = new HttpTestAuthenticator("earth", "arthur");
   141     }
   142     }
   142 
   143 
   143 
   144 
   144     final HttpServer       serverImpl; // this server endpoint
   145     final HttpTestServer       serverImpl; // this server endpoint
   145     final DigestEchoServer redirect;   // the target server where to redirect 3xx
   146     final DigestEchoServer     redirect;   // the target server where to redirect 3xx
   146     final HttpHandler      delegate;   // unused
   147     final HttpTestHandler      delegate;   // unused
   147 
   148     final String               key;
   148     private DigestEchoServer(HttpServer server, DigestEchoServer target,
   149 
   149                            HttpHandler delegate) {
   150     private DigestEchoServer(String key,
       
   151                              HttpTestServer server,
       
   152                              DigestEchoServer target,
       
   153                              HttpTestHandler delegate) {
       
   154         this.key = key;
   150         this.serverImpl = server;
   155         this.serverImpl = server;
   151         this.redirect = target;
   156         this.redirect = target;
   152         this.delegate = delegate;
   157         this.delegate = delegate;
   153     }
   158     }
   154 
   159 
   155     public static void main(String[] args)
   160     public static void main(String[] args)
   156             throws IOException {
   161             throws IOException {
   157 
   162 
   158         DigestEchoServer server = create(DEFAULT_PROTOCOL_TYPE,
   163         DigestEchoServer server = create(Version.HTTP_1_1,
       
   164                 DEFAULT_PROTOCOL_TYPE,
   159                 DEFAULT_HTTP_AUTH_TYPE,
   165                 DEFAULT_HTTP_AUTH_TYPE,
   160                 AUTHENTICATOR,
   166                 AUTHENTICATOR,
   161                 DEFAULT_SCHEME_TYPE);
   167                 DEFAULT_SCHEME_TYPE);
   162         try {
   168         try {
   163             System.out.println("Server created at " + server.getAddress());
   169             System.out.println("Server created at " + server.getAddress());
   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);
   242             final int max = addresses.size() + MAX;
   253             final int max = addresses.size() + MAX;
   243             final List<B> toClose = new ArrayList<>();
   254             final List<B> toClose = new ArrayList<>();
   244             try {
   255             try {
   245                 for (int i = 1; i <= max; i++) {
   256                 for (int i = 1; i <= max; i++) {
   246                     B bindable = createBindable();
   257                     B bindable = createBindable();
   247                     SocketAddress address = getAddress(bindable);
   258                     InetSocketAddress address = getAddress(bindable);
   248                     String key = address.toString();
   259                     String key = "127.0.0.1:" + address.getPort();
   249                     if (addresses.addIfAbsent(key)) {
   260                     if (addresses.addIfAbsent(key)) {
   250                         System.out.println("Socket bound to: " + key
   261                         System.out.println("Socket bound to: " + key
   251                                 + " after " + i + " attempt(s)");
   262                                 + " after " + i + " attempt(s)");
   252                         return bindable;
   263                         return bindable;
   253                     }
   264                     }
   268                     + "addresses used before: " + addresses);
   279                     + "addresses used before: " + addresses);
   269         }
   280         }
   270 
   281 
   271         protected abstract B createBindable() throws IOException;
   282         protected abstract B createBindable() throws IOException;
   272 
   283 
   273         protected abstract SocketAddress getAddress(B bindable);
   284         protected abstract InetSocketAddress getAddress(B bindable);
   274 
   285 
   275         protected abstract void close(B bindable) throws IOException;
   286         protected abstract void close(B bindable) throws IOException;
   276     }
   287     }
   277 
   288 
   278     /*
   289     /*
   286             return instance.createInternal();
   297             return instance.createInternal();
   287         }
   298         }
   288 
   299 
   289         @Override
   300         @Override
   290         protected ServerSocket createBindable() throws IOException {
   301         protected ServerSocket createBindable() throws IOException {
   291             return new ServerSocket(0, 0, InetAddress.getByName("127.0.0.1"));
   302             return new ServerSocket(0);
   292         }
   303         }
   293 
   304 
   294         @Override
   305         @Override
   295         protected SocketAddress getAddress(ServerSocket socket) {
   306         protected InetSocketAddress getAddress(ServerSocket socket) {
   296             return socket.getLocalSocketAddress();
   307             return new InetSocketAddress(socket.getInetAddress(), socket.getLocalPort());
   297         }
   308         }
   298 
   309 
   299         @Override
   310         @Override
   300         protected void close(ServerSocket socket) throws IOException {
   311         protected void close(ServerSocket socket) throws IOException {
   301             socket.close();
   312             socket.close();
   302         }
   313         }
   303     }
   314     }
   304 
   315 
   305     /*
   316     /*
   306      * Used to create HttpServer for a NTLMTestServer.
   317      * Used to create HttpServer
   307      */
   318      */
   308     private static abstract class WebServerFactory<S extends HttpServer>
   319     private static abstract class H1ServerFactory<S extends HttpServer>
   309             extends SocketBindableFactory<S> {
   320             extends SocketBindableFactory<S> {
   310         @Override
   321         @Override
   311         protected S createBindable() throws IOException {
   322         protected S createBindable() throws IOException {
   312             S server = newHttpServer();
   323             S server = newHttpServer();
   313             server.bind(new InetSocketAddress("127.0.0.1", 0), 0);
   324             server.bind(new InetSocketAddress( 0), 0);
   314             return server;
   325             return server;
   315         }
   326         }
   316 
   327 
   317         @Override
   328         @Override
   318         protected SocketAddress getAddress(S server) {
   329         protected InetSocketAddress getAddress(S server) {
   319             return server.getAddress();
   330             return server.getAddress();
   320         }
   331         }
   321 
   332 
   322         @Override
   333         @Override
   323         protected void close(S server) throws IOException {
   334         protected void close(S server) throws IOException {
   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 
   341         protected HttpServer newHttpServer() throws IOException {
   412         protected HttpServer newHttpServer() throws IOException {
   342             return HttpServer.create();
   413             return HttpServer.create();
   343         }
   414         }
   344     }
   415     }
   345 
   416 
   346     private static final class HttpsServerFactory extends WebServerFactory<HttpsServer> {
   417     private static final class Https1ServerFactory extends H1ServerFactory<HttpsServer> {
   347         private static final HttpsServerFactory instance = new HttpsServerFactory();
   418         private static final Https1ServerFactory instance = new Https1ServerFactory();
   348 
   419 
   349         static HttpsServer create() throws IOException {
   420         static HttpsServer create() throws IOException {
   350             return instance.createInternal();
   421             return instance.createInternal();
   351         }
   422         }
   352 
   423 
   354         protected HttpsServer newHttpServer() throws IOException {
   425         protected HttpsServer newHttpServer() throws IOException {
   355             return HttpsServer.create();
   426             return HttpsServer.create();
   356         }
   427         }
   357     }
   428     }
   358 
   429 
   359     static HttpServer createHttpServer(String protocol) throws IOException {
   430     static Http2TestServer createHttp2Server(String protocol) throws IOException {
       
   431         final Http2TestServer server;
       
   432         if ("http".equalsIgnoreCase(protocol)) {
       
   433             server = Http2ServerFactory.create();
       
   434         } else if ("https".equalsIgnoreCase(protocol)) {
       
   435             server = Https2ServerFactory.create();
       
   436         } else {
       
   437             throw new InternalError("unsupported protocol: " + protocol);
       
   438         }
       
   439         return server;
       
   440     }
       
   441 
       
   442     static HttpTestServer createHttpServer(Version version, String protocol)
       
   443             throws IOException
       
   444     {
       
   445         switch(version) {
       
   446             case HTTP_1_1:
       
   447                 return HttpTestServer.of(createHttp1Server(protocol));
       
   448             case HTTP_2:
       
   449                 return HttpTestServer.of(createHttp2Server(protocol));
       
   450             default:
       
   451                 throw new InternalError("Unexpected version: " + version);
       
   452         }
       
   453     }
       
   454 
       
   455     static HttpServer createHttp1Server(String protocol) throws IOException {
   360         final HttpServer server;
   456         final HttpServer server;
   361         if ("http".equalsIgnoreCase(protocol)) {
   457         if ("http".equalsIgnoreCase(protocol)) {
   362             server = HttpServerFactory.create();
   458             server = Http1ServerFactory.create();
   363         } else if ("https".equalsIgnoreCase(protocol)) {
   459         } else if ("https".equalsIgnoreCase(protocol)) {
   364             server = configure(HttpsServerFactory.create());
   460             server = configure(Https1ServerFactory.create());
   365         } else {
   461         } else {
   366             throw new InternalError("unsupported protocol: " + protocol);
   462             throw new InternalError("unsupported protocol: " + protocol);
   367         }
   463         }
   368         return server;
   464         return server;
   369     }
   465     }
   377         }
   473         }
   378         return server;
   474         return server;
   379     }
   475     }
   380 
   476 
   381 
   477 
   382     static void setContextAuthenticator(HttpContext ctxt,
   478     static void setContextAuthenticator(HttpTestContext ctxt,
   383                                         HttpTestAuthenticator auth) {
   479                                         HttpTestAuthenticator auth) {
   384         final String realm = auth.getRealm();
   480         final String realm = auth.getRealm();
   385         com.sun.net.httpserver.Authenticator authenticator =
   481         com.sun.net.httpserver.Authenticator authenticator =
   386             new BasicAuthenticator(realm) {
   482             new BasicAuthenticator(realm) {
   387                 @Override
   483                 @Override
   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;
   584         }
   713         }
   585         String getConnection() {
   714         String getConnection() {
   586             return authType == HttpAuthType.PROXY
   715             return authType == HttpAuthType.PROXY
   587                     ? "Proxy-Connection" : "Connection";
   716                     ? "Proxy-Connection" : "Connection";
   588         }
   717         }
   589         protected abstract boolean isAuthentified(HttpExchange he) throws IOException;
   718         protected abstract boolean isAuthentified(HttpTestExchange he) throws IOException;
   590         protected abstract void requestAuthentication(HttpExchange he) throws IOException;
   719         protected abstract void requestAuthentication(HttpTestExchange he) throws IOException;
   591         protected void accept(HttpExchange he, Chain chain) throws IOException {
   720         protected void accept(HttpTestExchange he, HttpChain chain) throws IOException {
   592             chain.doFilter(he);
   721             chain.doFilter(he);
   593         }
   722         }
   594 
   723 
   595         @Override
   724         @Override
   596         public String description() {
   725         public String description() {
   597             return "Filter for " + type;
   726             return "Filter for " + type;
   598         }
   727         }
   599         @Override
   728         @Override
   600         public void doFilter(HttpExchange he, Chain chain) throws IOException {
   729         public void doFilter(HttpTestExchange he, HttpChain chain) throws IOException {
   601             try {
   730             try {
   602                 System.out.println(type + ": Got " + he.getRequestMethod()
   731                 System.out.println(type + ": Got " + he.getRequestMethod()
   603                     + ": " + he.getRequestURI()
   732                     + ": " + he.getRequestURI()
   604                     + "\n" + DigestEchoServer.toString(he.getRequestHeaders()));
   733                     + "\n" + DigestEchoServer.toString(he.getRequestHeaders()));
   605                 if (!isAuthentified(he)) {
   734                 if (!isAuthentified(he)) {
   769                                       algorithm, qop, opaque, response);
   898                                       algorithm, qop, opaque, response);
   770         }
   899         }
   771 
   900 
   772     }
   901     }
   773 
   902 
   774     private class HttpNoAuthFilter extends AbstractHttpFilter {
   903     private static class HttpNoAuthFilter extends AbstractHttpFilter {
   775 
   904 
   776         public HttpNoAuthFilter(HttpAuthType authType) {
   905         static String type(String key, HttpAuthType authType) {
   777             super(authType, authType == HttpAuthType.SERVER
   906             String type = authType == HttpAuthType.SERVER
   778                             ? "NoAuth Server" : "NoAuth Proxy");
   907                     ? "NoAuth Server Filter" : "NoAuth Proxy Filter";
   779         }
   908             return "["+type+"]:"+key;
   780 
   909         }
   781         @Override
   910 
   782         protected boolean isAuthentified(HttpExchange he) throws IOException {
   911         public HttpNoAuthFilter(String key, HttpAuthType authType) {
       
   912             super(authType, type(key, authType));
       
   913         }
       
   914 
       
   915         @Override
       
   916         protected boolean isAuthentified(HttpTestExchange he) throws IOException {
   783             return true;
   917             return true;
   784         }
   918         }
   785 
   919 
   786         @Override
   920         @Override
   787         protected void requestAuthentication(HttpExchange he) throws IOException {
   921         protected void requestAuthentication(HttpTestExchange he) throws IOException {
   788             throw new InternalError("Should not com here");
   922             throw new InternalError("Should not com here");
   789         }
   923         }
   790 
   924 
   791         @Override
   925         @Override
   792         public String description() {
   926         public String description() {
   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(' ');
   965         }
  1112         }
   966 
  1113 
   967 
  1114 
   968         @Override
  1115         @Override
   969         public String description() {
  1116         public String description() {
   970             return "Filter for DIGEST authentication";
  1117             return "Filter for DIGEST authentication: " + type;
   971         }
  1118         }
   972     }
  1119     }
   973 
  1120 
   974     // Abstract HTTP handler class.
  1121     // Abstract HTTP handler class.
   975     private abstract static class AbstractHttpHandler implements HttpHandler {
  1122     private abstract static class AbstractHttpHandler implements HttpTestHandler {
   976 
  1123 
   977         final HttpAuthType authType;
  1124         final HttpAuthType authType;
   978         final String type;
  1125         final String type;
   979         public AbstractHttpHandler(HttpAuthType authType, String type) {
  1126         public AbstractHttpHandler(HttpAuthType authType, String type) {
   980             this.authType = authType;
  1127             this.authType = authType;
   984         String getLocation() {
  1131         String getLocation() {
   985             return "Location";
  1132             return "Location";
   986         }
  1133         }
   987 
  1134 
   988         @Override
  1135         @Override
   989         public void handle(HttpExchange he) throws IOException {
  1136         public void handle(HttpTestExchange he) throws IOException {
   990             try {
  1137             try {
   991                 sendResponse(he);
  1138                 sendResponse(he);
   992             } catch (RuntimeException | Error | IOException t) {
  1139             } catch (RuntimeException | Error | IOException t) {
   993                System.err.println(type
  1140                System.err.println(type
   994                     + ": Unexpected exception while handling request: " + t);
  1141                     + ": Unexpected exception while handling request: " + t);
   997             } finally {
  1144             } finally {
   998                 he.close();
  1145                 he.close();
   999             }
  1146             }
  1000         }
  1147         }
  1001 
  1148 
  1002         protected abstract void sendResponse(HttpExchange he) throws IOException;
  1149         protected abstract void sendResponse(HttpTestExchange he) throws IOException;
  1003 
  1150 
       
  1151     }
       
  1152 
       
  1153     static String stype(String type, String key, HttpAuthType authType, boolean tunnelled) {
       
  1154         type = type + (authType == HttpAuthType.SERVER
       
  1155                        ? " Server" : " Proxy")
       
  1156                 + (tunnelled ? " Tunnelled" : "");
       
  1157         return "["+type+"]:"+key;
  1004     }
  1158     }
  1005 
  1159 
  1006     private class HttpNoAuthHandler extends AbstractHttpHandler {
  1160     private class HttpNoAuthHandler extends AbstractHttpHandler {
  1007 
  1161 
  1008         // true if this server is behind a proxy tunnel.
  1162         // true if this server is behind a proxy tunnel.
  1009         final boolean tunnelled;
  1163         final boolean tunnelled;
  1010         public HttpNoAuthHandler(HttpAuthType authType, boolean tunnelled) {
  1164         public HttpNoAuthHandler(String key, HttpAuthType authType, boolean tunnelled) {
  1011             super(authType, authType == HttpAuthType.SERVER
  1165             super(authType, stype("NoAuth", key, authType, tunnelled));
  1012                             ? "NoAuth Server" : "NoAuth Proxy");
       
  1013             this.tunnelled = tunnelled;
  1166             this.tunnelled = tunnelled;
  1014         }
  1167         }
  1015 
  1168 
  1016         @Override
  1169         @Override
  1017         protected void sendResponse(HttpExchange he) throws IOException {
  1170         protected void sendResponse(HttpTestExchange he) throws IOException {
  1018             if (DEBUG) {
  1171             if (DEBUG) {
  1019                 System.out.println(type + ": headers are: "
  1172                 System.out.println(type + ": headers are: "
  1020                         + DigestEchoServer.toString(he.getRequestHeaders()));
  1173                         + DigestEchoServer.toString(he.getRequestHeaders()));
  1021             }
  1174             }
  1022             if (authType == HttpAuthType.SERVER && tunnelled) {
  1175             if (authType == HttpAuthType.SERVER && tunnelled) {
  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         }
  1094     static class  ProxyAuthorization {
  1247     static class  ProxyAuthorization {
  1095         final HttpAuthSchemeType schemeType;
  1248         final HttpAuthSchemeType schemeType;
  1096         final HttpTestAuthenticator authenticator;
  1249         final HttpTestAuthenticator authenticator;
  1097         private final byte[] nonce;
  1250         private final byte[] nonce;
  1098         private final String ns;
  1251         private final String ns;
  1099 
  1252         private final String key;
  1100         ProxyAuthorization(HttpAuthSchemeType schemeType, HttpTestAuthenticator auth) {
  1253 
       
  1254         ProxyAuthorization(String key, HttpAuthSchemeType schemeType, HttpTestAuthenticator auth) {
       
  1255             this.key = key;
  1101             this.schemeType = schemeType;
  1256             this.schemeType = schemeType;
  1102             this.authenticator = auth;
  1257             this.authenticator = auth;
  1103             nonce = new byte[16];
  1258             nonce = new byte[16];
  1104             new Random(Instant.now().toEpochMilli()).nextBytes(nonce);
  1259             new Random(Instant.now().toEpochMilli()).nextBytes(nonce);
  1105             ns = new BigInteger(1, nonce).toString(16);
  1260             ns = new BigInteger(1, nonce).toString(16);
  1127             char[] pw = authenticator.getPassword(u);
  1282             char[] pw = authenticator.getPassword(u);
  1128             if (!p.equals(new String(pw))) {
  1283             if (!p.equals(new String(pw))) {
  1129                 return "Proxy-Authenticate: BASIC " + "realm=\""
  1284                 return "Proxy-Authenticate: BASIC " + "realm=\""
  1130                         + authenticator.getRealm() +"\"";
  1285                         + authenticator.getRealm() +"\"";
  1131             }
  1286             }
  1132             System.out.println(now() + " Proxy basic authentication success");
  1287             System.out.println(now() + key + " Proxy basic authentication success");
  1133             return null;
  1288             return null;
  1134         }
  1289         }
  1135 
  1290 
  1136         String doDigest(Optional<String> authorization) {
  1291         String doDigest(Optional<String> authorization) {
  1137             String offset = "proxy-authorization: digest ";
  1292             String offset = "proxy-authorization: digest ";
  1163 
  1318 
  1164 
  1319 
  1165 
  1320 
  1166 
  1321 
  1167         boolean validate(String reqMethod, DigestResponse dg) {
  1322         boolean validate(String reqMethod, DigestResponse dg) {
  1168             String type = now() + this.getClass().getSimpleName();
  1323             String type = now() + this.getClass().getSimpleName() + ":" + key;
  1169             if (!"MD5".equalsIgnoreCase(dg.getAlgorithm("MD5"))) {
  1324             if (!"MD5".equalsIgnoreCase(dg.getAlgorithm("MD5"))) {
  1170                 System.out.println(type + ": Unsupported algorithm "
  1325                 System.out.println(type + ": Unsupported algorithm "
  1171                         + dg.algorithm);
  1326                         + dg.algorithm);
  1172                 return false;
  1327                 return false;
  1173             }
  1328             }
  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");
  1295             }
  1456             }
  1296         }
  1457         }
  1297 
  1458 
  1298 
  1459 
  1299         @Override
  1460         @Override
  1300         void configureAuthentication(HttpContext ctxt,
  1461         void configureAuthentication(HttpTestContext ctxt,
  1301                                      HttpAuthSchemeType schemeType,
  1462                                      HttpAuthSchemeType schemeType,
  1302                                      HttpTestAuthenticator auth,
  1463                                      HttpTestAuthenticator auth,
  1303                                      HttpAuthType authType) {
  1464                                      HttpAuthType authType) {
  1304             if (authType == HttpAuthType.PROXY || authType == HttpAuthType.PROXY305) {
  1465             if (authType == HttpAuthType.PROXY || authType == HttpAuthType.PROXY305) {
  1305                 authorization = new ProxyAuthorization(schemeType, auth);
  1466                 authorization = new ProxyAuthorization(key, schemeType, auth);
  1306             } else {
  1467             } else {
  1307                 super.configureAuthentication(ctxt, schemeType, auth, authType);
  1468                 super.configureAuthentication(ctxt, schemeType, auth, authType);
  1308             }
  1469             }
  1309         }
  1470         }
  1310 
  1471 
  1350         }
  1511         }
  1351         public InetSocketAddress getProxyAddress() {
  1512         public InetSocketAddress getProxyAddress() {
  1352             return getAddress();
  1513             return getAddress();
  1353         }
  1514         }
  1354         public InetSocketAddress getServerAddress() {
  1515         public InetSocketAddress getServerAddress() {
  1355             return serverImpl.getAddress();
  1516             return new InetSocketAddress("127.0.0.1",
       
  1517                     serverImpl.getAddress().getPort());
  1356         }
  1518         }
  1357 
  1519 
  1358 
  1520 
  1359         // This is a bit shaky. It doesn't handle continuation
  1521         // This is a bit shaky. It doesn't handle continuation
  1360         // lines, but our client shouldn't send any.
  1522         // lines, but our client shouldn't send any.