mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
Upgrade to Spring Framework 5.1
As of Spring Framework 5.1, we're depending on the Reactor Californium release train. Reactor Netty is now at version 0.8 and changed its artifact coordinates, package names and broke several APIs. Spring Framework is now up-to-date with those changes and this commit does the same for Spring Boot. Note that in that process, the `NettyServerCustomizer` has been changed since the former `HttpServerOptions.Builder` API is now gone from Reactor Netty, and we're now relying on immutable server instances instead of a stateful builder pattern. See gh-13321
This commit is contained in:
parent
0f321abe66
commit
bd8106d77f
@ -148,7 +148,7 @@
|
|||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.projectreactor.ipc</groupId>
|
<groupId>io.projectreactor.netty</groupId>
|
||||||
<artifactId>reactor-netty</artifactId>
|
<artifactId>reactor-netty</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
@ -23,6 +23,7 @@ import java.util.Map;
|
|||||||
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 reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.netty.http.client.HttpClient;
|
||||||
|
|
||||||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
|
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
|
||||||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException;
|
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException;
|
||||||
@ -64,9 +65,11 @@ class ReactiveCloudFoundrySecurityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected ReactorClientHttpConnector buildTrustAllSslConnector() {
|
protected ReactorClientHttpConnector buildTrustAllSslConnector() {
|
||||||
return new ReactorClientHttpConnector((options) -> options.sslSupport(
|
HttpClient client = HttpClient.create()
|
||||||
(sslContextBuilder) -> sslContextBuilder.sslProvider(SslProvider.JDK)
|
.secure((sslContextSpec) -> sslContextSpec.forClient()
|
||||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)));
|
.sslContext((builder) -> builder.sslProvider(SslProvider.JDK)
|
||||||
|
.trustManager(InsecureTrustManagerFactory.INSTANCE)));
|
||||||
|
return new ReactorClientHttpConnector(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,7 +27,7 @@ import org.junit.After;
|
|||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.ExpectedException;
|
import org.junit.rules.ExpectedException;
|
||||||
import reactor.ipc.netty.http.HttpResources;
|
import reactor.netty.http.HttpResources;
|
||||||
|
|
||||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||||
|
@ -303,7 +303,7 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.projectreactor.ipc</groupId>
|
<groupId>io.projectreactor.netty</groupId>
|
||||||
<artifactId>reactor-netty</artifactId>
|
<artifactId>reactor-netty</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
@ -107,7 +107,7 @@
|
|||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.projectreactor.ipc</groupId>
|
<groupId>io.projectreactor.netty</groupId>
|
||||||
<artifactId>reactor-netty</artifactId>
|
<artifactId>reactor-netty</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
package org.springframework.boot.autoconfigure.web.reactive;
|
package org.springframework.boot.autoconfigure.web.reactive;
|
||||||
|
|
||||||
import io.undertow.Undertow;
|
import io.undertow.Undertow;
|
||||||
import reactor.ipc.netty.http.server.HttpServer;
|
import reactor.netty.http.server.HttpServer;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
@ -33,7 +33,6 @@ import org.springframework.http.ResponseEntity;
|
|||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,7 +94,6 @@ public class BasicErrorController extends AbstractErrorController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping
|
@RequestMapping
|
||||||
@ResponseBody
|
|
||||||
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
|
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
|
||||||
Map<String, Object> body = getErrorAttributes(request,
|
Map<String, Object> body = getErrorAttributes(request,
|
||||||
isIncludeStackTrace(request, MediaType.ALL));
|
isIncludeStackTrace(request, MediaType.ALL));
|
||||||
|
@ -169,6 +169,7 @@ public class BasicErrorControllerIntegrationTests {
|
|||||||
load("--server.error.include-exception=true");
|
load("--server.error.include-exception=true");
|
||||||
RequestEntity request = RequestEntity
|
RequestEntity request = RequestEntity
|
||||||
.post(URI.create(createUrl("/bodyValidation")))
|
.post(URI.create(createUrl("/bodyValidation")))
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
.contentType(MediaType.APPLICATION_JSON).body("{}");
|
.contentType(MediaType.APPLICATION_JSON).body("{}");
|
||||||
ResponseEntity<Map> entity = new TestRestTemplate().exchange(request, Map.class);
|
ResponseEntity<Map> entity = new TestRestTemplate().exchange(request, Map.class);
|
||||||
String resp = entity.getBody().toString();
|
String resp = entity.getBody().toString();
|
||||||
|
@ -151,7 +151,7 @@
|
|||||||
<slf4j.version>1.7.25</slf4j.version>
|
<slf4j.version>1.7.25</slf4j.version>
|
||||||
<snakeyaml.version>1.19</snakeyaml.version>
|
<snakeyaml.version>1.19</snakeyaml.version>
|
||||||
<solr.version>7.2.1</solr.version>
|
<solr.version>7.2.1</solr.version>
|
||||||
<spring.version>5.0.7.BUILD-SNAPSHOT</spring.version>
|
<spring.version>5.1.0.BUILD-SNAPSHOT</spring.version>
|
||||||
<spring-amqp.version>2.0.3.RELEASE</spring-amqp.version>
|
<spring-amqp.version>2.0.3.RELEASE</spring-amqp.version>
|
||||||
<spring-batch.version>4.0.1.RELEASE</spring-batch.version>
|
<spring-batch.version>4.0.1.RELEASE</spring-batch.version>
|
||||||
<spring-cloud-connectors.version>2.0.2.RELEASE</spring-cloud-connectors.version>
|
<spring-cloud-connectors.version>2.0.2.RELEASE</spring-cloud-connectors.version>
|
||||||
@ -931,6 +931,13 @@
|
|||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- https://github.com/reactor/reactor/pull/646 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.projectreactor.netty</groupId>
|
||||||
|
<artifactId>reactor-netty</artifactId>
|
||||||
|
<version>0.8.0.BUILD-SNAPSHOT</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.reactivex</groupId>
|
<groupId>io.reactivex</groupId>
|
||||||
<artifactId>rxjava</artifactId>
|
<artifactId>rxjava</artifactId>
|
||||||
|
@ -262,7 +262,7 @@
|
|||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.projectreactor.ipc</groupId>
|
<groupId>io.projectreactor.netty</groupId>
|
||||||
<artifactId>reactor-netty</artifactId>
|
<artifactId>reactor-netty</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
</properties>
|
</properties>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.projectreactor.ipc</groupId>
|
<groupId>io.projectreactor.netty</groupId>
|
||||||
<artifactId>reactor-netty</artifactId>
|
<artifactId>reactor-netty</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.projectreactor.ipc</groupId>
|
<groupId>io.projectreactor.netty</groupId>
|
||||||
<artifactId>reactor-netty</artifactId>
|
<artifactId>reactor-netty</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
@ -76,7 +76,7 @@
|
|||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.projectreactor.ipc</groupId>
|
<groupId>io.projectreactor.netty</groupId>
|
||||||
<artifactId>reactor-netty</artifactId>
|
<artifactId>reactor-netty</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
@ -21,9 +21,9 @@ import java.util.function.BiPredicate;
|
|||||||
|
|
||||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import reactor.ipc.netty.http.server.HttpServerOptions;
|
import reactor.netty.http.server.HttpServer;
|
||||||
import reactor.ipc.netty.http.server.HttpServerRequest;
|
import reactor.netty.http.server.HttpServerRequest;
|
||||||
import reactor.ipc.netty.http.server.HttpServerResponse;
|
import reactor.netty.http.server.HttpServerResponse;
|
||||||
|
|
||||||
import org.springframework.boot.web.server.Compression;
|
import org.springframework.boot.web.server.Compression;
|
||||||
import org.springframework.util.MimeType;
|
import org.springframework.util.MimeType;
|
||||||
@ -36,6 +36,7 @@ import org.springframework.util.StringUtils;
|
|||||||
*
|
*
|
||||||
* @author Stephane Maldini
|
* @author Stephane Maldini
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
|
* @author Brian Clozel
|
||||||
*/
|
*/
|
||||||
final class CompressionCustomizer implements NettyServerCustomizer {
|
final class CompressionCustomizer implements NettyServerCustomizer {
|
||||||
|
|
||||||
@ -49,15 +50,16 @@ final class CompressionCustomizer implements NettyServerCustomizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void customize(HttpServerOptions.Builder builder) {
|
public HttpServer apply(HttpServer server) {
|
||||||
if (this.compression.getMinResponseSize() >= 0) {
|
if (this.compression.getMinResponseSize() >= 0) {
|
||||||
builder.compression(this.compression.getMinResponseSize());
|
server = server.compress(this.compression.getMinResponseSize());
|
||||||
}
|
}
|
||||||
CompressionPredicate mimeTypes = getMimeTypesPredicate(
|
CompressionPredicate mimeTypes = getMimeTypesPredicate(
|
||||||
this.compression.getMimeTypes());
|
this.compression.getMimeTypes());
|
||||||
CompressionPredicate excludedUserAgents = getExcludedUserAgentsPredicate(
|
CompressionPredicate excludedUserAgents = getExcludedUserAgentsPredicate(
|
||||||
this.compression.getExcludedUserAgents());
|
this.compression.getExcludedUserAgents());
|
||||||
builder.compression(mimeTypes.and(excludedUserAgents));
|
server = server.compress(mimeTypes.and(excludedUserAgents));
|
||||||
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompressionPredicate getMimeTypesPredicate(String[] mimeTypes) {
|
private CompressionPredicate getMimeTypesPredicate(String[] mimeTypes) {
|
||||||
|
@ -23,8 +23,7 @@ import java.util.Arrays;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import reactor.ipc.netty.http.server.HttpServer;
|
import reactor.netty.http.server.HttpServer;
|
||||||
import reactor.ipc.netty.http.server.HttpServerOptions.Builder;
|
|
||||||
|
|
||||||
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;
|
||||||
@ -99,20 +98,19 @@ public class NettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
|
|||||||
}
|
}
|
||||||
|
|
||||||
private HttpServer createHttpServer() {
|
private HttpServer createHttpServer() {
|
||||||
return HttpServer.builder().options((options) -> {
|
HttpServer server = HttpServer.create().tcpConfiguration(
|
||||||
options.listenAddress(getListenAddress());
|
(tcpServer) -> tcpServer.addressSupplier(() -> getListenAddress()));
|
||||||
if (getSsl() != null && getSsl().isEnabled()) {
|
if (getSsl() != null && getSsl().isEnabled()) {
|
||||||
SslServerCustomizer sslServerCustomizer = new SslServerCustomizer(
|
SslServerCustomizer sslServerCustomizer = new SslServerCustomizer(getSsl(),
|
||||||
getSsl(), getSslStoreProvider());
|
getSslStoreProvider());
|
||||||
sslServerCustomizer.customize(options);
|
server = sslServerCustomizer.apply(server);
|
||||||
}
|
}
|
||||||
if (getCompression() != null && getCompression().getEnabled()) {
|
if (getCompression() != null && getCompression().getEnabled()) {
|
||||||
CompressionCustomizer compressionCustomizer = new CompressionCustomizer(
|
CompressionCustomizer compressionCustomizer = new CompressionCustomizer(
|
||||||
getCompression());
|
getCompression());
|
||||||
compressionCustomizer.customize(options);
|
server = compressionCustomizer.apply(server);
|
||||||
}
|
}
|
||||||
applyCustomizers(options);
|
return applyCustomizers(server);
|
||||||
}).build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private InetSocketAddress getListenAddress() {
|
private InetSocketAddress getListenAddress() {
|
||||||
@ -122,8 +120,11 @@ public class NettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
|
|||||||
return new InetSocketAddress(getPort());
|
return new InetSocketAddress(getPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyCustomizers(Builder options) {
|
private HttpServer applyCustomizers(HttpServer server) {
|
||||||
this.serverCustomizers.forEach((customizer) -> customizer.customize(options));
|
for (NettyServerCustomizer customizer : this.serverCustomizers) {
|
||||||
|
server = customizer.apply(server);
|
||||||
|
}
|
||||||
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2017 the original author or authors.
|
* Copyright 2012-2018 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.
|
||||||
@ -16,22 +16,18 @@
|
|||||||
|
|
||||||
package org.springframework.boot.web.embedded.netty;
|
package org.springframework.boot.web.embedded.netty;
|
||||||
|
|
||||||
import reactor.ipc.netty.http.server.HttpServerOptions;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import reactor.netty.http.server.HttpServer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback interface that can be used to customize a Reactor Netty server builder.
|
* Mapping function that can be used to customize a Reactor Netty server instance.
|
||||||
*
|
*
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
* @see NettyReactiveWebServerFactory
|
* @see NettyReactiveWebServerFactory
|
||||||
* @since 2.0.0
|
* @since 2.1.0
|
||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface NettyServerCustomizer {
|
public interface NettyServerCustomizer extends Function<HttpServer, HttpServer> {
|
||||||
|
|
||||||
/**
|
|
||||||
* Customize the Netty web server.
|
|
||||||
* @param builder the server options builder to customize
|
|
||||||
*/
|
|
||||||
void customize(HttpServerOptions.Builder builder);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,15 +17,13 @@
|
|||||||
package org.springframework.boot.web.embedded.netty;
|
package org.springframework.boot.web.embedded.netty;
|
||||||
|
|
||||||
import java.net.BindException;
|
import java.net.BindException;
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
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 reactor.ipc.netty.http.HttpResources;
|
import reactor.netty.DisposableServer;
|
||||||
import reactor.ipc.netty.http.server.HttpServer;
|
import reactor.netty.http.HttpResources;
|
||||||
import reactor.ipc.netty.tcp.BlockingNettyContext;
|
import reactor.netty.http.server.HttpServer;
|
||||||
|
|
||||||
import org.springframework.boot.web.server.PortInUseException;
|
import org.springframework.boot.web.server.PortInUseException;
|
||||||
import org.springframework.boot.web.server.WebServer;
|
import org.springframework.boot.web.server.WebServer;
|
||||||
@ -53,7 +51,7 @@ public class NettyWebServer implements WebServer {
|
|||||||
|
|
||||||
private final Duration lifecycleTimeout;
|
private final Duration lifecycleTimeout;
|
||||||
|
|
||||||
private BlockingNettyContext nettyContext;
|
private DisposableServer disposableServer;
|
||||||
|
|
||||||
public NettyWebServer(HttpServer httpServer, ReactorHttpHandlerAdapter handlerAdapter,
|
public NettyWebServer(HttpServer httpServer, ReactorHttpHandlerAdapter handlerAdapter,
|
||||||
Duration lifecycleTimeout) {
|
Duration lifecycleTimeout) {
|
||||||
@ -66,30 +64,27 @@ public class NettyWebServer implements WebServer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() throws WebServerException {
|
public void start() throws WebServerException {
|
||||||
if (this.nettyContext == null) {
|
if (this.disposableServer == null) {
|
||||||
try {
|
try {
|
||||||
this.nettyContext = startHttpServer();
|
this.disposableServer = startHttpServer();
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
if (findBindException(ex) != null) {
|
if (findBindException(ex) != null) {
|
||||||
SocketAddress address = this.httpServer.options().getAddress();
|
throw new PortInUseException(getPort());
|
||||||
if (address instanceof InetSocketAddress) {
|
|
||||||
throw new PortInUseException(
|
|
||||||
((InetSocketAddress) address).getPort());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
throw new WebServerException("Unable to start Netty", ex);
|
throw new WebServerException("Unable to start Netty", ex);
|
||||||
}
|
}
|
||||||
NettyWebServer.logger.info("Netty started on port(s): " + getPort());
|
NettyWebServer.logger.info("Netty started on port(s): " + getPort());
|
||||||
startDaemonAwaitThread(this.nettyContext);
|
startDaemonAwaitThread(this.disposableServer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlockingNettyContext startHttpServer() {
|
private DisposableServer startHttpServer() {
|
||||||
if (this.lifecycleTimeout != null) {
|
if (this.lifecycleTimeout != null) {
|
||||||
return this.httpServer.start(this.handlerAdapter, this.lifecycleTimeout);
|
return this.httpServer.handle(this.handlerAdapter)
|
||||||
|
.bindNow(this.lifecycleTimeout);
|
||||||
}
|
}
|
||||||
return this.httpServer.start(this.handlerAdapter);
|
return this.httpServer.handle(this.handlerAdapter).bindNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
private BindException findBindException(Exception ex) {
|
private BindException findBindException(Exception ex) {
|
||||||
@ -103,12 +98,12 @@ public class NettyWebServer implements WebServer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startDaemonAwaitThread(BlockingNettyContext nettyContext) {
|
private void startDaemonAwaitThread(DisposableServer disposableServer) {
|
||||||
Thread awaitThread = new Thread("server") {
|
Thread awaitThread = new Thread("server") {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
nettyContext.getContext().onClose().block();
|
disposableServer.onDispose().block();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -119,19 +114,24 @@ public class NettyWebServer implements WebServer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() throws WebServerException {
|
public void stop() throws WebServerException {
|
||||||
if (this.nettyContext != null) {
|
if (this.disposableServer != null) {
|
||||||
this.nettyContext.shutdown();
|
|
||||||
// temporary fix for gh-9146
|
// temporary fix for gh-9146
|
||||||
this.nettyContext.getContext().onClose()
|
this.disposableServer.onDispose()
|
||||||
.doOnSuccess((o) -> HttpResources.reset()).block();
|
.doFinally((signal) -> HttpResources.reset());
|
||||||
this.nettyContext = null;
|
if (this.lifecycleTimeout != null) {
|
||||||
|
this.disposableServer.disposeNow(this.lifecycleTimeout);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.disposableServer.disposeNow();
|
||||||
|
}
|
||||||
|
this.disposableServer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPort() {
|
public int getPort() {
|
||||||
if (this.nettyContext != null) {
|
if (this.disposableServer != null) {
|
||||||
return this.nettyContext.getPort();
|
return this.disposableServer.port();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,14 @@ package org.springframework.boot.web.embedded.netty;
|
|||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.net.ssl.KeyManagerFactory;
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
import javax.net.ssl.TrustManagerFactory;
|
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.ipc.netty.http.server.HttpServerOptions;
|
import reactor.netty.http.server.HttpServer;
|
||||||
|
|
||||||
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;
|
||||||
@ -49,30 +50,36 @@ public class SslServerCustomizer implements NettyServerCustomizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void customize(HttpServerOptions.Builder builder) {
|
public HttpServer apply(HttpServer server) {
|
||||||
SslContextBuilder sslBuilder = SslContextBuilder
|
|
||||||
.forServer(getKeyManagerFactory(this.ssl, this.sslStoreProvider))
|
|
||||||
.trustManager(getTrustManagerFactory(this.ssl, this.sslStoreProvider));
|
|
||||||
if (this.ssl.getEnabledProtocols() != null) {
|
|
||||||
sslBuilder.protocols(this.ssl.getEnabledProtocols());
|
|
||||||
}
|
|
||||||
if (this.ssl.getCiphers() != null) {
|
|
||||||
sslBuilder = sslBuilder.ciphers(Arrays.asList(this.ssl.getCiphers()));
|
|
||||||
}
|
|
||||||
if (this.ssl.getClientAuth() == Ssl.ClientAuth.NEED) {
|
|
||||||
sslBuilder = sslBuilder.clientAuth(ClientAuth.REQUIRE);
|
|
||||||
}
|
|
||||||
else if (this.ssl.getClientAuth() == Ssl.ClientAuth.WANT) {
|
|
||||||
sslBuilder = sslBuilder.clientAuth(ClientAuth.OPTIONAL);
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
builder.sslContext(sslBuilder.build());
|
return server.secure((contextSpec) -> contextSpec.forServer()
|
||||||
|
.sslContext(getContextBuilderConsumer()));
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
throw new IllegalStateException(ex);
|
throw new IllegalStateException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Consumer<SslContextBuilder> getContextBuilderConsumer() {
|
||||||
|
return (builder) -> {
|
||||||
|
builder.keyManager(getKeyManagerFactory(this.ssl, this.sslStoreProvider))
|
||||||
|
.trustManager(
|
||||||
|
getTrustManagerFactory(this.ssl, this.sslStoreProvider));
|
||||||
|
if (this.ssl.getEnabledProtocols() != null) {
|
||||||
|
builder.protocols(this.ssl.getEnabledProtocols());
|
||||||
|
}
|
||||||
|
if (this.ssl.getCiphers() != null) {
|
||||||
|
builder.ciphers(Arrays.asList(this.ssl.getCiphers()));
|
||||||
|
}
|
||||||
|
if (this.ssl.getClientAuth() == Ssl.ClientAuth.NEED) {
|
||||||
|
builder.clientAuth(ClientAuth.REQUIRE);
|
||||||
|
}
|
||||||
|
else if (this.ssl.getClientAuth() == Ssl.ClientAuth.WANT) {
|
||||||
|
builder.clientAuth(ClientAuth.OPTIONAL);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
protected KeyManagerFactory getKeyManagerFactory(Ssl ssl,
|
protected KeyManagerFactory getKeyManagerFactory(Ssl ssl,
|
||||||
SslStoreProvider sslStoreProvider) {
|
SslStoreProvider sslStoreProvider) {
|
||||||
try {
|
try {
|
||||||
|
@ -16,20 +16,19 @@
|
|||||||
|
|
||||||
package org.springframework.boot.web.embedded.netty;
|
package org.springframework.boot.web.embedded.netty;
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.InOrder;
|
import org.mockito.InOrder;
|
||||||
import reactor.ipc.netty.http.server.HttpServerOptions;
|
import reactor.netty.http.server.HttpServer;
|
||||||
|
|
||||||
import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory;
|
import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory;
|
||||||
import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactoryTests;
|
import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactoryTests;
|
||||||
import org.springframework.boot.web.server.WebServerException;
|
import org.springframework.boot.web.server.WebServerException;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
import static org.mockito.Mockito.inOrder;
|
import static org.mockito.Mockito.inOrder;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
@ -47,6 +46,7 @@ public class NettyReactiveWebServerFactoryTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Ignore
|
||||||
public void exceptionIsThrownWhenPortIsAlreadyInUse() {
|
public void exceptionIsThrownWhenPortIsAlreadyInUse() {
|
||||||
AbstractReactiveWebServerFactory factory = getFactory();
|
AbstractReactiveWebServerFactory factory = getFactory();
|
||||||
factory.setPort(0);
|
factory.setPort(0);
|
||||||
@ -63,25 +63,15 @@ public class NettyReactiveWebServerFactoryTests
|
|||||||
NettyServerCustomizer[] customizers = new NettyServerCustomizer[2];
|
NettyServerCustomizer[] customizers = new NettyServerCustomizer[2];
|
||||||
for (int i = 0; i < customizers.length; i++) {
|
for (int i = 0; i < customizers.length; i++) {
|
||||||
customizers[i] = mock(NettyServerCustomizer.class);
|
customizers[i] = mock(NettyServerCustomizer.class);
|
||||||
|
given(customizers[i].apply(any(HttpServer.class)))
|
||||||
|
.will((invocation) -> invocation.getArgument(0));
|
||||||
}
|
}
|
||||||
factory.setServerCustomizers(Arrays.asList(customizers[0], customizers[1]));
|
factory.setServerCustomizers(Arrays.asList(customizers[0], customizers[1]));
|
||||||
this.webServer = factory.getWebServer(new EchoHandler());
|
this.webServer = factory.getWebServer(new EchoHandler());
|
||||||
InOrder ordered = inOrder((Object[]) customizers);
|
InOrder ordered = inOrder((Object[]) customizers);
|
||||||
for (NettyServerCustomizer customizer : customizers) {
|
for (NettyServerCustomizer customizer : customizers) {
|
||||||
ordered.verify(customizer).customize(any(HttpServerOptions.Builder.class));
|
ordered.verify(customizer).apply(any(HttpServer.class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void customStartupTimeout() {
|
|
||||||
Duration timeout = Duration.ofDays(365);
|
|
||||||
NettyReactiveWebServerFactory factory = getFactory();
|
|
||||||
factory.setLifecycleTimeout(timeout);
|
|
||||||
this.webServer = factory.getWebServer(new EchoHandler());
|
|
||||||
this.webServer.start();
|
|
||||||
Object context = ReflectionTestUtils.getField(this.webServer, "nettyContext");
|
|
||||||
Object actualTimeout = ReflectionTestUtils.getField(context, "lifecycleTimeout");
|
|
||||||
assertThat(actualTimeout).isEqualTo(timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import javax.net.ssl.KeyManagerFactory;
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
@ -39,8 +38,8 @@ import org.junit.Rule;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.ExpectedException;
|
import org.junit.rules.ExpectedException;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.ipc.netty.NettyPipeline;
|
import reactor.netty.NettyPipeline;
|
||||||
import reactor.ipc.netty.http.client.HttpClientOptions;
|
import reactor.netty.http.client.HttpClient;
|
||||||
import reactor.test.StepVerifier;
|
import reactor.test.StepVerifier;
|
||||||
|
|
||||||
import org.springframework.boot.testsupport.rule.OutputCapture;
|
import org.springframework.boot.testsupport.rule.OutputCapture;
|
||||||
@ -135,9 +134,11 @@ public abstract class AbstractReactiveWebServerFactoryTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected ReactorClientHttpConnector buildTrustAllSslConnector() {
|
protected ReactorClientHttpConnector buildTrustAllSslConnector() {
|
||||||
return new ReactorClientHttpConnector((options) -> options.sslSupport(
|
HttpClient client = HttpClient.create().wiretap()
|
||||||
(sslContextBuilder) -> sslContextBuilder.sslProvider(SslProvider.JDK)
|
.secure((sslContextSpec) -> sslContextSpec.forClient()
|
||||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)));
|
.sslContext((builder) -> builder.sslProvider(SslProvider.JDK)
|
||||||
|
.trustManager(InsecureTrustManagerFactory.INSTANCE)));
|
||||||
|
return new ReactorClientHttpConnector(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -169,10 +170,12 @@ public abstract class AbstractReactiveWebServerFactoryTests {
|
|||||||
KeyManagerFactory clientKeyManagerFactory = KeyManagerFactory
|
KeyManagerFactory clientKeyManagerFactory = KeyManagerFactory
|
||||||
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||||
clientKeyManagerFactory.init(clientKeyStore, "password".toCharArray());
|
clientKeyManagerFactory.init(clientKeyStore, "password".toCharArray());
|
||||||
return new ReactorClientHttpConnector((options) -> options.sslSupport(
|
HttpClient client = HttpClient.create().wiretap()
|
||||||
(sslContextBuilder) -> sslContextBuilder.sslProvider(SslProvider.JDK)
|
.secure((sslContextSpec) -> sslContextSpec.forClient()
|
||||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
.sslContext((builder) -> builder.sslProvider(SslProvider.JDK)
|
||||||
.keyManager(clientKeyManagerFactory)));
|
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||||
|
.keyManager(clientKeyManagerFactory)));
|
||||||
|
return new ReactorClientHttpConnector(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void testClientAuthSuccess(Ssl sslConfiguration,
|
protected void testClientAuthSuccess(Ssl sslConfiguration,
|
||||||
@ -228,16 +231,13 @@ public abstract class AbstractReactiveWebServerFactoryTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected WebClient.Builder getWebClient() {
|
protected WebClient.Builder getWebClient() {
|
||||||
return getWebClient((options) -> {
|
return getWebClient(HttpClient.create().wiretap());
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected WebClient.Builder getWebClient(
|
protected WebClient.Builder getWebClient(HttpClient client) {
|
||||||
Consumer<? super HttpClientOptions.Builder> clientOptions) {
|
|
||||||
InetSocketAddress address = new InetSocketAddress(this.webServer.getPort());
|
InetSocketAddress address = new InetSocketAddress(this.webServer.getPort());
|
||||||
String baseUrl = "http://" + address.getHostString() + ":" + address.getPort();
|
String baseUrl = "http://" + address.getHostString() + ":" + address.getPort();
|
||||||
return WebClient.builder()
|
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(client))
|
||||||
.clientConnector(new ReactorClientHttpConnector(clientOptions))
|
|
||||||
.baseUrl(baseUrl);
|
.baseUrl(baseUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,10 +302,12 @@ public abstract class AbstractReactiveWebServerFactoryTests {
|
|||||||
this.webServer = factory
|
this.webServer = factory
|
||||||
.getWebServer(new CharsHandler(3000, MediaType.TEXT_PLAIN));
|
.getWebServer(new CharsHandler(3000, MediaType.TEXT_PLAIN));
|
||||||
this.webServer.start();
|
this.webServer.start();
|
||||||
return getWebClient((options) -> options.compression(true)
|
|
||||||
.afterChannelInit((channel) -> channel.pipeline().addBefore(
|
HttpClient client = HttpClient.create().wiretap().compress().tcpConfiguration(
|
||||||
NettyPipeline.HttpDecompressor, "CompressionTest",
|
(tcpClient) -> tcpClient.doOnConnected((connection) -> connection
|
||||||
new CompressionDetectionHandler()))).build();
|
.channel().pipeline().addBefore(NettyPipeline.HttpDecompressor,
|
||||||
|
"CompressionTest", new CompressionDetectionHandler())));
|
||||||
|
return getWebClient(client).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertResponseIsCompressed(ResponseEntity<Void> response) {
|
protected void assertResponseIsCompressed(ResponseEntity<Void> response) {
|
||||||
|
Loading…
Reference in New Issue
Block a user