Skip to content

Commit dd79143

Browse files
committed
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
1 parent c98bb40 commit dd79143

File tree

5 files changed

+71
-7
lines changed

5 files changed

+71
-7
lines changed

Diff for: spring-boot-project/spring-boot-dependencies/pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
<nekohtml.version>1.9.22</nekohtml.version>
134134
<neo4j-ogm.version>3.1.0</neo4j-ogm.version>
135135
<netty.version>4.1.27.Final</netty.version>
136+
<netty-tcnative.version>2.0.12.Final</netty-tcnative.version>
136137
<nio-multipart-parser.version>1.1.0</nio-multipart-parser.version>
137138
<postgresql.version>42.2.4</postgresql.version>
138139
<quartz.version>2.3.0</quartz.version>
@@ -924,6 +925,11 @@
924925
<scope>import</scope>
925926
<type>pom</type>
926927
</dependency>
928+
<dependency>
929+
<groupId>io.netty</groupId>
930+
<artifactId>netty-tcnative-boringssl-static</artifactId>
931+
<version>${netty-tcnative.version}</version>
932+
</dependency>
927933
<dependency>
928934
<groupId>io.projectreactor</groupId>
929935
<artifactId>reactor-bom</artifactId>

Diff for: spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc

+16
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,22 @@ This error is not fatal, and the application still starts with HTTP/1.1 SSL supp
753753

754754

755755

756+
[[howto-configure-http2-netty]]
757+
==== HTTP/2 with Reactor Netty
758+
The `spring-boot-webflux-starter` is using by default Reactor Netty as a server.
759+
Reactor Netty can be configured for HTTP/2 using the JDK support with JDK 9 or later.
760+
For JDK 8 environments, or for optimal runtime performance, this server also supports
761+
HTTP/2 with native libraries. To enable that, your application needs to have an
762+
additional dependency.
763+
764+
Spring Boot manages the version for the
765+
`io.netty:netty-tcnative-boringssl-static` "uber jar", containing native libraries for
766+
all platforms. Developers can choose to import only the required dependendencies using
767+
a classifier (see http://netty.io/wiki/forked-tomcat-native.html[the Netty official
768+
documentation]).
769+
770+
771+
756772
[[howto-configure-webserver]]
757773
=== Configure the Web Server
758774

Diff for: spring-boot-project/spring-boot/pom.xml

+20
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@
7676
<artifactId>reactor-netty</artifactId>
7777
<optional>true</optional>
7878
</dependency>
79+
<dependency>
80+
<groupId>io.netty</groupId>
81+
<artifactId>netty-tcnative-boringssl-static</artifactId>
82+
<optional>true</optional>
83+
</dependency>
7984
<dependency>
8085
<groupId>io.undertow</groupId>
8186
<artifactId>undertow-servlet</artifactId>
@@ -373,6 +378,21 @@
373378
<artifactId>jaybird-jdk18</artifactId>
374379
<scope>test</scope>
375380
</dependency>
381+
<dependency>
382+
<groupId>org.eclipse.jetty</groupId>
383+
<artifactId>jetty-client</artifactId>
384+
<scope>test</scope>
385+
</dependency>
386+
<dependency>
387+
<groupId>org.eclipse.jetty.http2</groupId>
388+
<artifactId>http2-client</artifactId>
389+
<scope>test</scope>
390+
</dependency>
391+
<dependency>
392+
<groupId>org.eclipse.jetty.http2</groupId>
393+
<artifactId>http2-http-client-transport</artifactId>
394+
<scope>test</scope>
395+
</dependency>
376396
<dependency>
377397
<groupId>org.hsqldb</groupId>
378398
<artifactId>hsqldb</artifactId>

Diff for: spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactory.java

+16-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Collection;
2424
import java.util.List;
2525

26+
import reactor.netty.http.HttpProtocol;
2627
import reactor.netty.http.server.HttpServer;
2728

2829
import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory;
@@ -110,21 +111,34 @@ public void setUseForwardHeaders(boolean useForwardHeaders) {
110111

111112
private HttpServer createHttpServer() {
112113
HttpServer server = HttpServer.create().tcpConfiguration(
113-
(tcpServer) -> tcpServer.addressSupplier(() -> getListenAddress()));
114+
(tcpServer) -> tcpServer.addressSupplier(this::getListenAddress));
114115
if (getSsl() != null && getSsl().isEnabled()) {
115116
SslServerCustomizer sslServerCustomizer = new SslServerCustomizer(getSsl(),
116-
getSslStoreProvider());
117+
getHttp2(), getSslStoreProvider());
117118
server = sslServerCustomizer.apply(server);
118119
}
119120
if (getCompression() != null && getCompression().getEnabled()) {
120121
CompressionCustomizer compressionCustomizer = new CompressionCustomizer(
121122
getCompression());
122123
server = compressionCustomizer.apply(server);
123124
}
125+
server = server.protocol(listProtocols());
124126
server = (this.useForwardHeaders ? server.forwarded() : server.noForwarded());
125127
return applyCustomizers(server);
126128
}
127129

130+
private HttpProtocol[] listProtocols() {
131+
if (getHttp2() != null && getHttp2().isEnabled()) {
132+
if (getSsl() != null && getSsl().isEnabled()) {
133+
return new HttpProtocol[] { HttpProtocol.H2, HttpProtocol.HTTP11 };
134+
}
135+
else {
136+
return new HttpProtocol[] { HttpProtocol.H2C, HttpProtocol.HTTP11 };
137+
}
138+
}
139+
return new HttpProtocol[] { HttpProtocol.HTTP11 };
140+
}
141+
128142
private InetSocketAddress getListenAddress() {
129143
if (getAddress() != null) {
130144
return new InetSocketAddress(getAddress().getHostAddress(), getPort());

Diff for: spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/SslServerCustomizer.java

+13-5
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@
2626
import io.netty.handler.ssl.ClientAuth;
2727
import io.netty.handler.ssl.SslContextBuilder;
2828
import reactor.netty.http.server.HttpServer;
29-
import reactor.netty.tcp.SslProvider.DefaultConfigurationType;
29+
import reactor.netty.tcp.SslProvider;
3030

31+
import org.springframework.boot.web.server.Http2;
3132
import org.springframework.boot.web.server.Ssl;
3233
import org.springframework.boot.web.server.SslStoreProvider;
3334
import org.springframework.util.ResourceUtils;
@@ -42,19 +43,26 @@ public class SslServerCustomizer implements NettyServerCustomizer {
4243

4344
private final Ssl ssl;
4445

46+
private final Http2 http2;
47+
4548
private final SslStoreProvider sslStoreProvider;
4649

47-
public SslServerCustomizer(Ssl ssl, SslStoreProvider sslStoreProvider) {
50+
public SslServerCustomizer(Ssl ssl, Http2 http2, SslStoreProvider sslStoreProvider) {
4851
this.ssl = ssl;
52+
this.http2 = http2;
4953
this.sslStoreProvider = sslStoreProvider;
5054
}
5155

5256
@Override
5357
public HttpServer apply(HttpServer server) {
5458
try {
55-
return server
56-
.secure((contextSpec) -> contextSpec.sslContext(getContextBuilder())
57-
.defaultConfiguration(DefaultConfigurationType.NONE));
59+
return server.secure((contextSpec) -> {
60+
SslProvider.DefaultConfigurationSpec spec = contextSpec
61+
.sslContext(getContextBuilder());
62+
if (this.http2 != null && this.http2.isEnabled()) {
63+
spec.defaultConfiguration(SslProvider.DefaultConfigurationType.H2);
64+
}
65+
});
5866
}
5967
catch (Exception ex) {
6068
throw new IllegalStateException(ex);

0 commit comments

Comments
 (0)