test/jdk/java/net/httpclient/HttpServerAdapters.java
changeset 49765 ee6f7a61f3a5
child 50681 4254bed3c09d
child 56451 9585061fdb04
equal deleted inserted replaced
49707:f7fd051519ac 49765:ee6f7a61f3a5
       
     1 /*
       
     2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 import com.sun.net.httpserver.Filter;
       
    25 import com.sun.net.httpserver.Headers;
       
    26 import com.sun.net.httpserver.HttpContext;
       
    27 import com.sun.net.httpserver.HttpExchange;
       
    28 import com.sun.net.httpserver.HttpHandler;
       
    29 import com.sun.net.httpserver.HttpServer;
       
    30 
       
    31 import java.net.InetAddress;
       
    32 import java.io.ByteArrayInputStream;
       
    33 import java.net.http.HttpClient.Version;
       
    34 import jdk.internal.net.http.common.HttpHeadersImpl;
       
    35 import java.io.ByteArrayOutputStream;
       
    36 import java.io.IOException;
       
    37 import java.io.InputStream;
       
    38 import java.io.OutputStream;
       
    39 import java.io.PrintStream;
       
    40 import java.io.UncheckedIOException;
       
    41 import java.math.BigInteger;
       
    42 import java.net.InetSocketAddress;
       
    43 import java.net.URI;
       
    44 import java.util.List;
       
    45 import java.util.ListIterator;
       
    46 import java.util.Map;
       
    47 import java.util.Optional;
       
    48 import java.util.Set;
       
    49 import java.util.concurrent.CopyOnWriteArrayList;
       
    50 import java.util.logging.Level;
       
    51 import java.util.logging.Logger;
       
    52 import java.util.stream.Stream;
       
    53 
       
    54 /**
       
    55  * Defines an adaptation layers so that a test server handlers and filters
       
    56  * can be implemented independently of the underlying server version.
       
    57  * <p>
       
    58  * For instance:
       
    59  * <pre>{@code
       
    60  *
       
    61  *  URI http1URI, http2URI;
       
    62  *
       
    63  *  InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
       
    64  *  HttpTestServer server1 = HttpTestServer.of(HttpServer.create(sa, 0));
       
    65  *  HttpTestContext context = server.addHandler(new HttpTestEchoHandler(), "/http1/echo");
       
    66  *  http2URI = "http://localhost:" + server1.getAddress().getPort() + "/http1/echo";
       
    67  *
       
    68  *  Http2TestServer http2TestServer = new Http2TestServer("localhost", false, 0);
       
    69  *  HttpTestServer server2 = HttpTestServer.of(http2TestServer);
       
    70  *  server2.addHandler(new HttpTestEchoHandler(), "/http2/echo");
       
    71  *  http1URI = "http://localhost:" + server2.getAddress().getPort() + "/http2/echo";
       
    72  *
       
    73  *  }</pre>
       
    74  */
       
    75 public interface HttpServerAdapters {
       
    76 
       
    77     static final boolean PRINTSTACK =
       
    78             Boolean.getBoolean("jdk.internal.httpclient.debug");
       
    79 
       
    80     static void uncheckedWrite(ByteArrayOutputStream baos, byte[] ba) {
       
    81         try {
       
    82             baos.write(ba);
       
    83         } catch (IOException e) {
       
    84             throw new UncheckedIOException(e);
       
    85         }
       
    86     }
       
    87 
       
    88     static void printBytes(PrintStream out, String prefix, byte[] bytes) {
       
    89         int padding = 4 + 4 - (bytes.length % 4);
       
    90         padding = padding > 4 ? padding - 4 : 4;
       
    91         byte[] bigbytes = new byte[bytes.length + padding];
       
    92         System.arraycopy(bytes, 0, bigbytes, padding, bytes.length);
       
    93         out.println(prefix + bytes.length + " "
       
    94                     + new BigInteger(bigbytes).toString(16));
       
    95     }
       
    96 
       
    97     /**
       
    98      * A version agnostic adapter class for HTTP Headers.
       
    99      */
       
   100     public static abstract class HttpTestHeaders {
       
   101         public abstract Optional<String> firstValue(String name);
       
   102         public abstract void addHeader(String name, String value);
       
   103         public abstract Set<String> keySet();
       
   104         public abstract Set<Map.Entry<String, List<String>>> entrySet();
       
   105         public abstract List<String> get(String name);
       
   106         public abstract boolean containsKey(String name);
       
   107 
       
   108         public static HttpTestHeaders of(Headers headers) {
       
   109             return new Http1TestHeaders(headers);
       
   110         }
       
   111         public static HttpTestHeaders of(HttpHeadersImpl headers) {
       
   112             return new Http2TestHeaders(headers);
       
   113         }
       
   114 
       
   115         private final static class Http1TestHeaders extends HttpTestHeaders {
       
   116             private final Headers headers;
       
   117             Http1TestHeaders(Headers h) { this.headers = h; }
       
   118             @Override
       
   119             public Optional<String> firstValue(String name) {
       
   120                 if (headers.containsKey(name)) {
       
   121                     return Optional.ofNullable(headers.getFirst(name));
       
   122                 }
       
   123                 return Optional.empty();
       
   124             }
       
   125             @Override
       
   126             public void addHeader(String name, String value) {
       
   127                 headers.add(name, value);
       
   128             }
       
   129 
       
   130             @Override
       
   131             public Set<String> keySet() { return headers.keySet(); }
       
   132             @Override
       
   133             public Set<Map.Entry<String, List<String>>> entrySet() {
       
   134                 return headers.entrySet();
       
   135             }
       
   136             @Override
       
   137             public List<String> get(String name) {
       
   138                 return headers.get(name);
       
   139             }
       
   140             @Override
       
   141             public boolean containsKey(String name) {
       
   142                 return headers.containsKey(name);
       
   143             }
       
   144         }
       
   145         private final static class Http2TestHeaders extends HttpTestHeaders {
       
   146             private final HttpHeadersImpl headers;
       
   147             Http2TestHeaders(HttpHeadersImpl h) { this.headers = h; }
       
   148             @Override
       
   149             public Optional<String> firstValue(String name) {
       
   150                 return headers.firstValue(name);
       
   151             }
       
   152             @Override
       
   153             public void addHeader(String name, String value) {
       
   154                 headers.addHeader(name, value);
       
   155             }
       
   156             public Set<String> keySet() { return headers.map().keySet(); }
       
   157             @Override
       
   158             public Set<Map.Entry<String, List<String>>> entrySet() {
       
   159                 return headers.map().entrySet();
       
   160             }
       
   161             @Override
       
   162             public List<String> get(String name) {
       
   163                 return headers.allValues(name);
       
   164             }
       
   165             @Override
       
   166             public boolean containsKey(String name) {
       
   167                 return headers.firstValue(name).isPresent();
       
   168             }
       
   169         }
       
   170     }
       
   171 
       
   172     /**
       
   173      * A version agnostic adapter class for HTTP Server Exchange.
       
   174      */
       
   175     public static abstract class HttpTestExchange {
       
   176         public abstract Version getServerVersion();
       
   177         public abstract Version getExchangeVersion();
       
   178         public abstract InputStream   getRequestBody();
       
   179         public abstract OutputStream  getResponseBody();
       
   180         public abstract HttpTestHeaders getRequestHeaders();
       
   181         public abstract HttpTestHeaders getResponseHeaders();
       
   182         public abstract void sendResponseHeaders(int code, int contentLength) throws IOException;
       
   183         public abstract URI getRequestURI();
       
   184         public abstract String getRequestMethod();
       
   185         public abstract void close();
       
   186         public void serverPush(URI uri, HttpTestHeaders headers, byte[] body) {
       
   187             ByteArrayInputStream bais = new ByteArrayInputStream(body);
       
   188             serverPush(uri, headers, bais);
       
   189         }
       
   190         public void serverPush(URI uri, HttpTestHeaders headers, InputStream body) {
       
   191             throw new UnsupportedOperationException("serverPush with " + getExchangeVersion());
       
   192         }
       
   193         public boolean serverPushAllowed() {
       
   194             return false;
       
   195         }
       
   196         public static HttpTestExchange of(HttpExchange exchange) {
       
   197             return new Http1TestExchange(exchange);
       
   198         }
       
   199         public static HttpTestExchange of(Http2TestExchange exchange) {
       
   200             return new Http2TestExchangeImpl(exchange);
       
   201         }
       
   202 
       
   203         abstract void doFilter(Filter.Chain chain) throws IOException;
       
   204 
       
   205         // implementations...
       
   206         private static final class Http1TestExchange extends HttpTestExchange {
       
   207             private final HttpExchange exchange;
       
   208             Http1TestExchange(HttpExchange exch) {
       
   209                 this.exchange = exch;
       
   210             }
       
   211             @Override
       
   212             public Version getServerVersion() { return Version.HTTP_1_1; }
       
   213             @Override
       
   214             public Version getExchangeVersion() { return Version.HTTP_1_1; }
       
   215             @Override
       
   216             public InputStream getRequestBody() {
       
   217                 return exchange.getRequestBody();
       
   218             }
       
   219             @Override
       
   220             public OutputStream getResponseBody() {
       
   221                 return exchange.getResponseBody();
       
   222             }
       
   223             @Override
       
   224             public HttpTestHeaders getRequestHeaders() {
       
   225                 return HttpTestHeaders.of(exchange.getRequestHeaders());
       
   226             }
       
   227             @Override
       
   228             public HttpTestHeaders getResponseHeaders() {
       
   229                 return HttpTestHeaders.of(exchange.getResponseHeaders());
       
   230             }
       
   231             @Override
       
   232             public void sendResponseHeaders(int code, int contentLength) throws IOException {
       
   233                 if (contentLength == 0) contentLength = -1;
       
   234                 else if (contentLength < 0) contentLength = 0;
       
   235                 exchange.sendResponseHeaders(code, contentLength);
       
   236             }
       
   237             @Override
       
   238             void doFilter(Filter.Chain chain) throws IOException {
       
   239                 chain.doFilter(exchange);
       
   240             }
       
   241             @Override
       
   242             public void close() { exchange.close(); }
       
   243             @Override
       
   244             public URI getRequestURI() { return exchange.getRequestURI(); }
       
   245             @Override
       
   246             public String getRequestMethod() { return exchange.getRequestMethod(); }
       
   247             @Override
       
   248             public String toString() {
       
   249                 return this.getClass().getSimpleName() + ": " + exchange.toString();
       
   250             }
       
   251         }
       
   252 
       
   253         private static final class Http2TestExchangeImpl extends HttpTestExchange {
       
   254             private final Http2TestExchange exchange;
       
   255             Http2TestExchangeImpl(Http2TestExchange exch) {
       
   256                 this.exchange = exch;
       
   257             }
       
   258             @Override
       
   259             public Version getServerVersion() { return Version.HTTP_2; }
       
   260             @Override
       
   261             public Version getExchangeVersion() { return Version.HTTP_2; }
       
   262             @Override
       
   263             public InputStream getRequestBody() {
       
   264                 return exchange.getRequestBody();
       
   265             }
       
   266             @Override
       
   267             public OutputStream getResponseBody() {
       
   268                 return exchange.getResponseBody();
       
   269             }
       
   270             @Override
       
   271             public HttpTestHeaders getRequestHeaders() {
       
   272                 return HttpTestHeaders.of(exchange.getRequestHeaders());
       
   273             }
       
   274             @Override
       
   275             public HttpTestHeaders getResponseHeaders() {
       
   276                 return HttpTestHeaders.of(exchange.getResponseHeaders());
       
   277             }
       
   278             @Override
       
   279             public void sendResponseHeaders(int code, int contentLength) throws IOException {
       
   280                 if (contentLength == 0) contentLength = -1;
       
   281                 else if (contentLength < 0) contentLength = 0;
       
   282                 exchange.sendResponseHeaders(code, contentLength);
       
   283             }
       
   284             @Override
       
   285             public boolean serverPushAllowed() {
       
   286                 return exchange.serverPushAllowed();
       
   287             }
       
   288             @Override
       
   289             public void serverPush(URI uri, HttpTestHeaders headers, InputStream body) {
       
   290                 HttpHeadersImpl headersImpl;
       
   291                 if (headers instanceof HttpTestHeaders.Http2TestHeaders) {
       
   292                     headersImpl = ((HttpTestHeaders.Http2TestHeaders)headers).headers.deepCopy();
       
   293                 } else {
       
   294                     headersImpl = new HttpHeadersImpl();
       
   295                     for (Map.Entry<String, List<String>> e : headers.entrySet()) {
       
   296                         String name = e.getKey();
       
   297                         for (String v : e.getValue()) {
       
   298                             headersImpl.addHeader(name, v);
       
   299                         }
       
   300                     }
       
   301                 }
       
   302                 exchange.serverPush(uri, headersImpl, body);
       
   303             }
       
   304             void doFilter(Filter.Chain filter) throws IOException {
       
   305                 throw new IOException("cannot use HTTP/1.1 filter with HTTP/2 server");
       
   306             }
       
   307             @Override
       
   308             public void close() { exchange.close();}
       
   309             @Override
       
   310             public URI getRequestURI() { return exchange.getRequestURI(); }
       
   311             @Override
       
   312             public String getRequestMethod() { return exchange.getRequestMethod(); }
       
   313             @Override
       
   314             public String toString() {
       
   315                 return this.getClass().getSimpleName() + ": " + exchange.toString();
       
   316             }
       
   317         }
       
   318 
       
   319     }
       
   320 
       
   321 
       
   322     /**
       
   323      * A version agnostic adapter class for HTTP Server Handlers.
       
   324      */
       
   325     public interface HttpTestHandler {
       
   326         void handle(HttpTestExchange t) throws IOException;
       
   327 
       
   328         default HttpHandler toHttpHandler() {
       
   329             return (t) -> doHandle(HttpTestExchange.of(t));
       
   330         }
       
   331         default Http2Handler toHttp2Handler() {
       
   332             return (t) -> doHandle(HttpTestExchange.of(t));
       
   333         }
       
   334         private void doHandle(HttpTestExchange t) throws IOException {
       
   335             try {
       
   336                 handle(t);
       
   337             } catch (Throwable x) {
       
   338                 System.out.println("WARNING: exception caught in HttpTestHandler::handle " + x);
       
   339                 System.err.println("WARNING: exception caught in HttpTestHandler::handle " + x);
       
   340                 if (PRINTSTACK && !expectException(t)) x.printStackTrace(System.out);
       
   341                 throw x;
       
   342             }
       
   343         }
       
   344     }
       
   345 
       
   346 
       
   347     public static class HttpTestEchoHandler implements HttpTestHandler {
       
   348         @Override
       
   349         public void handle(HttpTestExchange t) throws IOException {
       
   350             try (InputStream is = t.getRequestBody();
       
   351                  OutputStream os = t.getResponseBody()) {
       
   352                 byte[] bytes = is.readAllBytes();
       
   353                 printBytes(System.out,"Echo server got "
       
   354                         + t.getExchangeVersion() + " bytes: ", bytes);
       
   355                 if (t.getRequestHeaders().firstValue("Content-type").isPresent()) {
       
   356                     t.getResponseHeaders().addHeader("Content-type",
       
   357                             t.getRequestHeaders().firstValue("Content-type").get());
       
   358                 }
       
   359                 t.sendResponseHeaders(200, bytes.length);
       
   360                 os.write(bytes);
       
   361             }
       
   362         }
       
   363     }
       
   364 
       
   365     public static boolean expectException(HttpTestExchange e) {
       
   366         HttpTestHeaders h = e.getRequestHeaders();
       
   367         Optional<String> expectException = h.firstValue("X-expect-exception");
       
   368         if (expectException.isPresent()) {
       
   369             return expectException.get().equalsIgnoreCase("true");
       
   370         }
       
   371         return false;
       
   372     }
       
   373 
       
   374     /**
       
   375      * A version agnostic adapter class for HTTP Server Filter Chains.
       
   376      */
       
   377     public abstract class HttpChain {
       
   378 
       
   379         public abstract void doFilter(HttpTestExchange exchange) throws IOException;
       
   380         public static HttpChain of(Filter.Chain chain) {
       
   381             return new Http1Chain(chain);
       
   382         }
       
   383 
       
   384         public static HttpChain of(List<HttpTestFilter> filters, HttpTestHandler handler) {
       
   385             return new Http2Chain(filters, handler);
       
   386         }
       
   387 
       
   388         private static class Http1Chain extends HttpChain {
       
   389             final Filter.Chain chain;
       
   390             Http1Chain(Filter.Chain chain) {
       
   391                 this.chain = chain;
       
   392             }
       
   393             @Override
       
   394             public void doFilter(HttpTestExchange exchange) throws IOException {
       
   395                 try {
       
   396                     exchange.doFilter(chain);
       
   397                 } catch (Throwable t) {
       
   398                     System.out.println("WARNING: exception caught in Http1Chain::doFilter " + t);
       
   399                     System.err.println("WARNING: exception caught in Http1Chain::doFilter " + t);
       
   400                     if (PRINTSTACK && !expectException(exchange)) t.printStackTrace(System.out);
       
   401                     throw t;
       
   402                 }
       
   403             }
       
   404         }
       
   405 
       
   406         private static class Http2Chain extends HttpChain {
       
   407             ListIterator<HttpTestFilter> iter;
       
   408             HttpTestHandler handler;
       
   409             Http2Chain(List<HttpTestFilter> filters, HttpTestHandler handler) {
       
   410                 this.iter = filters.listIterator();
       
   411                 this.handler = handler;
       
   412             }
       
   413             @Override
       
   414             public void doFilter(HttpTestExchange exchange) throws IOException {
       
   415                 try {
       
   416                     if (iter.hasNext()) {
       
   417                         iter.next().doFilter(exchange, this);
       
   418                     } else {
       
   419                         handler.handle(exchange);
       
   420                     }
       
   421                 } catch (Throwable t) {
       
   422                     System.out.println("WARNING: exception caught in Http2Chain::doFilter " + t);
       
   423                     System.err.println("WARNING: exception caught in Http2Chain::doFilter " + t);
       
   424                     if (PRINTSTACK && !expectException(exchange)) t.printStackTrace(System.out);
       
   425                     throw t;
       
   426                 }
       
   427             }
       
   428         }
       
   429 
       
   430     }
       
   431 
       
   432     /**
       
   433      * A version agnostic adapter class for HTTP Server Filters.
       
   434      */
       
   435     public abstract class HttpTestFilter {
       
   436 
       
   437         public abstract String description();
       
   438 
       
   439         public abstract void doFilter(HttpTestExchange exchange, HttpChain chain) throws IOException;
       
   440 
       
   441         public Filter toFilter() {
       
   442             return new Filter() {
       
   443                 @Override
       
   444                 public void doFilter(HttpExchange exchange, Chain chain) throws IOException {
       
   445                     HttpTestFilter.this.doFilter(HttpTestExchange.of(exchange), HttpChain.of(chain));
       
   446                 }
       
   447                 @Override
       
   448                 public String description() {
       
   449                     return HttpTestFilter.this.description();
       
   450                 }
       
   451             };
       
   452         }
       
   453     }
       
   454 
       
   455     /**
       
   456      * A version agnostic adapter class for HTTP Server Context.
       
   457      */
       
   458     public static abstract class HttpTestContext {
       
   459         public abstract String getPath();
       
   460         public abstract void addFilter(HttpTestFilter filter);
       
   461         public abstract Version getVersion();
       
   462 
       
   463         // will throw UOE if the server is HTTP/2
       
   464         public abstract void setAuthenticator(com.sun.net.httpserver.Authenticator authenticator);
       
   465     }
       
   466 
       
   467     /**
       
   468      * A version agnostic adapter class for HTTP Servers.
       
   469      */
       
   470     public static abstract class HttpTestServer {
       
   471         private static final class ServerLogging {
       
   472             private static final Logger logger = Logger.getLogger("com.sun.net.httpserver");
       
   473             static void enableLogging() {
       
   474                 logger.setLevel(Level.FINE);
       
   475                 Stream.of(Logger.getLogger("").getHandlers())
       
   476                         .forEach(h -> h.setLevel(Level.ALL));
       
   477             }
       
   478         }
       
   479 
       
   480         public abstract void start();
       
   481         public abstract void stop();
       
   482         public abstract HttpTestContext addHandler(HttpTestHandler handler, String root);
       
   483         public abstract InetSocketAddress getAddress();
       
   484         public abstract Version getVersion();
       
   485 
       
   486         public String serverAuthority() {
       
   487             return InetAddress.getLoopbackAddress().getHostName() + ":"
       
   488                     + getAddress().getPort();
       
   489         }
       
   490 
       
   491         public static HttpTestServer of(HttpServer server) {
       
   492             return new Http1TestServer(server);
       
   493         }
       
   494 
       
   495         public static HttpTestServer of(Http2TestServer server) {
       
   496             return new Http2TestServerImpl(server);
       
   497         }
       
   498 
       
   499         private static class Http1TestServer extends  HttpTestServer {
       
   500             private final HttpServer impl;
       
   501             Http1TestServer(HttpServer server) {
       
   502                 this.impl = server;
       
   503             }
       
   504             @Override
       
   505             public void start() {
       
   506                 System.out.println("Http1TestServer: start");
       
   507                 impl.start();
       
   508             }
       
   509             @Override
       
   510             public void stop() {
       
   511                 System.out.println("Http1TestServer: stop");
       
   512                 impl.stop(0);
       
   513             }
       
   514             @Override
       
   515             public HttpTestContext addHandler(HttpTestHandler handler, String path) {
       
   516                 System.out.println("Http1TestServer[" + getAddress()
       
   517                         + "]::addHandler " + handler + ", " + path);
       
   518                 return new Http1TestContext(impl.createContext(path, handler.toHttpHandler()));
       
   519             }
       
   520             @Override
       
   521             public InetSocketAddress getAddress() {
       
   522                 return new InetSocketAddress(InetAddress.getLoopbackAddress(),
       
   523                         impl.getAddress().getPort());
       
   524             }
       
   525             public Version getVersion() { return Version.HTTP_1_1; }
       
   526         }
       
   527 
       
   528         private static class Http1TestContext extends HttpTestContext {
       
   529             private final HttpContext context;
       
   530             Http1TestContext(HttpContext ctxt) {
       
   531                 this.context = ctxt;
       
   532             }
       
   533             @Override public String getPath() {
       
   534                 return context.getPath();
       
   535             }
       
   536             @Override
       
   537             public void addFilter(HttpTestFilter filter) {
       
   538                 System.out.println("Http1TestContext::addFilter " + filter.description());
       
   539                 context.getFilters().add(filter.toFilter());
       
   540             }
       
   541             @Override
       
   542             public void setAuthenticator(com.sun.net.httpserver.Authenticator authenticator) {
       
   543                 context.setAuthenticator(authenticator);
       
   544             }
       
   545             @Override public Version getVersion() { return Version.HTTP_1_1; }
       
   546         }
       
   547 
       
   548         private static class Http2TestServerImpl extends  HttpTestServer {
       
   549             private final Http2TestServer impl;
       
   550             Http2TestServerImpl(Http2TestServer server) {
       
   551                 this.impl = server;
       
   552             }
       
   553             @Override
       
   554             public void start() {
       
   555                 System.out.println("Http2TestServerImpl: start");
       
   556                 impl.start();
       
   557             }
       
   558             @Override
       
   559             public void stop() {
       
   560                 System.out.println("Http2TestServerImpl: stop");
       
   561                 impl.stop();
       
   562             }
       
   563             @Override
       
   564             public HttpTestContext addHandler(HttpTestHandler handler, String path) {
       
   565                 System.out.println("Http2TestServerImpl[" + getAddress()
       
   566                                    + "]::addHandler " + handler + ", " + path);
       
   567                 Http2TestContext context = new Http2TestContext(handler, path);
       
   568                 impl.addHandler(context.toHttp2Handler(), path);
       
   569                 return context;
       
   570             }
       
   571             @Override
       
   572             public InetSocketAddress getAddress() {
       
   573                 return new InetSocketAddress(InetAddress.getLoopbackAddress(),
       
   574                         impl.getAddress().getPort());
       
   575             }
       
   576             public Version getVersion() { return Version.HTTP_2; }
       
   577         }
       
   578 
       
   579         private static class Http2TestContext
       
   580                 extends HttpTestContext implements HttpTestHandler {
       
   581             private final HttpTestHandler handler;
       
   582             private final String path;
       
   583             private final List<HttpTestFilter> filters = new CopyOnWriteArrayList<>();
       
   584             Http2TestContext(HttpTestHandler hdl, String path) {
       
   585                 this.handler = hdl;
       
   586                 this.path = path;
       
   587             }
       
   588             @Override
       
   589             public String getPath() { return path; }
       
   590             @Override
       
   591             public void addFilter(HttpTestFilter filter) {
       
   592                 System.out.println("Http2TestContext::addFilter " + filter.description());
       
   593                 filters.add(filter);
       
   594             }
       
   595             @Override
       
   596             public void handle(HttpTestExchange exchange) throws IOException {
       
   597                 System.out.println("Http2TestContext::handle " + exchange);
       
   598                 HttpChain.of(filters, handler).doFilter(exchange);
       
   599             }
       
   600             @Override
       
   601             public void setAuthenticator(com.sun.net.httpserver.Authenticator authenticator) {
       
   602                 throw new UnsupportedOperationException("Can't set HTTP/1.1 authenticator on HTTP/2 context");
       
   603             }
       
   604             @Override public Version getVersion() { return Version.HTTP_2; }
       
   605         }
       
   606     }
       
   607 
       
   608     public static void enableServerLogging() {
       
   609         System.setProperty("java.util.logging.SimpleFormatter.format",
       
   610                 "%4$s [%1$tb %1$td, %1$tl:%1$tM:%1$tS.%1$tN] %2$s: %5$s%6$s%n");
       
   611         HttpTestServer.ServerLogging.enableLogging();
       
   612     }
       
   613 
       
   614 }