Support http/2 configuration with Reactor-Netty

Just like Jetty, Reactor Netty supports ALPN with JDK8 or with a
dependency that delegates TLS to a native library using boringSSL.

Closes gh-13333
pull/13870/merge
Brian Clozel 6 years ago
parent c98bb40136
commit dd79143d1a

@ -133,6 +133,7 @@
<nekohtml.version>1.9.22</nekohtml.version> <nekohtml.version>1.9.22</nekohtml.version>
<neo4j-ogm.version>3.1.0</neo4j-ogm.version> <neo4j-ogm.version>3.1.0</neo4j-ogm.version>
<netty.version>4.1.27.Final</netty.version> <netty.version>4.1.27.Final</netty.version>
<netty-tcnative.version>2.0.12.Final</netty-tcnative.version>
<nio-multipart-parser.version>1.1.0</nio-multipart-parser.version> <nio-multipart-parser.version>1.1.0</nio-multipart-parser.version>
<postgresql.version>42.2.4</postgresql.version> <postgresql.version>42.2.4</postgresql.version>
<quartz.version>2.3.0</quartz.version> <quartz.version>2.3.0</quartz.version>
@ -924,6 +925,11 @@
<scope>import</scope> <scope>import</scope>
<type>pom</type> <type>pom</type>
</dependency> </dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>${netty-tcnative.version}</version>
</dependency>
<dependency> <dependency>
<groupId>io.projectreactor</groupId> <groupId>io.projectreactor</groupId>
<artifactId>reactor-bom</artifactId> <artifactId>reactor-bom</artifactId>

@ -753,6 +753,22 @@ This error is not fatal, and the application still starts with HTTP/1.1 SSL supp
[[howto-configure-http2-netty]]
==== HTTP/2 with Reactor Netty
The `spring-boot-webflux-starter` is using by default Reactor Netty as a server.
Reactor Netty can be configured for HTTP/2 using the JDK support with JDK 9 or later.
For JDK 8 environments, or for optimal runtime performance, this server also supports
HTTP/2 with native libraries. To enable that, your application needs to have an
additional dependency.
Spring Boot manages the version for the
`io.netty:netty-tcnative-boringssl-static` "uber jar", containing native libraries for
all platforms. Developers can choose to import only the required dependendencies using
a classifier (see http://netty.io/wiki/forked-tomcat-native.html[the Netty official
documentation]).
[[howto-configure-webserver]] [[howto-configure-webserver]]
=== Configure the Web Server === Configure the Web Server

@ -76,6 +76,11 @@
<artifactId>reactor-netty</artifactId> <artifactId>reactor-netty</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>io.undertow</groupId> <groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId> <artifactId>undertow-servlet</artifactId>
@ -373,6 +378,21 @@
<artifactId>jaybird-jdk18</artifactId> <artifactId>jaybird-jdk18</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-http-client-transport</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.hsqldb</groupId> <groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId> <artifactId>hsqldb</artifactId>

@ -23,6 +23,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import reactor.netty.http.HttpProtocol;
import reactor.netty.http.server.HttpServer; import reactor.netty.http.server.HttpServer;
import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory; import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory;
@ -110,10 +111,10 @@ public class NettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
private HttpServer createHttpServer() { private HttpServer createHttpServer() {
HttpServer server = HttpServer.create().tcpConfiguration( HttpServer server = HttpServer.create().tcpConfiguration(
(tcpServer) -> tcpServer.addressSupplier(() -> getListenAddress())); (tcpServer) -> tcpServer.addressSupplier(this::getListenAddress));
if (getSsl() != null && getSsl().isEnabled()) { if (getSsl() != null && getSsl().isEnabled()) {
SslServerCustomizer sslServerCustomizer = new SslServerCustomizer(getSsl(), SslServerCustomizer sslServerCustomizer = new SslServerCustomizer(getSsl(),
getSslStoreProvider()); getHttp2(), getSslStoreProvider());
server = sslServerCustomizer.apply(server); server = sslServerCustomizer.apply(server);
} }
if (getCompression() != null && getCompression().getEnabled()) { if (getCompression() != null && getCompression().getEnabled()) {
@ -121,10 +122,23 @@ public class NettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
getCompression()); getCompression());
server = compressionCustomizer.apply(server); server = compressionCustomizer.apply(server);
} }
server = server.protocol(listProtocols());
server = (this.useForwardHeaders ? server.forwarded() : server.noForwarded()); server = (this.useForwardHeaders ? server.forwarded() : server.noForwarded());
return applyCustomizers(server); return applyCustomizers(server);
} }
private HttpProtocol[] listProtocols() {
if (getHttp2() != null && getHttp2().isEnabled()) {
if (getSsl() != null && getSsl().isEnabled()) {
return new HttpProtocol[] { HttpProtocol.H2, HttpProtocol.HTTP11 };
}
else {
return new HttpProtocol[] { HttpProtocol.H2C, HttpProtocol.HTTP11 };
}
}
return new HttpProtocol[] { HttpProtocol.HTTP11 };
}
private InetSocketAddress getListenAddress() { private InetSocketAddress getListenAddress() {
if (getAddress() != null) { if (getAddress() != null) {
return new InetSocketAddress(getAddress().getHostAddress(), getPort()); return new InetSocketAddress(getAddress().getHostAddress(), getPort());

@ -26,8 +26,9 @@ import javax.net.ssl.TrustManagerFactory;
import io.netty.handler.ssl.ClientAuth; import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslContextBuilder;
import reactor.netty.http.server.HttpServer; import reactor.netty.http.server.HttpServer;
import reactor.netty.tcp.SslProvider.DefaultConfigurationType; import reactor.netty.tcp.SslProvider;
import org.springframework.boot.web.server.Http2;
import org.springframework.boot.web.server.Ssl; import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.server.SslStoreProvider; import org.springframework.boot.web.server.SslStoreProvider;
import org.springframework.util.ResourceUtils; import org.springframework.util.ResourceUtils;
@ -42,19 +43,26 @@ public class SslServerCustomizer implements NettyServerCustomizer {
private final Ssl ssl; private final Ssl ssl;
private final Http2 http2;
private final SslStoreProvider sslStoreProvider; private final SslStoreProvider sslStoreProvider;
public SslServerCustomizer(Ssl ssl, SslStoreProvider sslStoreProvider) { public SslServerCustomizer(Ssl ssl, Http2 http2, SslStoreProvider sslStoreProvider) {
this.ssl = ssl; this.ssl = ssl;
this.http2 = http2;
this.sslStoreProvider = sslStoreProvider; this.sslStoreProvider = sslStoreProvider;
} }
@Override @Override
public HttpServer apply(HttpServer server) { public HttpServer apply(HttpServer server) {
try { try {
return server return server.secure((contextSpec) -> {
.secure((contextSpec) -> contextSpec.sslContext(getContextBuilder()) SslProvider.DefaultConfigurationSpec spec = contextSpec
.defaultConfiguration(DefaultConfigurationType.NONE)); .sslContext(getContextBuilder());
if (this.http2 != null && this.http2.isEnabled()) {
spec.defaultConfiguration(SslProvider.DefaultConfigurationType.H2);
}
});
} }
catch (Exception ex) { catch (Exception ex) {
throw new IllegalStateException(ex); throw new IllegalStateException(ex);

Loading…
Cancel
Save