package io.dropwizard.http2; import com.codahale.metrics.MetricRegistry; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeName; import com.google.common.collect.ImmutableList; import io.dropwizard.jetty.HttpsConnectorFactory; import io.dropwizard.jetty.Jetty93InstrumentedConnectionFactory; import io.dropwizard.jetty.SslReload; import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory; import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.NegotiatingServerConnectionFactory; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; import org.eclipse.jetty.util.thread.ThreadPool; import javax.validation.constraints.Max; import javax.validation.constraints.Min; /** * Builds HTTP/2 over TLS (h2) connectors. * <p/> * <b>Configuration Parameters:</b> * <table> * <tr> * <td>Name</td> * <td>Default</td> * <td>Description</td> * </tr> * <tr> * <td>{@code maxConcurrentStreams}</td> * <td><1024</td> * <td> * The maximum number of concurrently open streams allowed on a single HTTP/2 connection. * Larger values increase parallelism, but cost a memory commitment. * </td> * </tr> * <tr> * <td>{@code initialStreamRecvWindow}</td> * <td>65535</td> * <td> * The initial flow control window size for a new stream. Larger values may allow greater throughput, * but also risk head of line blocking if TCP/IP flow control is triggered. * </td> * </tr> * </table> * <p/> * For more configuration parameters, see {@link HttpsConnectorFactory}. * * @see HttpsConnectorFactory */ @JsonTypeName("h2") public class Http2ConnectorFactory extends HttpsConnectorFactory { /** * Supported protocols */ private static final String H2 = "h2"; private static final String H2_17 = "h2-17"; private static final String HTTP_1_1 = "http/1.1"; @Min(100) @Max(Integer.MAX_VALUE) private int maxConcurrentStreams = 1024; @Min(1) @Max(Integer.MAX_VALUE) private int initialStreamRecvWindow = 65535; @JsonProperty public int getMaxConcurrentStreams() { return maxConcurrentStreams; } @JsonProperty public void setMaxConcurrentStreams(int maxConcurrentStreams) { this.maxConcurrentStreams = maxConcurrentStreams; } @JsonProperty public int getInitialStreamRecvWindow() { return initialStreamRecvWindow; } @JsonProperty public void setInitialStreamRecvWindow(int initialStreamRecvWindow) { this.initialStreamRecvWindow = initialStreamRecvWindow; } @Override public Connector build(Server server, MetricRegistry metrics, String name, ThreadPool threadPool) { // HTTP/2 requires that a server MUST support TLSv1.2 and TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 cipher // See http://http2.github.io/http2-spec/index.html#rfc.section.9.2.2 setSupportedProtocols(ImmutableList.of("TLSv1.2")); setSupportedCipherSuites(ImmutableList.of("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")); // Setup connection factories final HttpConfiguration httpConfig = buildHttpConfiguration(); final HttpConnectionFactory http1 = buildHttpConnectionFactory(httpConfig); final HTTP2ServerConnectionFactory http2 = new HTTP2ServerConnectionFactory(httpConfig); http2.setMaxConcurrentStreams(maxConcurrentStreams); http2.setInitialStreamRecvWindow(initialStreamRecvWindow); final NegotiatingServerConnectionFactory alpn = new ALPNServerConnectionFactory(H2, H2_17); alpn.setDefaultProtocol(HTTP_1_1); // Speak HTTP 1.1 over TLS if negotiation fails final SslContextFactory sslContextFactory = configureSslContextFactory(new SslContextFactory()); sslContextFactory.addLifeCycleListener(logSslInfoOnStart(sslContextFactory)); server.addBean(sslContextFactory); server.addBean(new SslReload(sslContextFactory, this::configureSslContextFactory)); // We should use ALPN as a negotiation protocol. Old clients that don't support it will be served // via HTTPS. New clients, however, that want to use HTTP/2 will use TLS with ALPN extension. // If negotiation succeeds, the client and server switch to HTTP/2 protocol. final SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(sslContextFactory, "alpn"); return buildConnector(server, new ScheduledExecutorScheduler(), buildBufferPool(), name, threadPool, new Jetty93InstrumentedConnectionFactory(sslConnectionFactory, metrics.timer(httpConnections())), alpn, http2, http1); } }