jdk/test/java/net/httpclient/http2/server/Http2TestServer.java
changeset 42460 7133f144981a
child 45713 ee3f2cbfe23a
equal deleted inserted replaced
42459:1ad58e0cbf16 42460:7133f144981a
       
     1 /*
       
     2  * Copyright (c) 2015, 2016, 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 java.io.IOException;
       
    25 import java.net.*;
       
    26 import java.util.*;
       
    27 import java.util.concurrent.ExecutorService;
       
    28 import java.util.concurrent.Executors;
       
    29 import java.util.concurrent.ThreadFactory;
       
    30 import java.util.concurrent.atomic.AtomicReference;
       
    31 import javax.net.ServerSocketFactory;
       
    32 import javax.net.ssl.SSLContext;
       
    33 import javax.net.ssl.SSLParameters;
       
    34 import javax.net.ssl.SSLServerSocket;
       
    35 import javax.net.ssl.SSLServerSocketFactory;
       
    36 
       
    37 /**
       
    38  * Waits for incoming TCP connections from a client and establishes
       
    39  * a HTTP2 connection. Two threads are created per connection. One for reading
       
    40  * and one for writing. Incoming requests are dispatched to the supplied
       
    41  * Http2Handler on additional threads. All threads
       
    42  * obtained from the supplied ExecutorService.
       
    43  */
       
    44 public class Http2TestServer implements AutoCloseable {
       
    45     final ServerSocket server;
       
    46     volatile boolean secure;
       
    47     final ExecutorService exec;
       
    48     volatile boolean stopping = false;
       
    49     final Map<String,Http2Handler> handlers;
       
    50     final SSLContext sslContext;
       
    51     final HashMap<InetSocketAddress,Http2TestServerConnection> connections;
       
    52 
       
    53     private static ThreadFactory defaultThreadFac =
       
    54         (Runnable r) -> {
       
    55             Thread t = new Thread(r);
       
    56             t.setName("Test-server-pool");
       
    57             return t;
       
    58         };
       
    59 
       
    60 
       
    61     private static ExecutorService getDefaultExecutor() {
       
    62         return Executors.newCachedThreadPool(defaultThreadFac);
       
    63     }
       
    64 
       
    65     public Http2TestServer(boolean secure, int port) throws Exception {
       
    66         this(secure, port, getDefaultExecutor(), null);
       
    67     }
       
    68 
       
    69     public InetSocketAddress getAddress() {
       
    70         return (InetSocketAddress)server.getLocalSocketAddress();
       
    71     }
       
    72 
       
    73     public Http2TestServer(boolean secure,
       
    74                            SSLContext context) throws Exception {
       
    75         this(secure, 0, null, context);
       
    76     }
       
    77 
       
    78     /**
       
    79      * Create a Http2Server listening on the given port. Currently needs
       
    80      * to know in advance whether incoming connections are plain TCP "h2c"
       
    81      * or TLS "h2"/
       
    82      *
       
    83      * @param secure https or http
       
    84      * @param port listen port
       
    85      * @param exec executor service (cached thread pool is used if null)
       
    86      * @param context the SSLContext used when secure is true
       
    87      */
       
    88     public Http2TestServer(boolean secure,
       
    89                            int port,
       
    90                            ExecutorService exec,
       
    91                            SSLContext context)
       
    92         throws Exception
       
    93     {
       
    94         if (secure) {
       
    95             server = initSecure(port);
       
    96         } else {
       
    97             server = initPlaintext(port);
       
    98         }
       
    99         this.secure = secure;
       
   100         this.exec = exec == null ? getDefaultExecutor() : exec;
       
   101         this.handlers = Collections.synchronizedMap(new HashMap<>());
       
   102         this.sslContext = context;
       
   103         this.connections = new HashMap<>();
       
   104     }
       
   105 
       
   106     /**
       
   107      * Adds the given handler for the given path
       
   108      */
       
   109     public void addHandler(Http2Handler handler, String path) {
       
   110         handlers.put(path, handler);
       
   111     }
       
   112 
       
   113     Http2Handler getHandlerFor(String path) {
       
   114         if (path == null || path.equals(""))
       
   115             path = "/";
       
   116 
       
   117         final String fpath = path;
       
   118         AtomicReference<String> bestMatch = new AtomicReference<>("");
       
   119         AtomicReference<Http2Handler> href = new AtomicReference<>();
       
   120 
       
   121         handlers.forEach((key, value) -> {
       
   122             if (fpath.startsWith(key) && key.length() > bestMatch.get().length()) {
       
   123                 bestMatch.set(key);
       
   124                 href.set(value);
       
   125             }
       
   126         });
       
   127         Http2Handler handler = href.get();
       
   128         if (handler == null)
       
   129             throw new RuntimeException("No handler found for path " + path);
       
   130         System.err.println("Using handler for: " + bestMatch.get());
       
   131         return handler;
       
   132     }
       
   133 
       
   134     final ServerSocket initPlaintext(int port) throws Exception {
       
   135         return new ServerSocket(port);
       
   136     }
       
   137 
       
   138     public void stop() {
       
   139         // TODO: clean shutdown GoAway
       
   140         stopping = true;
       
   141         for (Http2TestServerConnection connection : connections.values()) {
       
   142             connection.close();
       
   143         }
       
   144         try {
       
   145             server.close();
       
   146         } catch (IOException e) {}
       
   147         exec.shutdownNow();
       
   148     }
       
   149 
       
   150 
       
   151     final ServerSocket initSecure(int port) throws Exception {
       
   152         ServerSocketFactory fac;
       
   153         if (sslContext != null) {
       
   154             fac = sslContext.getServerSocketFactory();
       
   155         } else {
       
   156             fac = SSLServerSocketFactory.getDefault();
       
   157         }
       
   158         SSLServerSocket se = (SSLServerSocket) fac.createServerSocket(port);
       
   159         SSLParameters sslp = se.getSSLParameters();
       
   160         sslp.setApplicationProtocols(new String[]{"h2"});
       
   161         se.setSSLParameters(sslp);
       
   162         se.setEnabledCipherSuites(se.getSupportedCipherSuites());
       
   163         se.setEnabledProtocols(se.getSupportedProtocols());
       
   164         // other initialisation here
       
   165         return se;
       
   166     }
       
   167 
       
   168     /**
       
   169      * Starts a thread which waits for incoming connections.
       
   170      */
       
   171     public void start() {
       
   172         exec.submit(() -> {
       
   173             try {
       
   174                 while (!stopping) {
       
   175                     Socket socket = server.accept();
       
   176                     InetSocketAddress addr = (InetSocketAddress) socket.getRemoteSocketAddress();
       
   177                     Http2TestServerConnection c = new Http2TestServerConnection(this, socket);
       
   178                     connections.put(addr, c);
       
   179                     c.run();
       
   180                 }
       
   181             } catch (Throwable e) {
       
   182                 if (!stopping) {
       
   183                     System.err.println("TestServer: start exception: " + e);
       
   184                     e.printStackTrace();
       
   185                 }
       
   186             }
       
   187         });
       
   188     }
       
   189 
       
   190     @Override
       
   191     public void close() throws Exception {
       
   192         stop();
       
   193     }
       
   194 }