Commit bb9396e3 authored by Brian Clozel's avatar Brian Clozel

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
parent 58db841c
...@@ -734,6 +734,54 @@ sample project for an example. ...@@ -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]] [[howto-configure-accesslogs]]
=== Configure Access Logging === Configure Access Logging
Access logs can be configured for Tomcat, Undertow, and Jetty through their respective Access logs can be configured for Tomcat, Undertow, and Jetty through their respective
......
...@@ -45,22 +45,21 @@ class SslConnectorCustomizer implements TomcatConnectorCustomizer { ...@@ -45,22 +45,21 @@ class SslConnectorCustomizer implements TomcatConnectorCustomizer {
private final SslStoreProvider sslStoreProvider; private final SslStoreProvider sslStoreProvider;
SslConnectorCustomizer(Ssl ssl, SslStoreProvider sslStoreProvider) { SslConnectorCustomizer(Ssl ssl, SslStoreProvider sslStoreProvider) {
Assert.notNull(ssl, "Ssl configuration should not be null");
this.ssl = ssl; this.ssl = ssl;
this.sslStoreProvider = sslStoreProvider; this.sslStoreProvider = sslStoreProvider;
} }
@Override @Override
public void customize(Connector connector) { public void customize(Connector connector) {
if (this.ssl != null && this.ssl.isEnabled()) { ProtocolHandler handler = connector.getProtocolHandler();
ProtocolHandler handler = connector.getProtocolHandler(); Assert.state(handler instanceof AbstractHttp11JsseProtocol,
Assert.state(handler instanceof AbstractHttp11JsseProtocol, "To use SSL, the connector's protocol handler must be an "
"To use SSL, the connector's protocol handler must be an " + "AbstractHttp11JsseProtocol subclass");
+ "AbstractHttp11JsseProtocol subclass"); configureSsl((AbstractHttp11JsseProtocol<?>) handler,
configureSsl((AbstractHttp11JsseProtocol<?>) handler, this.ssl, this.sslStoreProvider);
this.ssl, this.sslStoreProvider); connector.setScheme("https");
connector.setScheme("https"); connector.setSecure(true);
connector.setSecure(true);
}
} }
/** /**
......
...@@ -30,6 +30,7 @@ import org.apache.catalina.core.AprLifecycleListener; ...@@ -30,6 +30,7 @@ import org.apache.catalina.core.AprLifecycleListener;
import org.apache.catalina.loader.WebappLoader; import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.Tomcat;
import org.apache.coyote.AbstractProtocol; 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.AbstractReactiveWebServerFactory;
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory; import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
...@@ -141,8 +142,13 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac ...@@ -141,8 +142,13 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac
// If ApplicationContext is slow to start we want Tomcat not to bind to the socket // If ApplicationContext is slow to start we want Tomcat not to bind to the socket
// prematurely... // prematurely...
connector.setProperty("bindOnInit", "false"); connector.setProperty("bindOnInit", "false");
TomcatConnectorCustomizer ssl = new SslConnectorCustomizer(getSsl(), getSslStoreProvider()); if (getSsl() != null && getSsl().isEnabled()) {
ssl.customize(connector); TomcatConnectorCustomizer ssl = new SslConnectorCustomizer(getSsl(), getSslStoreProvider());
ssl.customize(connector);
if (getHttp2() != null && getHttp2().getEnabled()) {
connector.addUpgradeProtocol(new Http2Protocol());
}
}
TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(getCompression()); TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(getCompression());
compression.customize(connector); compression.customize(connector);
for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) { for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {
......
...@@ -59,6 +59,7 @@ import org.apache.catalina.webresources.AbstractResourceSet; ...@@ -59,6 +59,7 @@ import org.apache.catalina.webresources.AbstractResourceSet;
import org.apache.catalina.webresources.EmptyResource; import org.apache.catalina.webresources.EmptyResource;
import org.apache.catalina.webresources.StandardRoot; import org.apache.catalina.webresources.StandardRoot;
import org.apache.coyote.AbstractProtocol; import org.apache.coyote.AbstractProtocol;
import org.apache.coyote.http2.Http2Protocol;
import org.springframework.boot.web.server.ErrorPage; import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.MimeMappings; import org.springframework.boot.web.server.MimeMappings;
...@@ -297,8 +298,13 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto ...@@ -297,8 +298,13 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto
// prematurely... // prematurely...
connector.setProperty("bindOnInit", "false"); connector.setProperty("bindOnInit", "false");
TomcatConnectorCustomizer ssl = new SslConnectorCustomizer(getSsl(), getSslStoreProvider()); if (getSsl() != null && getSsl().isEnabled()) {
ssl.customize(connector); TomcatConnectorCustomizer ssl = new SslConnectorCustomizer(getSsl(), getSslStoreProvider());
ssl.customize(connector);
if (getHttp2() != null && getHttp2().getEnabled()) {
connector.addUpgradeProtocol(new Http2Protocol());
}
}
TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(getCompression()); TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(getCompression());
compression.customize(connector); compression.customize(connector);
for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) { for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {
......
...@@ -22,6 +22,7 @@ import java.util.Collection; ...@@ -22,6 +22,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import io.undertow.Undertow; import io.undertow.Undertow;
import io.undertow.UndertowOptions;
import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory; import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory;
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory; import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
...@@ -89,6 +90,9 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF ...@@ -89,6 +90,9 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF
SslBuilderCustomizer sslBuilderCustomizer = SslBuilderCustomizer sslBuilderCustomizer =
new SslBuilderCustomizer(getPort(), getAddress(), getSsl(), getSslStoreProvider()); new SslBuilderCustomizer(getPort(), getAddress(), getSsl(), getSslStoreProvider());
sslBuilderCustomizer.customize(builder); sslBuilderCustomizer.customize(builder);
if (getHttp2() != null) {
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, getHttp2().getEnabled());
}
} }
else { else {
builder.addHttpListener(port, getListenAddress()); builder.addHttpListener(port, getListenAddress());
......
...@@ -36,6 +36,7 @@ import javax.servlet.ServletException; ...@@ -36,6 +36,7 @@ import javax.servlet.ServletException;
import io.undertow.Undertow; import io.undertow.Undertow;
import io.undertow.Undertow.Builder; import io.undertow.Undertow.Builder;
import io.undertow.UndertowOptions;
import io.undertow.server.HttpHandler; import io.undertow.server.HttpHandler;
import io.undertow.server.handlers.accesslog.AccessLogHandler; import io.undertow.server.handlers.accesslog.AccessLogHandler;
import io.undertow.server.handlers.accesslog.AccessLogReceiver; import io.undertow.server.handlers.accesslog.AccessLogReceiver;
...@@ -235,6 +236,9 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac ...@@ -235,6 +236,9 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
SslBuilderCustomizer sslBuilderCustomizer = SslBuilderCustomizer sslBuilderCustomizer =
new SslBuilderCustomizer(getPort(), getAddress(), getSsl(), getSslStoreProvider()); new SslBuilderCustomizer(getPort(), getAddress(), getSsl(), getSslStoreProvider());
sslBuilderCustomizer.customize(builder); sslBuilderCustomizer.customize(builder);
if (getHttp2() != null) {
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, getHttp2().getEnabled());
}
} }
else { else {
builder.addHttpListener(port, getListenAddress()); builder.addHttpListener(port, getListenAddress());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment