Auto-configure H2C when HTTP/2 is enabled and SSL is disabled
This commit is contained in:
parent
288e86d871
commit
713c0fce7c
|
|
@ -594,26 +594,23 @@ We recommend using `application.properties` to configure HTTPS, as the HTTP conn
|
||||||
[[howto-configure-http2]]
|
[[howto-configure-http2]]
|
||||||
=== Configure HTTP/2
|
=== Configure HTTP/2
|
||||||
You can enable HTTP/2 support in your Spring Boot application with the configprop:server.http2.enabled[] configuration property.
|
You can enable HTTP/2 support in your Spring Boot application with the configprop: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 all JDK8 releases.
|
Both `h2` (HTTP/2 over TLS) and `h2c` (HTTP/2 over TCP) are supported.
|
||||||
|
To use `h2`, SSL must also be enabled.
|
||||||
|
When SSL is not enabled, `h2c` will be used.
|
||||||
|
The details of the `h2` support depend on the chosen web server and the application environment, since that protocol is not supported out-of-the-box by all JDK 8 releases.
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
====
|
|
||||||
Spring Boot does not advise using `h2c`, the cleartext version of the HTTP/2 protocol.
|
|
||||||
As a result, the following sections require you to <<howto-configure-ssl, configure SSL first>>.
|
|
||||||
If you still choose to use `h2c`, you can check <<howto-configure-http2-h2c, the dedicated section>>.
|
|
||||||
====
|
|
||||||
|
|
||||||
|
|
||||||
[[howto-configure-http2-tomcat]]
|
[[howto-configure-http2-tomcat]]
|
||||||
==== HTTP/2 with Tomcat
|
==== HTTP/2 with Tomcat
|
||||||
Spring Boot ships by default with Tomcat 9.0.x which supports HTTP/2 out of the box when using JDK 9 or later.
|
Spring Boot ships by default with Tomcat 9.0.x which supports `h2c` out of the box and `h2` out of the box when using JDK 9 or later.
|
||||||
Alternatively, HTTP/2 can be used on JDK 8 if the `libtcnative` library and its dependencies are installed on the host operating system.
|
Alternatively, `h2` can be used on JDK 8 if the `libtcnative` library and its dependencies are installed on the host operating system.
|
||||||
|
|
||||||
The library directory must be made available, if not already, to the JVM library path.
|
The library directory must be made available, if not already, to the JVM library path.
|
||||||
You can do so with a JVM argument such as `-Djava.library.path=/usr/local/opt/tomcat-native/lib`.
|
You can do so with a JVM argument such as `-Djava.library.path=/usr/local/opt/tomcat-native/lib`.
|
||||||
More on this in the https://tomcat.apache.org/tomcat-9.0-doc/apr.html[official Tomcat documentation].
|
More on this in the https://tomcat.apache.org/tomcat-9.0-doc/apr.html[official Tomcat documentation].
|
||||||
|
|
||||||
Starting Tomcat 9.0.x on JDK 8 without that native support logs the following error:
|
Starting Tomcat 9.0.x on JDK 8 with HTTP/2 and SSL enabled but without that native support logs the following error:
|
||||||
|
|
||||||
[indent=0,subs="attributes"]
|
[indent=0,subs="attributes"]
|
||||||
----
|
----
|
||||||
|
|
@ -626,7 +623,8 @@ This error is not fatal, and the application still starts with HTTP/1.1 SSL supp
|
||||||
[[howto-configure-http2-jetty]]
|
[[howto-configure-http2-jetty]]
|
||||||
==== HTTP/2 with Jetty
|
==== HTTP/2 with Jetty
|
||||||
For HTTP/2 support, Jetty requires the additional `org.eclipse.jetty.http2:http2-server` dependency.
|
For HTTP/2 support, Jetty requires the additional `org.eclipse.jetty.http2:http2-server` dependency.
|
||||||
Now depending on your deployment, you also need to choose other dependencies:
|
To use `h2c` no other dependencies are required.
|
||||||
|
To use `h2`, you also need to choose one of the following dependencies, depending on your deployment:
|
||||||
|
|
||||||
* `org.eclipse.jetty:jetty-alpn-java-server` for applications running on JDK9+
|
* `org.eclipse.jetty:jetty-alpn-java-server` for applications running on JDK9+
|
||||||
* `org.eclipse.jetty:jetty-alpn-openjdk8-server` for applications running on JDK8u252+
|
* `org.eclipse.jetty:jetty-alpn-openjdk8-server` for applications running on JDK8u252+
|
||||||
|
|
@ -637,8 +635,9 @@ Now depending on your deployment, you also need to choose other dependencies:
|
||||||
[[howto-configure-http2-netty]]
|
[[howto-configure-http2-netty]]
|
||||||
==== HTTP/2 with Reactor Netty
|
==== HTTP/2 with Reactor Netty
|
||||||
The `spring-boot-webflux-starter` is using by default Reactor Netty as a server.
|
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.
|
Reactor Netty supports `h2c` using JDK 8 or later with no additional dependencies.
|
||||||
For JDK 8 environments, or for optimal runtime performance, this server also supports HTTP/2 with native libraries.
|
Reactor Netty supports `h2` using the JDK support with JDK 9 or later.
|
||||||
|
For JDK 8 environments, or for optimal runtime performance, this server also supports `h2` with native libraries.
|
||||||
To enable that, your application needs to have an additional dependency.
|
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.
|
Spring Boot manages the version for the `io.netty:netty-tcnative-boringssl-static` "uber jar", containing native libraries for all platforms.
|
||||||
|
|
@ -648,65 +647,7 @@ Developers can choose to import only the required dependencies using a classifie
|
||||||
|
|
||||||
[[howto-configure-http2-undertow]]
|
[[howto-configure-http2-undertow]]
|
||||||
==== HTTP/2 with Undertow
|
==== HTTP/2 with Undertow
|
||||||
As of Undertow 1.4.0+, HTTP/2 is supported without any additional requirement on JDK8.
|
As of Undertow 1.4.0+, both `h2` and `h2c` are supported on JDK 8 without any additional dependencies.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[howto-configure-http2-h2c]]
|
|
||||||
==== HTTP/2 Cleartext with supported servers
|
|
||||||
To enable HTTP/2 with cleartext support, you need to leave the configprop:server.http2.enabled[] property set to `false`,
|
|
||||||
and instead apply a customizer specific to your choice of server:
|
|
||||||
|
|
||||||
For Tomcat, we need to add an upgrade protocol:
|
|
||||||
|
|
||||||
[source,java,pending-extract=true,indent=0,subs="verbatim,quotes,attributes"]
|
|
||||||
----
|
|
||||||
@Bean
|
|
||||||
public TomcatConnectorCustomizer connectorCustomizer() {
|
|
||||||
return (connector) -> connector.addUpgradeProtocol(new Http2Protocol());
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
For Jetty, we need to add a connection factory to the existing connector:
|
|
||||||
|
|
||||||
[source,java,pending-extract=true,indent=0,subs="verbatim,quotes,attributes"]
|
|
||||||
----
|
|
||||||
@Bean
|
|
||||||
public JettyServerCustomizer serverCustomizer() {
|
|
||||||
return (server) -> {
|
|
||||||
HttpConfiguration configuration = new HttpConfiguration();
|
|
||||||
configuration.setSendServerVersion(false);
|
|
||||||
Arrays.stream(server.getConnectors())
|
|
||||||
.filter(connector -> connector instanceof ServerConnector)
|
|
||||||
.map(ServerConnector.class::cast)
|
|
||||||
.forEach(connector -> {
|
|
||||||
connector.addConnectionFactory(new HTTP2CServerConnectionFactory(configuration));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
For Netty, we need to add `h2c` as a supported protocol:
|
|
||||||
|
|
||||||
[source,java,pending-extract=true,indent=0,subs="verbatim,quotes,attributes"]
|
|
||||||
----
|
|
||||||
@Bean
|
|
||||||
public NettyServerCustomizer serverCustomizer() {
|
|
||||||
return (server) -> server.protocol(HttpProtocol.H2C);
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
For Undertow, we need to enable the HTTP2 option:
|
|
||||||
|
|
||||||
[source,java,pending-extract=true,indent=0,subs="verbatim,quotes,attributes"]
|
|
||||||
----
|
|
||||||
@Bean
|
|
||||||
public UndertowBuilderCustomizer builderCustomizer() {
|
|
||||||
return (builder) -> {
|
|
||||||
builder.setServerOption(ENABLE_HTTP2, true);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ dependencies {
|
||||||
optional("junit:junit")
|
optional("junit:junit")
|
||||||
optional("org.apache.commons:commons-dbcp2")
|
optional("org.apache.commons:commons-dbcp2")
|
||||||
optional("org.apache.httpcomponents:httpclient")
|
optional("org.apache.httpcomponents:httpclient")
|
||||||
|
optional("org.apache.httpcomponents.client5:httpclient5")
|
||||||
optional("org.apache.logging.log4j:log4j-api")
|
optional("org.apache.logging.log4j:log4j-api")
|
||||||
optional("org.apache.logging.log4j:log4j-core")
|
optional("org.apache.logging.log4j:log4j-core")
|
||||||
optional("org.apache.tomcat.embed:tomcat-embed-core")
|
optional("org.apache.tomcat.embed:tomcat-embed-core")
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2020 the original author or authors.
|
* Copyright 2012-2021 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -17,13 +17,16 @@
|
||||||
package org.springframework.boot.web.embedded.jetty;
|
package org.springframework.boot.web.embedded.jetty;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
|
||||||
import org.eclipse.jetty.server.AbstractConnector;
|
import org.eclipse.jetty.server.AbstractConnector;
|
||||||
import org.eclipse.jetty.server.ConnectionFactory;
|
import org.eclipse.jetty.server.ConnectionFactory;
|
||||||
import org.eclipse.jetty.server.Handler;
|
import org.eclipse.jetty.server.Handler;
|
||||||
|
|
@ -193,23 +196,26 @@ public class JettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
|
||||||
}
|
}
|
||||||
|
|
||||||
private AbstractConnector createConnector(InetSocketAddress address, Server server) {
|
private AbstractConnector createConnector(InetSocketAddress address, Server server) {
|
||||||
ServerConnector connector;
|
HttpConfiguration httpConfiguration = new HttpConfiguration();
|
||||||
|
httpConfiguration.setSendServerVersion(false);
|
||||||
|
List<ConnectionFactory> connectionFactories = new ArrayList<>();
|
||||||
|
if (getHttp2() != null && getHttp2().isEnabled()) {
|
||||||
|
connectionFactories.add(new HTTP2ServerConnectionFactory(httpConfiguration));
|
||||||
|
}
|
||||||
|
connectionFactories.add(new HttpConnectionFactory(httpConfiguration));
|
||||||
JettyResourceFactory resourceFactory = getResourceFactory();
|
JettyResourceFactory resourceFactory = getResourceFactory();
|
||||||
|
ServerConnector connector;
|
||||||
if (resourceFactory != null) {
|
if (resourceFactory != null) {
|
||||||
connector = new ServerConnector(server, resourceFactory.getExecutor(), resourceFactory.getScheduler(),
|
connector = new ServerConnector(server, resourceFactory.getExecutor(), resourceFactory.getScheduler(),
|
||||||
resourceFactory.getByteBufferPool(), this.acceptors, this.selectors, new HttpConnectionFactory());
|
resourceFactory.getByteBufferPool(), this.acceptors, this.selectors,
|
||||||
|
connectionFactories.toArray(new ConnectionFactory[0]));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
connector = new ServerConnector(server, this.acceptors, this.selectors);
|
connector = new ServerConnector(server, this.acceptors, this.selectors,
|
||||||
|
connectionFactories.toArray(new ConnectionFactory[0]));
|
||||||
}
|
}
|
||||||
connector.setHost(address.getHostString());
|
connector.setHost(address.getHostString());
|
||||||
connector.setPort(address.getPort());
|
connector.setPort(address.getPort());
|
||||||
for (ConnectionFactory connectionFactory : connector.getConnectionFactories()) {
|
|
||||||
if (connectionFactory instanceof HttpConfiguration.ConnectionFactory) {
|
|
||||||
((HttpConfiguration.ConnectionFactory) connectionFactory).getHttpConfiguration()
|
|
||||||
.setSendServerVersion(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return connector;
|
return connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,11 +34,13 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.MimeTypes;
|
import org.eclipse.jetty.http.MimeTypes;
|
||||||
|
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
|
||||||
import org.eclipse.jetty.server.AbstractConnector;
|
import org.eclipse.jetty.server.AbstractConnector;
|
||||||
import org.eclipse.jetty.server.ConnectionFactory;
|
import org.eclipse.jetty.server.ConnectionFactory;
|
||||||
import org.eclipse.jetty.server.Connector;
|
import org.eclipse.jetty.server.Connector;
|
||||||
import org.eclipse.jetty.server.Handler;
|
import org.eclipse.jetty.server.Handler;
|
||||||
import org.eclipse.jetty.server.HttpConfiguration;
|
import org.eclipse.jetty.server.HttpConfiguration;
|
||||||
|
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.ServerConnector;
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
import org.eclipse.jetty.server.handler.ErrorHandler;
|
import org.eclipse.jetty.server.handler.ErrorHandler;
|
||||||
|
|
@ -175,15 +177,17 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
|
||||||
}
|
}
|
||||||
|
|
||||||
private AbstractConnector createConnector(InetSocketAddress address, Server server) {
|
private AbstractConnector createConnector(InetSocketAddress address, Server server) {
|
||||||
ServerConnector connector = new ServerConnector(server, this.acceptors, this.selectors);
|
HttpConfiguration httpConfiguration = new HttpConfiguration();
|
||||||
|
httpConfiguration.setSendServerVersion(false);
|
||||||
|
List<ConnectionFactory> connectionFactories = new ArrayList<>();
|
||||||
|
if (getHttp2() != null && getHttp2().isEnabled()) {
|
||||||
|
connectionFactories.add(new HTTP2ServerConnectionFactory(httpConfiguration));
|
||||||
|
}
|
||||||
|
connectionFactories.add(new HttpConnectionFactory(httpConfiguration));
|
||||||
|
ServerConnector connector = new ServerConnector(server, this.acceptors, this.selectors,
|
||||||
|
connectionFactories.toArray(new ConnectionFactory[0]));
|
||||||
connector.setHost(address.getHostString());
|
connector.setHost(address.getHostString());
|
||||||
connector.setPort(address.getPort());
|
connector.setPort(address.getPort());
|
||||||
for (ConnectionFactory connectionFactory : connector.getConnectionFactories()) {
|
|
||||||
if (connectionFactory instanceof HttpConfiguration.ConnectionFactory) {
|
|
||||||
((HttpConfiguration.ConnectionFactory) connectionFactory).getHttpConfiguration()
|
|
||||||
.setSendServerVersion(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return connector;
|
return connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2020 the original author or authors.
|
* Copyright 2012-2021 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -183,10 +183,17 @@ public class NettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpProtocol[] listProtocols() {
|
private HttpProtocol[] listProtocols() {
|
||||||
if (getHttp2() != null && getHttp2().isEnabled() && getSsl() != null && getSsl().isEnabled()) {
|
List<HttpProtocol> protocols = new ArrayList<>();
|
||||||
return new HttpProtocol[] { HttpProtocol.H2, HttpProtocol.HTTP11 };
|
protocols.add(HttpProtocol.HTTP11);
|
||||||
|
if (getHttp2() != null && getHttp2().isEnabled()) {
|
||||||
|
if (getSsl() != null && getSsl().isEnabled()) {
|
||||||
|
protocols.add(HttpProtocol.H2);
|
||||||
}
|
}
|
||||||
return new HttpProtocol[] { HttpProtocol.HTTP11 };
|
else {
|
||||||
|
protocols.add(HttpProtocol.H2C);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return protocols.toArray(new HttpProtocol[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private InetSocketAddress getListenAddress() {
|
private InetSocketAddress getListenAddress() {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2020 the original author or authors.
|
* Copyright 2012-2021 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -190,6 +190,9 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac
|
||||||
}
|
}
|
||||||
// Don't bind to the socket prematurely if ApplicationContext is slow to start
|
// Don't bind to the socket prematurely if ApplicationContext is slow to start
|
||||||
connector.setProperty("bindOnInit", "false");
|
connector.setProperty("bindOnInit", "false");
|
||||||
|
if (getHttp2() != null && getHttp2().isEnabled()) {
|
||||||
|
connector.addUpgradeProtocol(new Http2Protocol());
|
||||||
|
}
|
||||||
if (getSsl() != null && getSsl().isEnabled()) {
|
if (getSsl() != null && getSsl().isEnabled()) {
|
||||||
customizeSsl(connector);
|
customizeSsl(connector);
|
||||||
}
|
}
|
||||||
|
|
@ -214,9 +217,6 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac
|
||||||
|
|
||||||
private void customizeSsl(Connector connector) {
|
private void customizeSsl(Connector connector) {
|
||||||
new SslConnectorCustomizer(getSsl(), getSslStoreProvider()).customize(connector);
|
new SslConnectorCustomizer(getSsl(), getSslStoreProvider()).customize(connector);
|
||||||
if (getHttp2() != null && getHttp2().isEnabled()) {
|
|
||||||
connector.addUpgradeProtocol(new Http2Protocol());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,9 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto
|
||||||
}
|
}
|
||||||
// Don't bind to the socket prematurely if ApplicationContext is slow to start
|
// Don't bind to the socket prematurely if ApplicationContext is slow to start
|
||||||
connector.setProperty("bindOnInit", "false");
|
connector.setProperty("bindOnInit", "false");
|
||||||
|
if (getHttp2() != null && getHttp2().isEnabled()) {
|
||||||
|
connector.addUpgradeProtocol(new Http2Protocol());
|
||||||
|
}
|
||||||
if (getSsl() != null && getSsl().isEnabled()) {
|
if (getSsl() != null && getSsl().isEnabled()) {
|
||||||
customizeSsl(connector);
|
customizeSsl(connector);
|
||||||
}
|
}
|
||||||
|
|
@ -346,9 +349,6 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto
|
||||||
|
|
||||||
private void customizeSsl(Connector connector) {
|
private void customizeSsl(Connector connector) {
|
||||||
new SslConnectorCustomizer(getSsl(), getSslStoreProvider()).customize(connector);
|
new SslConnectorCustomizer(getSsl(), getSslStoreProvider()).customize(connector);
|
||||||
if (getHttp2() != null && getHttp2().isEnabled()) {
|
|
||||||
connector.addUpgradeProtocol(new Http2Protocol());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2020 the original author or authors.
|
* Copyright 2012-2021 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -158,12 +158,12 @@ class UndertowWebServerFactoryDelegate {
|
||||||
if (this.directBuffers != null) {
|
if (this.directBuffers != null) {
|
||||||
builder.setDirectBuffers(this.directBuffers);
|
builder.setDirectBuffers(this.directBuffers);
|
||||||
}
|
}
|
||||||
if (ssl != null && ssl.isEnabled()) {
|
|
||||||
new SslBuilderCustomizer(factory.getPort(), address, ssl, factory.getSslStoreProvider()).customize(builder);
|
|
||||||
Http2 http2 = factory.getHttp2();
|
Http2 http2 = factory.getHttp2();
|
||||||
if (http2 != null) {
|
if (http2 != null) {
|
||||||
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, http2.isEnabled());
|
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, http2.isEnabled());
|
||||||
}
|
}
|
||||||
|
if (ssl != null && ssl.isEnabled()) {
|
||||||
|
new SslBuilderCustomizer(factory.getPort(), address, ssl, factory.getSslStoreProvider()).customize(builder);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
builder.addHttpListener(port, (address != null) ? address.getHostAddress() : "0.0.0.0");
|
builder.addHttpListener(port, (address != null) ? address.getHostAddress() : "0.0.0.0");
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.boot.web.reactive.server;
|
package org.springframework.boot.web.reactive.server;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
@ -28,6 +29,7 @@ import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.BrokenBarrierException;
|
import java.util.concurrent.BrokenBarrierException;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
|
@ -39,6 +41,13 @@ import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.handler.ssl.SslProvider;
|
import io.netty.handler.ssl.SslProvider;
|
||||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||||
|
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
|
||||||
|
import org.apache.hc.client5.http.async.methods.SimpleHttpRequests;
|
||||||
|
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
|
||||||
|
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
|
||||||
|
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
|
||||||
|
import org.apache.hc.core5.concurrent.FutureCallback;
|
||||||
|
import org.apache.hc.core5.http.ContentType;
|
||||||
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
|
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
|
||||||
import org.awaitility.Awaitility;
|
import org.awaitility.Awaitility;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
|
@ -52,6 +61,7 @@ import reactor.test.StepVerifier;
|
||||||
|
|
||||||
import org.springframework.boot.web.server.Compression;
|
import org.springframework.boot.web.server.Compression;
|
||||||
import org.springframework.boot.web.server.GracefulShutdownResult;
|
import org.springframework.boot.web.server.GracefulShutdownResult;
|
||||||
|
import org.springframework.boot.web.server.Http2;
|
||||||
import org.springframework.boot.web.server.Shutdown;
|
import org.springframework.boot.web.server.Shutdown;
|
||||||
import org.springframework.boot.web.server.Ssl;
|
import org.springframework.boot.web.server.Ssl;
|
||||||
import org.springframework.boot.web.server.WebServer;
|
import org.springframework.boot.web.server.WebServer;
|
||||||
|
|
@ -448,6 +458,39 @@ public abstract class AbstractReactiveWebServerFactoryTests {
|
||||||
blockingHandler.completeOne();
|
blockingHandler.completeOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenHttp2IsEnabledAndSslIsDisabledThenH2cCanBeUsed()
|
||||||
|
throws InterruptedException, ExecutionException, IOException {
|
||||||
|
AbstractReactiveWebServerFactory factory = getFactory();
|
||||||
|
Http2 http2 = new Http2();
|
||||||
|
http2.setEnabled(true);
|
||||||
|
factory.setHttp2(http2);
|
||||||
|
this.webServer = factory.getWebServer(new EchoHandler());
|
||||||
|
this.webServer.start();
|
||||||
|
try (CloseableHttpAsyncClient http2Client = HttpAsyncClients.createHttp2Default()) {
|
||||||
|
http2Client.start();
|
||||||
|
SimpleHttpRequest request = SimpleHttpRequests.post("http://localhost:" + this.webServer.getPort());
|
||||||
|
request.setBody("Hello World", ContentType.TEXT_PLAIN);
|
||||||
|
SimpleHttpResponse response = http2Client.execute(request, new FutureCallback<SimpleHttpResponse>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failed(Exception ex) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void completed(SimpleHttpResponse result) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelled() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}).get();
|
||||||
|
assertThat(response.getCode() == HttpStatus.OK.value());
|
||||||
|
assertThat(response.getBodyText()).isEqualTo("Hello World");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected WebClient prepareCompressionTest() {
|
protected WebClient prepareCompressionTest() {
|
||||||
Compression compression = new Compression();
|
Compression compression = new Compression();
|
||||||
compression.setEnabled(true);
|
compression.setEnabled(true);
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.BrokenBarrierException;
|
import java.util.concurrent.BrokenBarrierException;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.CyclicBarrier;
|
import java.util.concurrent.CyclicBarrier;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.FutureTask;
|
import java.util.concurrent.FutureTask;
|
||||||
import java.util.concurrent.RunnableFuture;
|
import java.util.concurrent.RunnableFuture;
|
||||||
|
|
@ -78,6 +79,12 @@ import javax.servlet.http.HttpServletResponse;
|
||||||
import javax.servlet.http.HttpSession;
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory;
|
import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory;
|
||||||
|
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
|
||||||
|
import org.apache.hc.client5.http.async.methods.SimpleHttpRequests;
|
||||||
|
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
|
||||||
|
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
|
||||||
|
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
|
||||||
|
import org.apache.hc.core5.concurrent.FutureCallback;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.client.HttpClient;
|
import org.apache.http.client.HttpClient;
|
||||||
import org.apache.http.client.entity.InputStreamFactory;
|
import org.apache.http.client.entity.InputStreamFactory;
|
||||||
|
|
@ -112,6 +119,7 @@ import org.springframework.boot.testsupport.web.servlet.ExampleServlet;
|
||||||
import org.springframework.boot.web.server.Compression;
|
import org.springframework.boot.web.server.Compression;
|
||||||
import org.springframework.boot.web.server.ErrorPage;
|
import org.springframework.boot.web.server.ErrorPage;
|
||||||
import org.springframework.boot.web.server.GracefulShutdownResult;
|
import org.springframework.boot.web.server.GracefulShutdownResult;
|
||||||
|
import org.springframework.boot.web.server.Http2;
|
||||||
import org.springframework.boot.web.server.MimeMappings;
|
import org.springframework.boot.web.server.MimeMappings;
|
||||||
import org.springframework.boot.web.server.PortInUseException;
|
import org.springframework.boot.web.server.PortInUseException;
|
||||||
import org.springframework.boot.web.server.Shutdown;
|
import org.springframework.boot.web.server.Shutdown;
|
||||||
|
|
@ -1121,6 +1129,39 @@ public abstract class AbstractServletWebServerFactoryTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenHttp2IsEnabledAndSslIsDisabledThenH2cCanBeUsed()
|
||||||
|
throws InterruptedException, ExecutionException, IOException {
|
||||||
|
AbstractServletWebServerFactory factory = getFactory();
|
||||||
|
Http2 http2 = new Http2();
|
||||||
|
http2.setEnabled(true);
|
||||||
|
factory.setHttp2(http2);
|
||||||
|
this.webServer = factory.getWebServer(exampleServletRegistration());
|
||||||
|
this.webServer.start();
|
||||||
|
try (CloseableHttpAsyncClient http2Client = HttpAsyncClients.createHttp2Default()) {
|
||||||
|
http2Client.start();
|
||||||
|
SimpleHttpRequest request = SimpleHttpRequests
|
||||||
|
.get("http://localhost:" + this.webServer.getPort() + "/hello");
|
||||||
|
SimpleHttpResponse response = http2Client.execute(request, new FutureCallback<SimpleHttpResponse>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failed(Exception ex) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void completed(SimpleHttpResponse result) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelled() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}).get();
|
||||||
|
assertThat(response.getCode()).isEqualTo(HttpStatus.OK.value());
|
||||||
|
assertThat(response.getBodyText()).isEqualTo("Hello World");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected Future<Object> initiateGetRequest(int port, String path) {
|
protected Future<Object> initiateGetRequest(int port, String path) {
|
||||||
return initiateGetRequest(HttpClients.createMinimal(), port, path);
|
return initiateGetRequest(HttpClients.createMinimal(), port, path);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue