Commit 427d2622 authored by Phillip Webb's avatar Phillip Webb

Polish

parent a0580da8
...@@ -193,10 +193,9 @@ public class DefaultErrorWebExceptionHandler extends AbstractErrorWebExceptionHa ...@@ -193,10 +193,9 @@ public class DefaultErrorWebExceptionHandler extends AbstractErrorWebExceptionHa
*/ */
protected void logError(ServerRequest request, HttpStatus errorStatus) { protected void logError(ServerRequest request, HttpStatus errorStatus) {
if (errorStatus.is5xxServerError()) { if (errorStatus.is5xxServerError()) {
Throwable error = getError(request); Throwable ex = getError(request);
final String message = "Failed to handle request [" logger.error("Failed to handle request [" + request.methodName() + " "
+ request.methodName() + " " + request.uri() + "]"; + request.uri() + "]", ex);
logger.error(message, error);
} }
} }
......
...@@ -165,7 +165,7 @@ content into your application; rather pick only the properties that you need. ...@@ -165,7 +165,7 @@ content into your application; rather pick only the properties that you need.
server.error.include-stacktrace=never # When to include a "stacktrace" attribute. server.error.include-stacktrace=never # When to include a "stacktrace" attribute.
server.error.path=/error # Path of the error controller. server.error.path=/error # Path of the error controller.
server.error.whitelabel.enabled=true # Enable the default error page displayed in browsers in case of a server error. server.error.whitelabel.enabled=true # Enable the default error page displayed in browsers in case of a server error.
server.http2.enabled=true # Enable HTTP/2 support if the current environment supports it. server.http2.enabled=true # Whether to enable HTTP/2 support, if the current environment supports it.
server.jetty.acceptors= # Number of acceptor threads to use. server.jetty.acceptors= # Number of acceptor threads to use.
server.jetty.accesslog.append=false # Append to log. server.jetty.accesslog.append=false # Append to log.
server.jetty.accesslog.date-format=dd/MMM/yyyy:HH:mm:ss Z # Timestamp format of the request log. server.jetty.accesslog.date-format=dd/MMM/yyyy:HH:mm:ss Z # Timestamp format of the request log.
......
...@@ -737,50 +737,51 @@ sample project for an example. ...@@ -737,50 +737,51 @@ sample project for an example.
[[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 You can enable HTTP/2 support in your Spring Boot application with the
`+server.http2.enabled+` configuration property. This support depends on the `+server.http2.enabled+` configuration property. This support depends on the chosen web
chosen web server and the application environment, since that protocol is not server and the application environment, since that protocol is not supported
supported out-of-the-box by JDK8. out-of-the-box by JDK8.
[NOTE] [NOTE]
==== ====
Spring Boot does not support `h2c`, the cleartext version of the HTTP/2 Spring Boot does not support `h2c`, the cleartext version of the HTTP/2 protocol. So you
protocol. So you must configure <<howto-configure-ssl, configure SSL first>>. must configure <<howto-configure-ssl, configure SSL first>>.
==== ====
Currently, only Undertow and Tomcat are supported with this configuration key. Currently, only Undertow and Tomcat are supported with this configuration key.
[[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 As of Undertow 1.4.0+, HTTP/2 is supported without any additional requirement on JDK8.
on JDK8.
[[howto-configure-http2-tomcat]] [[howto-configure-http2-tomcat]]
==== HTTP/2 with Tomcat ==== HTTP/2 with Tomcat
Spring Boot ships by default with Tomcat 8.5.x; with that version, Spring Boot ships by default with Tomcat 8.5.x; with that version, HTTP/2 is only
HTTP/2 is only supported if the `libtcnative` library and its dependencies supported if the `libtcnative` library and its dependencies are installed on the host
are installed on the host operating system. operating system.
The library folder must be made available, if not already, to the JVM library The library folder must be made available, if not already, to the JVM library path; this
path; this can be done with a JVM argument such as can be done with a JVM argument such as
`-Djava.library.path=/usr/local/opt/tomcat-native/lib`. More on this in the `-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 http://tomcat.apache.org/tomcat-8.5-doc/apr.html[official Tomcat documentation].
documentation].
Starting Tomcat 8.5.x without that native support will log the following error: Starting Tomcat 8.5.x without that native support will log the following error:
[indent=0,subs="attributes"] [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. 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 This error is not fatal, and the application starts with HTTP/1.1 SSL support still.
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.
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
......
...@@ -97,10 +97,9 @@ public class JettyReactiveWebServerFactory extends AbstractReactiveWebServerFact ...@@ -97,10 +97,9 @@ public class JettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
ServletContextHandler contextHandler = new ServletContextHandler(server, "", ServletContextHandler contextHandler = new ServletContextHandler(server, "",
false, false); false, false);
contextHandler.addServlet(servletHolder, "/"); contextHandler.addServlet(servletHolder, "/");
this.logger.info("Server initialized with port: " + port); JettyReactiveWebServerFactory.logger
SslServerCustomizer sslServerCustomizer = new SslServerCustomizer(port, .info("Server initialized with port: " + port);
getSsl(), getSslStoreProvider()); new SslServerCustomizer(port, getSsl(), getSslStoreProvider()).customize(server);
sslServerCustomizer.customize(server);
for (JettyServerCustomizer customizer : getServerCustomizers()) { for (JettyServerCustomizer customizer : getServerCustomizers()) {
customizer.customize(server); customizer.customize(server);
} }
......
...@@ -153,9 +153,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor ...@@ -153,9 +153,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
configureWebAppContext(context, initializers); configureWebAppContext(context, initializers);
server.setHandler(addHandlerWrappers(context)); server.setHandler(addHandlerWrappers(context));
this.logger.info("Server initialized with port: " + port); this.logger.info("Server initialized with port: " + port);
SslServerCustomizer sslServerCustomizer = new SslServerCustomizer(port, new SslServerCustomizer(port, getSsl(), getSslStoreProvider()).customize(server);
getSsl(), getSslStoreProvider());
sslServerCustomizer.customize(server);
for (JettyServerCustomizer customizer : getServerCustomizers()) { for (JettyServerCustomizer customizer : getServerCustomizers()) {
customizer.customize(server); customizer.customize(server);
} }
...@@ -167,7 +165,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor ...@@ -167,7 +165,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
private Server createServer(InetSocketAddress address) { private Server createServer(InetSocketAddress address) {
Server server = new Server(getThreadPool()); Server server = new Server(getThreadPool());
server.setConnectors(new Connector[] {createConnector(address, server)}); server.setConnectors(new Connector[] { createConnector(address, server) });
return server; return server;
} }
...@@ -338,7 +336,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor ...@@ -338,7 +336,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
context.getServletHandler().addServlet(holder); context.getServletHandler().addServlet(holder);
ServletMapping mapping = new ServletMapping(); ServletMapping mapping = new ServletMapping();
mapping.setServletName("jsp"); mapping.setServletName("jsp");
mapping.setPathSpecs(new String[] {"*.jsp", "*.jspx"}); mapping.setPathSpecs(new String[] { "*.jsp", "*.jspx" });
context.getServletHandler().addServletMapping(mapping); context.getServletHandler().addServletMapping(mapping);
} }
......
...@@ -38,8 +38,7 @@ import org.springframework.util.ObjectUtils; ...@@ -38,8 +38,7 @@ import org.springframework.util.ObjectUtils;
import org.springframework.util.ResourceUtils; import org.springframework.util.ResourceUtils;
/** /**
* {@link JettyServerCustomizer} that configures SSL on the * {@link JettyServerCustomizer} that configures SSL on the given Jetty server instance.
* given Jetty server instance.
* *
* @author Brian Clozel * @author Brian Clozel
*/ */
...@@ -88,7 +87,8 @@ class SslServerCustomizer implements JettyServerCustomizer { ...@@ -88,7 +87,8 @@ class SslServerCustomizer implements JettyServerCustomizer {
* @param ssl the ssl details. * @param ssl the ssl details.
* @param sslStoreProvider the ssl store provider * @param sslStoreProvider the ssl store provider
*/ */
protected void configureSsl(SslContextFactory factory, Ssl ssl, SslStoreProvider sslStoreProvider) { protected void configureSsl(SslContextFactory factory, Ssl ssl,
SslStoreProvider sslStoreProvider) {
factory.setProtocol(ssl.getProtocol()); factory.setProtocol(ssl.getProtocol());
configureSslClientAuth(factory, ssl); configureSslClientAuth(factory, ssl);
configureSslPasswords(factory, ssl); configureSslPasswords(factory, ssl);
...@@ -172,4 +172,5 @@ class SslServerCustomizer implements JettyServerCustomizer { ...@@ -172,4 +172,5 @@ class SslServerCustomizer implements JettyServerCustomizer {
factory.setTrustStoreProvider(ssl.getTrustStoreProvider()); factory.setTrustStoreProvider(ssl.getTrustStoreProvider());
} }
} }
} }
...@@ -24,14 +24,13 @@ import org.springframework.boot.web.server.Compression; ...@@ -24,14 +24,13 @@ import org.springframework.boot.web.server.Compression;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
* {@link TomcatConnectorCustomizer} that configures compression * {@link TomcatConnectorCustomizer} that configures compression support on the given
* support on the given Connector. * Connector.
* *
* @author Brian Clozel * @author Brian Clozel
*/ */
class CompressionConnectorCustomizer implements TomcatConnectorCustomizer { class CompressionConnectorCustomizer implements TomcatConnectorCustomizer {
private final Compression compression; private final Compression compression;
CompressionConnectorCustomizer(Compression compression) { CompressionConnectorCustomizer(Compression compression) {
...@@ -43,18 +42,21 @@ class CompressionConnectorCustomizer implements TomcatConnectorCustomizer { ...@@ -43,18 +42,21 @@ class CompressionConnectorCustomizer implements TomcatConnectorCustomizer {
if (this.compression != null && this.compression.getEnabled()) { if (this.compression != null && this.compression.getEnabled()) {
ProtocolHandler handler = connector.getProtocolHandler(); ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractHttp11Protocol) { if (handler instanceof AbstractHttp11Protocol) {
AbstractHttp11Protocol<?> protocol = (AbstractHttp11Protocol<?>) handler; customize((AbstractHttp11Protocol<?>) handler);
Compression compression = this.compression;
protocol.setCompression("on");
protocol.setCompressionMinSize(compression.getMinResponseSize());
protocol.setCompressibleMimeType(
StringUtils.arrayToCommaDelimitedString(compression.getMimeTypes()));
if (this.compression.getExcludedUserAgents() != null) {
protocol.setNoCompressionUserAgents(
StringUtils.arrayToCommaDelimitedString(
this.compression.getExcludedUserAgents()));
}
} }
} }
} }
private void customize(AbstractHttp11Protocol<?> protocol) {
Compression compression = this.compression;
protocol.setCompression("on");
protocol.setCompressionMinSize(compression.getMinResponseSize());
protocol.setCompressibleMimeType(
StringUtils.arrayToCommaDelimitedString(compression.getMimeTypes()));
if (this.compression.getExcludedUserAgents() != null) {
protocol.setNoCompressionUserAgents(StringUtils.arrayToCommaDelimitedString(
this.compression.getExcludedUserAgents()));
}
}
} }
...@@ -33,8 +33,7 @@ import org.springframework.util.ResourceUtils; ...@@ -33,8 +33,7 @@ import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
* {@link TomcatConnectorCustomizer} that configures SSL support * {@link TomcatConnectorCustomizer} that configures SSL support on the given connector.
* on the given connector.
* *
* @author Brian Clozel * @author Brian Clozel
*/ */
...@@ -56,8 +55,8 @@ class SslConnectorCustomizer implements TomcatConnectorCustomizer { ...@@ -56,8 +55,8 @@ class SslConnectorCustomizer implements TomcatConnectorCustomizer {
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.ssl, this.sslStoreProvider); this.sslStoreProvider);
connector.setScheme("https"); connector.setScheme("https");
connector.setSecure(true); connector.setSecure(true);
} }
...@@ -68,8 +67,8 @@ class SslConnectorCustomizer implements TomcatConnectorCustomizer { ...@@ -68,8 +67,8 @@ class SslConnectorCustomizer implements TomcatConnectorCustomizer {
* @param ssl the ssl details * @param ssl the ssl details
* @param sslStoreProvider the ssl store provider * @param sslStoreProvider the ssl store provider
*/ */
protected void configureSsl(AbstractHttp11JsseProtocol<?> protocol, protected void configureSsl(AbstractHttp11JsseProtocol<?> protocol, Ssl ssl,
Ssl ssl, SslStoreProvider sslStoreProvider) { SslStoreProvider sslStoreProvider) {
protocol.setSSLEnabled(true); protocol.setSSLEnabled(true);
protocol.setSslProtocol(ssl.getProtocol()); protocol.setSslProtocol(ssl.getProtocol());
configureSslClientAuth(protocol, ssl); configureSslClientAuth(protocol, ssl);
...@@ -112,8 +111,7 @@ class SslConnectorCustomizer implements TomcatConnectorCustomizer { ...@@ -112,8 +111,7 @@ class SslConnectorCustomizer implements TomcatConnectorCustomizer {
.getInstance(); .getInstance();
instance.addUserFactory( instance.addUserFactory(
new SslStoreProviderUrlStreamHandlerFactory(sslStoreProvider)); new SslStoreProviderUrlStreamHandlerFactory(sslStoreProvider));
protocol.setKeystoreFile( protocol.setKeystoreFile(SslStoreProviderUrlStreamHandlerFactory.KEY_STORE_URL);
SslStoreProviderUrlStreamHandlerFactory.KEY_STORE_URL);
protocol.setTruststoreFile( protocol.setTruststoreFile(
SslStoreProviderUrlStreamHandlerFactory.TRUST_STORE_URL); SslStoreProviderUrlStreamHandlerFactory.TRUST_STORE_URL);
} }
...@@ -153,4 +151,5 @@ class SslConnectorCustomizer implements TomcatConnectorCustomizer { ...@@ -153,4 +151,5 @@ class SslConnectorCustomizer implements TomcatConnectorCustomizer {
protocol.setTruststoreProvider(ssl.getTrustStoreProvider()); protocol.setTruststoreProvider(ssl.getTrustStoreProvider());
} }
} }
} }
...@@ -20,6 +20,7 @@ import java.io.File; ...@@ -20,6 +20,7 @@ import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.apache.catalina.Context; import org.apache.catalina.Context;
...@@ -56,7 +57,8 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac ...@@ -56,7 +57,8 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac
private String protocol = DEFAULT_PROTOCOL; private String protocol = DEFAULT_PROTOCOL;
private List<LifecycleListener> contextLifecycleListeners = Arrays.asList(new AprLifecycleListener()); private List<LifecycleListener> contextLifecycleListeners = new ArrayList<LifecycleListener>(
Collections.singleton(new AprLifecycleListener()));
private List<TomcatContextCustomizer> tomcatContextCustomizers = new ArrayList<>(); private List<TomcatContextCustomizer> tomcatContextCustomizers = new ArrayList<>();
...@@ -120,15 +122,11 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac ...@@ -120,15 +122,11 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac
* @param context the Tomcat context * @param context the Tomcat context
*/ */
protected void configureContext(Context context) { protected void configureContext(Context context) {
for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) { this.contextLifecycleListeners.forEach(context::addLifecycleListener);
context.addLifecycleListener(lifecycleListener); this.tomcatContextCustomizers
} .forEach((customizer) -> customizer.customize(context));
for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
customizer.customize(context);
}
} }
// Needs to be protected so it can be used by subclasses
protected void customizeConnector(Connector connector) { protected void customizeConnector(Connector connector) {
int port = (getPort() >= 0 ? getPort() : 0); int port = (getPort() >= 0 ? getPort() : 0);
connector.setPort(port); connector.setPort(port);
...@@ -138,18 +136,13 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac ...@@ -138,18 +136,13 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac
if (connector.getProtocolHandler() instanceof AbstractProtocol) { if (connector.getProtocolHandler() instanceof AbstractProtocol) {
customizeProtocol((AbstractProtocol<?>) connector.getProtocolHandler()); customizeProtocol((AbstractProtocol<?>) connector.getProtocolHandler());
} }
// Don't bind to the socket prematurely if ApplicationContext is slow to start
// If ApplicationContext is slow to start we want Tomcat not to bind to the socket
// prematurely...
connector.setProperty("bindOnInit", "false"); connector.setProperty("bindOnInit", "false");
if (getSsl() != null && getSsl().isEnabled()) { if (getSsl() != null && getSsl().isEnabled()) {
TomcatConnectorCustomizer ssl = new SslConnectorCustomizer(getSsl(), getSslStoreProvider()); customizeSsl(connector);
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) {
customizer.customize(connector); customizer.customize(connector);
...@@ -162,6 +155,13 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac ...@@ -162,6 +155,13 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac
} }
} }
private void customizeSsl(Connector connector) {
new SslConnectorCustomizer(getSsl(), getSslStoreProvider()).customize(connector);
if (getHttp2() != null && getHttp2().getEnabled()) {
connector.addUpgradeProtocol(new Http2Protocol());
}
}
/** /**
* Set {@link TomcatContextCustomizer}s that should be applied to the Tomcat * Set {@link TomcatContextCustomizer}s that should be applied to the Tomcat
* {@link Context} . Calling this method will replace any existing customizers. * {@link Context} . Calling this method will replace any existing customizers.
...@@ -260,7 +260,6 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac ...@@ -260,7 +260,6 @@ public class TomcatReactiveWebServerFactory extends AbstractReactiveWebServerFac
this.contextLifecycleListeners.addAll(Arrays.asList(contextLifecycleListeners)); this.contextLifecycleListeners.addAll(Arrays.asList(contextLifecycleListeners));
} }
/** /**
* Factory method called to create the {@link TomcatWebServer}. Subclasses can * Factory method called to create the {@link TomcatWebServer}. Subclasses can
* override this method to return a different {@link TomcatWebServer} or apply * override this method to return a different {@link TomcatWebServer} or apply
......
...@@ -111,7 +111,8 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto ...@@ -111,7 +111,8 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto
private List<Valve> contextValves = new ArrayList<>(); private List<Valve> contextValves = new ArrayList<>();
private List<LifecycleListener> contextLifecycleListeners = Arrays.asList(new AprLifecycleListener()); private List<LifecycleListener> contextLifecycleListeners = new ArrayList<>(
Collections.singleton(new AprLifecycleListener()));
private List<TomcatContextCustomizer> tomcatContextCustomizers = new ArrayList<>(); private List<TomcatContextCustomizer> tomcatContextCustomizers = new ArrayList<>();
...@@ -293,19 +294,13 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto ...@@ -293,19 +294,13 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto
if (getUriEncoding() != null) { if (getUriEncoding() != null) {
connector.setURIEncoding(getUriEncoding().name()); connector.setURIEncoding(getUriEncoding().name());
} }
// Don't bind to the socket prematurely if ApplicationContext is slow to start
// If ApplicationContext is slow to start we want Tomcat not to bind to the socket
// prematurely...
connector.setProperty("bindOnInit", "false"); connector.setProperty("bindOnInit", "false");
if (getSsl() != null && getSsl().isEnabled()) { if (getSsl() != null && getSsl().isEnabled()) {
TomcatConnectorCustomizer ssl = new SslConnectorCustomizer(getSsl(), getSslStoreProvider()); customizeSsl(connector);
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) {
customizer.customize(connector); customizer.customize(connector);
...@@ -318,6 +313,13 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto ...@@ -318,6 +313,13 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto
} }
} }
private void customizeSsl(Connector connector) {
new SslConnectorCustomizer(getSsl(), getSslStoreProvider()).customize(connector);
if (getHttp2() != null && getHttp2().getEnabled()) {
connector.addUpgradeProtocol(new Http2Protocol());
}
}
/** /**
* Configure the Tomcat {@link Context}. * Configure the Tomcat {@link Context}.
* @param context the Tomcat context * @param context the Tomcat context
......
...@@ -44,8 +44,7 @@ import org.springframework.boot.web.server.SslStoreProvider; ...@@ -44,8 +44,7 @@ import org.springframework.boot.web.server.SslStoreProvider;
import org.springframework.util.ResourceUtils; import org.springframework.util.ResourceUtils;
/** /**
* {@link UndertowBuilderCustomizer} that configures SSL * {@link UndertowBuilderCustomizer} that configures SSL on the given builder instance.
* on the given builder instance.
* *
* @author Brian Clozel * @author Brian Clozel
*/ */
...@@ -59,8 +58,8 @@ class SslBuilderCustomizer implements UndertowBuilderCustomizer { ...@@ -59,8 +58,8 @@ class SslBuilderCustomizer implements UndertowBuilderCustomizer {
private final SslStoreProvider sslStoreProvider; private final SslStoreProvider sslStoreProvider;
SslBuilderCustomizer(int port, InetAddress address, SslBuilderCustomizer(int port, InetAddress address, Ssl ssl,
Ssl ssl, SslStoreProvider sslStoreProvider) { SslStoreProvider sslStoreProvider) {
this.port = port; this.port = port;
this.address = address; this.address = address;
this.ssl = ssl; this.ssl = ssl;
...@@ -73,7 +72,8 @@ class SslBuilderCustomizer implements UndertowBuilderCustomizer { ...@@ -73,7 +72,8 @@ class SslBuilderCustomizer implements UndertowBuilderCustomizer {
SSLContext sslContext = SSLContext.getInstance(this.ssl.getProtocol()); SSLContext sslContext = SSLContext.getInstance(this.ssl.getProtocol());
sslContext.init(getKeyManagers(this.ssl, this.sslStoreProvider), sslContext.init(getKeyManagers(this.ssl, this.sslStoreProvider),
getTrustManagers(this.ssl, this.sslStoreProvider), null); getTrustManagers(this.ssl, this.sslStoreProvider), null);
builder.addHttpsListener(this.port, getListenAddress(this.address), sslContext); builder.addHttpsListener(this.port, getListenAddress(this.address),
sslContext);
builder.setSocketOption(Options.SSL_CLIENT_AUTH_MODE, builder.setSocketOption(Options.SSL_CLIENT_AUTH_MODE,
getSslClientAuthMode(this.ssl)); getSslClientAuthMode(this.ssl));
if (this.ssl.getEnabledProtocols() != null) { if (this.ssl.getEnabledProtocols() != null) {
...@@ -143,7 +143,8 @@ class SslBuilderCustomizer implements UndertowBuilderCustomizer { ...@@ -143,7 +143,8 @@ class SslBuilderCustomizer implements UndertowBuilderCustomizer {
return keyManagers; return keyManagers;
} }
private KeyStore getKeyStore(Ssl ssl, SslStoreProvider sslStoreProvider) throws Exception { private KeyStore getKeyStore(Ssl ssl, SslStoreProvider sslStoreProvider)
throws Exception {
if (sslStoreProvider != null) { if (sslStoreProvider != null) {
return sslStoreProvider.getKeyStore(); return sslStoreProvider.getKeyStore();
} }
...@@ -164,7 +165,8 @@ class SslBuilderCustomizer implements UndertowBuilderCustomizer { ...@@ -164,7 +165,8 @@ class SslBuilderCustomizer implements UndertowBuilderCustomizer {
} }
} }
private KeyStore getTrustStore(Ssl ssl, SslStoreProvider sslStoreProvider) throws Exception { private KeyStore getTrustStore(Ssl ssl, SslStoreProvider sslStoreProvider)
throws Exception {
if (sslStoreProvider != null) { if (sslStoreProvider != null) {
return sslStoreProvider.getTrustStore(); return sslStoreProvider.getTrustStore();
} }
...@@ -247,4 +249,5 @@ class SslBuilderCustomizer implements UndertowBuilderCustomizer { ...@@ -247,4 +249,5 @@ class SslBuilderCustomizer implements UndertowBuilderCustomizer {
} }
} }
} }
...@@ -87,12 +87,7 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF ...@@ -87,12 +87,7 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF
builder.setDirectBuffers(this.directBuffers); builder.setDirectBuffers(this.directBuffers);
} }
if (getSsl() != null && getSsl().isEnabled()) { if (getSsl() != null && getSsl().isEnabled()) {
SslBuilderCustomizer sslBuilderCustomizer = customizeSsl(builder);
new SslBuilderCustomizer(getPort(), getAddress(), getSsl(), getSslStoreProvider());
sslBuilderCustomizer.customize(builder);
if (getHttp2() != null) {
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, getHttp2().getEnabled());
}
} }
else { else {
builder.addHttpListener(port, getListenAddress()); builder.addHttpListener(port, getListenAddress());
...@@ -103,6 +98,15 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF ...@@ -103,6 +98,15 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF
return builder; return builder;
} }
private void customizeSsl(Undertow.Builder builder) {
new SslBuilderCustomizer(getPort(), getAddress(), getSsl(), getSslStoreProvider())
.customize(builder);
if (getHttp2() != null) {
builder.setServerOption(UndertowOptions.ENABLE_HTTP2,
getHttp2().getEnabled());
}
}
private String getListenAddress() { private String getListenAddress() {
if (getAddress() == null) { if (getAddress() == null) {
return "0.0.0.0"; return "0.0.0.0";
......
...@@ -233,12 +233,7 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac ...@@ -233,12 +233,7 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
builder.setDirectBuffers(this.directBuffers); builder.setDirectBuffers(this.directBuffers);
} }
if (getSsl() != null && getSsl().isEnabled()) { if (getSsl() != null && getSsl().isEnabled()) {
SslBuilderCustomizer sslBuilderCustomizer = customizeSsl(builder);
new SslBuilderCustomizer(getPort(), getAddress(), getSsl(), getSslStoreProvider());
sslBuilderCustomizer.customize(builder);
if (getHttp2() != null) {
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, getHttp2().getEnabled());
}
} }
else { else {
builder.addHttpListener(port, getListenAddress()); builder.addHttpListener(port, getListenAddress());
...@@ -249,6 +244,15 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac ...@@ -249,6 +244,15 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
return builder; return builder;
} }
private void customizeSsl(Builder builder) {
new SslBuilderCustomizer(getPort(), getAddress(), getSsl(), getSslStoreProvider())
.customize(builder);
if (getHttp2() != null) {
builder.setServerOption(UndertowOptions.ENABLE_HTTP2,
getHttp2().getEnabled());
}
}
private String getListenAddress() { private String getListenAddress() {
if (getAddress() == null) { if (getAddress() == null) {
return "0.0.0.0"; return "0.0.0.0";
...@@ -602,7 +606,6 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac ...@@ -602,7 +606,6 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
} }
} }
private static final class LoaderHidingResourceManager implements ResourceManager { private static final class LoaderHidingResourceManager implements ResourceManager {
private final ResourceManager delegate; private final ResourceManager delegate;
......
...@@ -36,4 +36,5 @@ public class Http2 { ...@@ -36,4 +36,5 @@ public class Http2 {
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
this.enabled = enabled; this.enabled = enabled;
} }
} }
...@@ -56,13 +56,11 @@ public class SslConnectorCustomizerTests { ...@@ -56,13 +56,11 @@ public class SslConnectorCustomizerTests {
Ssl ssl = new Ssl(); Ssl ssl = new Ssl();
ssl.setKeyStore("test.jks"); ssl.setKeyStore("test.jks");
ssl.setKeyStorePassword("secret"); ssl.setKeyStorePassword("secret");
ssl.setCiphers(new String[] {"ALPHA", "BRAVO", "CHARLIE"}); ssl.setCiphers(new String[] { "ALPHA", "BRAVO", "CHARLIE" });
SslConnectorCustomizer customizer = new SslConnectorCustomizer(ssl, null); SslConnectorCustomizer customizer = new SslConnectorCustomizer(ssl, null);
Connector connector = this.tomcat.getConnector(); Connector connector = this.tomcat.getConnector();
customizer.customize(connector); customizer.customize(connector);
this.tomcat.start(); this.tomcat.start();
SSLHostConfig[] sslHostConfigs = connector.getProtocolHandler() SSLHostConfig[] sslHostConfigs = connector.getProtocolHandler()
.findSslHostConfigs(); .findSslHostConfigs();
assertThat(sslHostConfigs[0].getCiphers()).isEqualTo("ALPHA:BRAVO:CHARLIE"); assertThat(sslHostConfigs[0].getCiphers()).isEqualTo("ALPHA:BRAVO:CHARLIE");
...@@ -73,15 +71,12 @@ public class SslConnectorCustomizerTests { ...@@ -73,15 +71,12 @@ public class SslConnectorCustomizerTests {
Ssl ssl = new Ssl(); Ssl ssl = new Ssl();
ssl.setKeyPassword("password"); ssl.setKeyPassword("password");
ssl.setKeyStore("src/test/resources/test.jks"); ssl.setKeyStore("src/test/resources/test.jks");
ssl.setEnabledProtocols(new String[] { "TLSv1.1", "TLSv1.2" });
ssl.setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"}); ssl.setCiphers(new String[] { "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "BRAVO" });
ssl.setCiphers(new String[] {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "BRAVO"});
SslConnectorCustomizer customizer = new SslConnectorCustomizer(ssl, null); SslConnectorCustomizer customizer = new SslConnectorCustomizer(ssl, null);
Connector connector = this.tomcat.getConnector(); Connector connector = this.tomcat.getConnector();
customizer.customize(connector); customizer.customize(connector);
this.tomcat.start(); this.tomcat.start();
SSLHostConfig sslHostConfig = connector.getProtocolHandler() SSLHostConfig sslHostConfig = connector.getProtocolHandler()
.findSslHostConfigs()[0]; .findSslHostConfigs()[0];
assertThat(sslHostConfig.getSslProtocol()).isEqualTo("TLS"); assertThat(sslHostConfig.getSslProtocol()).isEqualTo("TLS");
...@@ -94,18 +89,16 @@ public class SslConnectorCustomizerTests { ...@@ -94,18 +89,16 @@ public class SslConnectorCustomizerTests {
Ssl ssl = new Ssl(); Ssl ssl = new Ssl();
ssl.setKeyPassword("password"); ssl.setKeyPassword("password");
ssl.setKeyStore("src/test/resources/test.jks"); ssl.setKeyStore("src/test/resources/test.jks");
ssl.setEnabledProtocols(new String[] { "TLSv1.2" });
ssl.setEnabledProtocols(new String[] {"TLSv1.2"}); ssl.setCiphers(new String[] { "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "BRAVO" });
ssl.setCiphers(new String[] {"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "BRAVO"});
SslConnectorCustomizer customizer = new SslConnectorCustomizer(ssl, null); SslConnectorCustomizer customizer = new SslConnectorCustomizer(ssl, null);
Connector connector = this.tomcat.getConnector(); Connector connector = this.tomcat.getConnector();
customizer.customize(connector); customizer.customize(connector);
this.tomcat.start(); this.tomcat.start();
SSLHostConfig sslHostConfig = connector.getProtocolHandler() SSLHostConfig sslHostConfig = connector.getProtocolHandler()
.findSslHostConfigs()[0]; .findSslHostConfigs()[0];
assertThat(sslHostConfig.getSslProtocol()).isEqualTo("TLS"); assertThat(sslHostConfig.getSslProtocol()).isEqualTo("TLS");
assertThat(sslHostConfig.getEnabledProtocols()).containsExactly("TLSv1.2"); assertThat(sslHostConfig.getEnabledProtocols()).containsExactly("TLSv1.2");
} }
} }
...@@ -67,9 +67,8 @@ public class TomcatReactiveWebServerFactoryTests ...@@ -67,9 +67,8 @@ public class TomcatReactiveWebServerFactoryTests
@Test @Test
public void defaultTomcatListeners() throws Exception { public void defaultTomcatListeners() throws Exception {
TomcatReactiveWebServerFactory factory = getFactory(); TomcatReactiveWebServerFactory factory = getFactory();
assertThat(factory.getContextLifecycleListeners()) assertThat(factory.getContextLifecycleListeners()).hasSize(1).first()
.hasSize(1) .isInstanceOf(AprLifecycleListener.class);
.first().isInstanceOf(AprLifecycleListener.class);
} }
@Test @Test
......
...@@ -104,9 +104,8 @@ public class TomcatServletWebServerFactoryTests ...@@ -104,9 +104,8 @@ public class TomcatServletWebServerFactoryTests
@Test @Test
public void defaultTomcatListeners() throws Exception { public void defaultTomcatListeners() throws Exception {
TomcatServletWebServerFactory factory = getFactory(); TomcatServletWebServerFactory factory = getFactory();
assertThat(factory.getContextLifecycleListeners()) assertThat(factory.getContextLifecycleListeners()).hasSize(1).first()
.hasSize(1) .isInstanceOf(AprLifecycleListener.class);
.first().isInstanceOf(AprLifecycleListener.class);
} }
@Test @Test
......
...@@ -39,11 +39,13 @@ public class SslBuilderCustomizerTests { ...@@ -39,11 +39,13 @@ public class SslBuilderCustomizerTests {
Ssl ssl = new Ssl(); Ssl ssl = new Ssl();
ssl.setKeyPassword("password"); ssl.setKeyPassword("password");
ssl.setKeyStore("src/test/resources/test.jks"); ssl.setKeyStore("src/test/resources/test.jks");
SslBuilderCustomizer customizer = new SslBuilderCustomizer(8080, InetAddress.getLocalHost(), ssl, null); SslBuilderCustomizer customizer = new SslBuilderCustomizer(8080,
InetAddress.getLocalHost(), ssl, null);
KeyManager[] keyManagers = ReflectionTestUtils.invokeMethod(customizer, KeyManager[] keyManagers = ReflectionTestUtils.invokeMethod(customizer,
"getKeyManagers", ssl, null); "getKeyManagers", ssl, null);
Class<?> name = Class.forName("org.springframework.boot.web.embedded.undertow" Class<?> name = Class.forName("org.springframework.boot.web.embedded.undertow"
+ ".SslBuilderCustomizer$ConfigurableAliasKeyManager"); + ".SslBuilderCustomizer$ConfigurableAliasKeyManager");
assertThat(keyManagers[0]).isNotInstanceOf(name); assertThat(keyManagers[0]).isNotInstanceOf(name);
} }
} }
...@@ -79,8 +79,8 @@ public abstract class AbstractReactiveWebServerFactoryTests { ...@@ -79,8 +79,8 @@ public abstract class AbstractReactiveWebServerFactoryTests {
factory.setPort(specificPort); factory.setPort(specificPort);
this.webServer = factory.getWebServer(new EchoHandler()); this.webServer = factory.getWebServer(new EchoHandler());
this.webServer.start(); this.webServer.start();
Mono<String> result = getWebClient().build().post() Mono<String> result = getWebClient().build().post().uri("/test")
.uri("/test").contentType(MediaType.TEXT_PLAIN) .contentType(MediaType.TEXT_PLAIN)
.body(BodyInserters.fromObject("Hello World")).exchange() .body(BodyInserters.fromObject("Hello World")).exchange()
.flatMap((response) -> response.bodyToMono(String.class)); .flatMap((response) -> response.bodyToMono(String.class));
assertThat(result.block()).isEqualTo("Hello World"); assertThat(result.block()).isEqualTo("Hello World");
...@@ -97,7 +97,6 @@ public abstract class AbstractReactiveWebServerFactoryTests { ...@@ -97,7 +97,6 @@ public abstract class AbstractReactiveWebServerFactoryTests {
testBasicSslWithKeyStore("src/test/resources/test.jks"); testBasicSslWithKeyStore("src/test/resources/test.jks");
} }
protected final void testBasicSslWithKeyStore(String keyStore) throws Exception { protected final void testBasicSslWithKeyStore(String keyStore) throws Exception {
AbstractReactiveWebServerFactory factory = getFactory(); AbstractReactiveWebServerFactory factory = getFactory();
Ssl ssl = new Ssl(); Ssl ssl = new Ssl();
...@@ -106,30 +105,27 @@ public abstract class AbstractReactiveWebServerFactoryTests { ...@@ -106,30 +105,27 @@ public abstract class AbstractReactiveWebServerFactoryTests {
factory.setSsl(ssl); factory.setSsl(ssl);
this.webServer = factory.getWebServer(new EchoHandler()); this.webServer = factory.getWebServer(new EchoHandler());
this.webServer.start(); this.webServer.start();
ReactorClientHttpConnector connector = buildTrustAllSslConnector(); ReactorClientHttpConnector connector = buildTrustAllSslConnector();
WebClient client = WebClient.builder() WebClient client = WebClient.builder()
.baseUrl("https://localhost:" + this.webServer.getPort()) .baseUrl("https://localhost:" + this.webServer.getPort())
.clientConnector(connector).build(); .clientConnector(connector).build();
Mono<String> result = client.post().uri("/test").contentType(MediaType.TEXT_PLAIN)
Mono<String> result = client.post()
.uri("/test").contentType(MediaType.TEXT_PLAIN)
.body(BodyInserters.fromObject("Hello World")).exchange() .body(BodyInserters.fromObject("Hello World")).exchange()
.flatMap((response) -> response.bodyToMono(String.class)); .flatMap((response) -> response.bodyToMono(String.class));
assertThat(result.block()).isEqualTo("Hello World"); assertThat(result.block()).isEqualTo("Hello World");
} }
protected ReactorClientHttpConnector buildTrustAllSslConnector() { protected ReactorClientHttpConnector buildTrustAllSslConnector() {
return new ReactorClientHttpConnector(options -> options return new ReactorClientHttpConnector(
.sslSupport(sslContextBuilder -> { (options) -> options.sslSupport(sslContextBuilder -> {
sslContextBuilder sslContextBuilder.sslProvider(SslProvider.JDK)
.sslProvider(SslProvider.JDK)
.trustManager(InsecureTrustManagerFactory.INSTANCE); .trustManager(InsecureTrustManagerFactory.INSTANCE);
})); }));
} }
protected WebClient.Builder getWebClient() { protected WebClient.Builder getWebClient() {
return WebClient.builder().baseUrl("http://localhost:" + this.webServer.getPort()); return WebClient.builder()
.baseUrl("http://localhost:" + this.webServer.getPort());
} }
protected static class EchoHandler implements HttpHandler { protected static class EchoHandler implements HttpHandler {
......
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