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
This commit is contained in:
Brian Clozel 2017-11-03 11:34:30 +01:00
parent 58db841c8f
commit bb9396e3a4
6 changed files with 81 additions and 14 deletions

View File

@ -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 <<howto-configure-ssl, configure SSL first>>.
====
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

View File

@ -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);
}
/**

View File

@ -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) {

View File

@ -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) {

View File

@ -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());

View File

@ -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());