From bb9396e3a40abb49a542e829ec1a157fca9fb55a Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Fri, 3 Nov 2017 11:34:30 +0100 Subject: [PATCH] Enable HTTP/2 support for Tomcat and Undertow This commit enables HTTP/2 support for Tomcat and Undertow, for both Servlet-based and Reactive applications. Enabling the `server.http2.enabled` configuration flag is enough with Undertow. Tomcat has a few prerequisites: * Tomcat 8.5 requires JDK8+ and the proper libtcnative version installed on the host * Tomcat 9.0.x requires JDK9+ Closes gh-10043 --- .../src/main/asciidoc/howto.adoc | 48 +++++++++++++++++++ .../tomcat/SslConnectorCustomizer.java | 19 ++++---- .../TomcatReactiveWebServerFactory.java | 10 +++- .../tomcat/TomcatServletWebServerFactory.java | 10 +++- .../UndertowReactiveWebServerFactory.java | 4 ++ .../UndertowServletWebServerFactory.java | 4 ++ 6 files changed, 81 insertions(+), 14 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc index 22483d07175..9c9dc793919 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -734,6 +734,54 @@ sample project for an example. +[[howto-configure-http2]] +=== Configure HTTP/2 +You can enable HTTP/2 support in your Spring Boot application with the +`+server.http2.enabled+` configuration property. This support depends on the +chosen web server and the application environment, since that protocol is not +supported out-of-the-box by JDK8. + +[NOTE] +==== +Spring Boot does not support `h2c`, the cleartext version of the HTTP/2 +protocol. So you must configure <>. +==== + +Currently, only Undertow and Tomcat are supported with this configuration key. + + +[[howto-configure-http2-undertow]] +==== HTTP/2 with Undertow +As of Undertow 1.4.0+, HTTP/2 is supported without any additional requirement +on JDK8. + + +[[howto-configure-http2-tomcat]] +==== HTTP/2 with Tomcat +Spring Boot ships by default with Tomcat 8.5.x; with that version, +HTTP/2 is only supported if the `libtcnative` library and its dependencies +are installed on the host operating system. + +The library folder must be made available, if not already, to the JVM library +path; this can be done with a JVM argument such as +`-Djava.library.path=/usr/local/opt/tomcat-native/lib`. More on this in the +http://tomcat.apache.org/tomcat-8.5-doc/apr.html[official Tomcat +documentation]. + +Starting Tomcat 8.5.x without that native support will log the following error: + +[indent=0,subs="attributes"] +---- +ERROR 8787 --- [ main] o.a.coyote.http11.Http11NioProtocol : The upgrade handler [org.apache.coyote.http2.Http2Protocol] for [h2] only supports upgrade via ALPN but has been configured for the ["https-jsse-nio-8443"] connector that does not support ALPN. +---- + +This error is not fatal, and the application starts with HTTP/1.1 SSL +support still. + +Running your application with Tomcat 9.0.x and JDK9 doesn't require any native +library installed. To use Tomcat 9, you can override the `tomcat.version` +build property with the version of your choice. + [[howto-configure-accesslogs]] === Configure Access Logging Access logs can be configured for Tomcat, Undertow, and Jetty through their respective diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizer.java index 5b97e541e54..9dac144cc9d 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/SslConnectorCustomizer.java @@ -45,22 +45,21 @@ class SslConnectorCustomizer implements TomcatConnectorCustomizer { private final SslStoreProvider sslStoreProvider; SslConnectorCustomizer(Ssl ssl, SslStoreProvider sslStoreProvider) { + Assert.notNull(ssl, "Ssl configuration should not be null"); this.ssl = ssl; this.sslStoreProvider = sslStoreProvider; } @Override public void customize(Connector connector) { - if (this.ssl != null && this.ssl.isEnabled()) { - ProtocolHandler handler = connector.getProtocolHandler(); - Assert.state(handler instanceof AbstractHttp11JsseProtocol, - "To use SSL, the connector's protocol handler must be an " - + "AbstractHttp11JsseProtocol subclass"); - configureSsl((AbstractHttp11JsseProtocol) handler, - this.ssl, this.sslStoreProvider); - connector.setScheme("https"); - connector.setSecure(true); - } + ProtocolHandler handler = connector.getProtocolHandler(); + Assert.state(handler instanceof AbstractHttp11JsseProtocol, + "To use SSL, the connector's protocol handler must be an " + + "AbstractHttp11JsseProtocol subclass"); + configureSsl((AbstractHttp11JsseProtocol) handler, + this.ssl, this.sslStoreProvider); + connector.setScheme("https"); + connector.setSecure(true); } /** diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactory.java index 72704b39f2a..e2dfdf45615 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatReactiveWebServerFactory.java @@ -30,6 +30,7 @@ import org.apache.catalina.core.AprLifecycleListener; import org.apache.catalina.loader.WebappLoader; import org.apache.catalina.startup.Tomcat; import org.apache.coyote.AbstractProtocol; +import org.apache.coyote.http2.Http2Protocol; import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory; import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory; @@ -141,8 +142,13 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac // If ApplicationContext is slow to start we want Tomcat not to bind to the socket // prematurely... connector.setProperty("bindOnInit", "false"); - TomcatConnectorCustomizer ssl = new SslConnectorCustomizer(getSsl(), getSslStoreProvider()); - ssl.customize(connector); + if (getSsl() != null && getSsl().isEnabled()) { + TomcatConnectorCustomizer ssl = new SslConnectorCustomizer(getSsl(), getSslStoreProvider()); + ssl.customize(connector); + if (getHttp2() != null && getHttp2().getEnabled()) { + connector.addUpgradeProtocol(new Http2Protocol()); + } + } TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(getCompression()); compression.customize(connector); for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java index d63758e7ae4..eb2ee922f79 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java @@ -59,6 +59,7 @@ import org.apache.catalina.webresources.AbstractResourceSet; import org.apache.catalina.webresources.EmptyResource; import org.apache.catalina.webresources.StandardRoot; import org.apache.coyote.AbstractProtocol; +import org.apache.coyote.http2.Http2Protocol; import org.springframework.boot.web.server.ErrorPage; import org.springframework.boot.web.server.MimeMappings; @@ -297,8 +298,13 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto // prematurely... connector.setProperty("bindOnInit", "false"); - TomcatConnectorCustomizer ssl = new SslConnectorCustomizer(getSsl(), getSslStoreProvider()); - ssl.customize(connector); + if (getSsl() != null && getSsl().isEnabled()) { + TomcatConnectorCustomizer ssl = new SslConnectorCustomizer(getSsl(), getSslStoreProvider()); + ssl.customize(connector); + if (getHttp2() != null && getHttp2().getEnabled()) { + connector.addUpgradeProtocol(new Http2Protocol()); + } + } TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(getCompression()); compression.customize(connector); for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactory.java index 07e33a5f1f7..49115d2a0e3 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowReactiveWebServerFactory.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.List; import io.undertow.Undertow; +import io.undertow.UndertowOptions; import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory; import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory; @@ -89,6 +90,9 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF SslBuilderCustomizer sslBuilderCustomizer = new SslBuilderCustomizer(getPort(), getAddress(), getSsl(), getSslStoreProvider()); sslBuilderCustomizer.customize(builder); + if (getHttp2() != null) { + builder.setServerOption(UndertowOptions.ENABLE_HTTP2, getHttp2().getEnabled()); + } } else { builder.addHttpListener(port, getListenAddress()); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactory.java index 609ad8747f5..ce80dda3c06 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactory.java @@ -36,6 +36,7 @@ import javax.servlet.ServletException; import io.undertow.Undertow; import io.undertow.Undertow.Builder; +import io.undertow.UndertowOptions; import io.undertow.server.HttpHandler; import io.undertow.server.handlers.accesslog.AccessLogHandler; import io.undertow.server.handlers.accesslog.AccessLogReceiver; @@ -235,6 +236,9 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac SslBuilderCustomizer sslBuilderCustomizer = new SslBuilderCustomizer(getPort(), getAddress(), getSsl(), getSslStoreProvider()); sslBuilderCustomizer.customize(builder); + if (getHttp2() != null) { + builder.setServerOption(UndertowOptions.ENABLE_HTTP2, getHttp2().getEnabled()); + } } else { builder.addHttpListener(port, getListenAddress());