Consolidate Undertow WebServers and simplify their constructors
Closes gh-21391 Co-authored-by: Phillip Webb <pwebb@pivotal.io>
This commit is contained in:
parent
0d00947735
commit
c42571ba40
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.web.embedded.undertow;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import io.undertow.Undertow;
|
||||||
|
import io.undertow.server.HttpHandler;
|
||||||
|
import io.undertow.server.handlers.accesslog.AccessLogHandler;
|
||||||
|
import io.undertow.server.handlers.accesslog.DefaultAccessLogReceiver;
|
||||||
|
import org.xnio.OptionMap;
|
||||||
|
import org.xnio.Options;
|
||||||
|
import org.xnio.Xnio;
|
||||||
|
import org.xnio.XnioWorker;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link HttpHandlerFactory} for an {@link AccessLogHandler}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
class AccessLogHttpHandlerFactory implements HttpHandlerFactory {
|
||||||
|
|
||||||
|
private final File directory;
|
||||||
|
|
||||||
|
private final String pattern;
|
||||||
|
|
||||||
|
private final String prefix;
|
||||||
|
|
||||||
|
private final String suffix;
|
||||||
|
|
||||||
|
private final boolean rotate;
|
||||||
|
|
||||||
|
AccessLogHttpHandlerFactory(File directory, String pattern, String prefix, String suffix, boolean rotate) {
|
||||||
|
this.directory = directory;
|
||||||
|
this.pattern = pattern;
|
||||||
|
this.prefix = prefix;
|
||||||
|
this.suffix = suffix;
|
||||||
|
this.rotate = rotate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpHandler getHandler(HttpHandler next) {
|
||||||
|
try {
|
||||||
|
createAccessLogDirectoryIfNecessary();
|
||||||
|
XnioWorker worker = createWorker();
|
||||||
|
String baseName = (this.prefix != null) ? this.prefix : "access_log.";
|
||||||
|
String formatString = (this.pattern != null) ? this.pattern : "common";
|
||||||
|
return new ClosableAccessLogHandler(next, worker,
|
||||||
|
new DefaultAccessLogReceiver(worker, this.directory, baseName, this.suffix, this.rotate),
|
||||||
|
formatString);
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new IllegalStateException("Failed to create AccessLogHandler", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createAccessLogDirectoryIfNecessary() {
|
||||||
|
Assert.state(this.directory != null, "Access log directory is not set");
|
||||||
|
if (!this.directory.isDirectory() && !this.directory.mkdirs()) {
|
||||||
|
throw new IllegalStateException("Failed to create access log directory '" + this.directory + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private XnioWorker createWorker() throws IOException {
|
||||||
|
Xnio xnio = Xnio.getInstance(Undertow.class.getClassLoader());
|
||||||
|
return xnio.createWorker(OptionMap.builder().set(Options.THREAD_DAEMON, true).getMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Closeable} variant of {@link AccessLogHandler}.
|
||||||
|
*/
|
||||||
|
private static class ClosableAccessLogHandler extends AccessLogHandler implements Closeable {
|
||||||
|
|
||||||
|
private final DefaultAccessLogReceiver accessLogReceiver;
|
||||||
|
|
||||||
|
private final XnioWorker worker;
|
||||||
|
|
||||||
|
ClosableAccessLogHandler(HttpHandler next, XnioWorker worker, DefaultAccessLogReceiver accessLogReceiver,
|
||||||
|
String formatString) {
|
||||||
|
super(next, accessLogReceiver, formatString, Undertow.class.getClassLoader());
|
||||||
|
this.worker = worker;
|
||||||
|
this.accessLogReceiver = accessLogReceiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
try {
|
||||||
|
this.accessLogReceiver.close();
|
||||||
|
this.worker.shutdown();
|
||||||
|
this.worker.awaitTermination(30, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
catch (InterruptedException ex) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -37,30 +37,28 @@ import org.springframework.util.MimeType;
|
||||||
import org.springframework.util.MimeTypeUtils;
|
import org.springframework.util.MimeTypeUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure the HTTP compression on an Undertow {@link HttpHandler}.
|
* {@link HttpHandlerFactory} that adds a compression handler.
|
||||||
*
|
*
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
*/
|
*/
|
||||||
final class UndertowCompressionConfigurer {
|
class CompressionHttpHandlerFactory implements HttpHandlerFactory {
|
||||||
|
|
||||||
private UndertowCompressionConfigurer() {
|
private final Compression compression;
|
||||||
|
|
||||||
|
CompressionHttpHandlerFactory(Compression compression) {
|
||||||
|
this.compression = compression;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Optionally wrap the given {@link HttpHandler} for HTTP compression support.
|
public HttpHandler getHandler(HttpHandler next) {
|
||||||
* @param compression the HTTP compression configuration
|
if (!this.compression.getEnabled()) {
|
||||||
* @param httpHandler the HTTP handler to wrap
|
return next;
|
||||||
* @return the wrapped HTTP handler if compression is enabled, or the handler itself
|
|
||||||
*/
|
|
||||||
static HttpHandler configureCompression(Compression compression, HttpHandler httpHandler) {
|
|
||||||
if (compression == null || !compression.getEnabled()) {
|
|
||||||
return httpHandler;
|
|
||||||
}
|
}
|
||||||
ContentEncodingRepository repository = new ContentEncodingRepository();
|
ContentEncodingRepository repository = new ContentEncodingRepository();
|
||||||
repository.addEncodingHandler("gzip", new GzipEncodingProvider(), 50,
|
repository.addEncodingHandler("gzip", new GzipEncodingProvider(), 50,
|
||||||
Predicates.and(getCompressionPredicates(compression)));
|
Predicates.and(getCompressionPredicates(this.compression)));
|
||||||
return new EncodingHandler(repository).setNext(httpHandler);
|
return new EncodingHandler(repository).setNext(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Predicate[] getCompressionPredicates(Compression compression) {
|
private static Predicate[] getCompressionPredicates(Compression compression) {
|
||||||
|
|
@ -76,6 +74,9 @@ final class UndertowCompressionConfigurer {
|
||||||
return predicates.toArray(new Predicate[0]);
|
return predicates.toArray(new Predicate[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate used to match specific mime types.
|
||||||
|
*/
|
||||||
private static class CompressibleMimeTypePredicate implements Predicate {
|
private static class CompressibleMimeTypePredicate implements Predicate {
|
||||||
|
|
||||||
private final List<MimeType> mimeTypes;
|
private final List<MimeType> mimeTypes;
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.boot.web.embedded.undertow;
|
package org.springframework.boot.web.embedded.undertow;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
import io.undertow.Undertow.Builder;
|
import io.undertow.Undertow.Builder;
|
||||||
|
|
||||||
|
|
@ -32,6 +33,14 @@ import org.springframework.boot.web.server.ConfigurableWebServerFactory;
|
||||||
*/
|
*/
|
||||||
public interface ConfigurableUndertowWebServerFactory extends ConfigurableWebServerFactory {
|
public interface ConfigurableUndertowWebServerFactory extends ConfigurableWebServerFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set {@link UndertowBuilderCustomizer}s that should be applied to the Undertow
|
||||||
|
* {@link Builder}. Calling this method will replace any existing customizers.
|
||||||
|
* @param customizers the customizers to set
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
void setBuilderCustomizers(Collection<? extends UndertowBuilderCustomizer> customizers);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add {@link UndertowBuilderCustomizer}s that should be used to customize the
|
* Add {@link UndertowBuilderCustomizer}s that should be used to customize the
|
||||||
* Undertow {@link Builder}.
|
* Undertow {@link Builder}.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.web.embedded.undertow;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
|
||||||
|
import io.undertow.server.HttpHandler;
|
||||||
|
import io.undertow.server.HttpServerExchange;
|
||||||
|
import io.undertow.servlet.api.DeploymentManager;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link HttpHandlerFactory} that for a {@link DeploymentManager}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
class DeploymentManagerHttpHandlerFactory implements HttpHandlerFactory {
|
||||||
|
|
||||||
|
private final DeploymentManager deploymentManager;
|
||||||
|
|
||||||
|
DeploymentManagerHttpHandlerFactory(DeploymentManager deploymentManager) {
|
||||||
|
this.deploymentManager = deploymentManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpHandler getHandler(HttpHandler next) {
|
||||||
|
Assert.state(next == null, "DeploymentManagerHttpHandlerFactory must be first");
|
||||||
|
return new DeploymentManagerHandler(this.deploymentManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeploymentManager getDeploymentManager() {
|
||||||
|
return this.deploymentManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link HttpHandler} that delegates to a {@link DeploymentManager}.
|
||||||
|
*/
|
||||||
|
static class DeploymentManagerHandler implements HttpHandler, Closeable {
|
||||||
|
|
||||||
|
private final DeploymentManager deploymentManager;
|
||||||
|
|
||||||
|
private final HttpHandler handler;
|
||||||
|
|
||||||
|
DeploymentManagerHandler(DeploymentManager deploymentManager) {
|
||||||
|
this.deploymentManager = deploymentManager;
|
||||||
|
try {
|
||||||
|
this.handler = deploymentManager.start();
|
||||||
|
}
|
||||||
|
catch (ServletException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||||
|
this.handler.handleRequest(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
try {
|
||||||
|
this.deploymentManager.stop();
|
||||||
|
this.deploymentManager.undeploy();
|
||||||
|
}
|
||||||
|
catch (ServletException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeploymentManager getDeploymentManager() {
|
||||||
|
return this.deploymentManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ package org.springframework.boot.web.embedded.undertow;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import io.undertow.server.HttpHandler;
|
||||||
import io.undertow.server.handlers.GracefulShutdownHandler;
|
import io.undertow.server.handlers.GracefulShutdownHandler;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
@ -25,34 +26,33 @@ import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.boot.web.server.GracefulShutdown;
|
import org.springframework.boot.web.server.GracefulShutdown;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link GracefulShutdown} for Undertow.
|
* A {@link GracefulShutdownHandler} with support for our own {@link GracefulShutdown}
|
||||||
|
* interface.
|
||||||
*
|
*
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
*/
|
*/
|
||||||
class UndertowGracefulShutdown implements GracefulShutdown {
|
class GracefulShutdownHttpHandler extends GracefulShutdownHandler implements GracefulShutdown {
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(UndertowGracefulShutdown.class);
|
private static final Log logger = LogFactory.getLog(GracefulShutdownHttpHandler.class);
|
||||||
|
|
||||||
private final GracefulShutdownHandler gracefulShutdownHandler;
|
private final Duration gracePeriod;
|
||||||
|
|
||||||
private final Duration period;
|
|
||||||
|
|
||||||
private volatile boolean shuttingDown;
|
private volatile boolean shuttingDown;
|
||||||
|
|
||||||
UndertowGracefulShutdown(GracefulShutdownHandler gracefulShutdownHandler, Duration period) {
|
GracefulShutdownHttpHandler(HttpHandler next, Duration period) {
|
||||||
this.gracefulShutdownHandler = gracefulShutdownHandler;
|
super(next);
|
||||||
this.period = period;
|
this.gracePeriod = period;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shutDownGracefully() {
|
public boolean shutDownGracefully() {
|
||||||
logger.info("Commencing graceful shutdown, allowing up to " + this.period.getSeconds()
|
logger.info("Commencing graceful shutdown, allowing up to " + this.gracePeriod.getSeconds()
|
||||||
+ "s for active requests to complete");
|
+ "s for active requests to complete");
|
||||||
this.gracefulShutdownHandler.shutdown();
|
shutdown();
|
||||||
this.shuttingDown = true;
|
this.shuttingDown = true;
|
||||||
boolean graceful = false;
|
boolean graceful = false;
|
||||||
try {
|
try {
|
||||||
graceful = this.gracefulShutdownHandler.awaitShutdown(this.period.toMillis());
|
graceful = awaitShutdown(this.gracePeriod.toMillis());
|
||||||
}
|
}
|
||||||
catch (InterruptedException ex) {
|
catch (InterruptedException ex) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.web.embedded.undertow;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
|
||||||
|
import io.undertow.server.HttpHandler;
|
||||||
|
|
||||||
|
import org.springframework.boot.web.server.GracefulShutdown;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory used by {@link UndertowServletWebServer} to add {@link HttpHandler
|
||||||
|
* HttpHandlers}. Instances returned from this factory may optionally implement the
|
||||||
|
* following interfaces:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link Closeable} - if they wish to be closed just before server stops.</li>
|
||||||
|
* <li>{@link GracefulShutdown} - if they wish to manage graceful shutdown.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface HttpHandlerFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the {@link HttpHandler} instance that should be added.
|
||||||
|
* @param next the next handler in the chain
|
||||||
|
* @return the new HTTP handler instance
|
||||||
|
*/
|
||||||
|
HttpHandler getHandler(HttpHandler next);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -16,34 +16,16 @@
|
||||||
|
|
||||||
package org.springframework.boot.web.embedded.undertow;
|
package org.springframework.boot.web.embedded.undertow;
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import io.undertow.Handlers;
|
|
||||||
import io.undertow.Undertow;
|
import io.undertow.Undertow;
|
||||||
import io.undertow.UndertowOptions;
|
|
||||||
import io.undertow.server.HttpHandler;
|
|
||||||
import io.undertow.server.handlers.GracefulShutdownHandler;
|
|
||||||
import io.undertow.server.handlers.accesslog.AccessLogHandler;
|
|
||||||
import io.undertow.server.handlers.accesslog.DefaultAccessLogReceiver;
|
|
||||||
import org.xnio.OptionMap;
|
|
||||||
import org.xnio.Options;
|
|
||||||
import org.xnio.Xnio;
|
|
||||||
import org.xnio.XnioWorker;
|
|
||||||
|
|
||||||
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;
|
||||||
import org.springframework.boot.web.server.GracefulShutdown;
|
|
||||||
import org.springframework.boot.web.server.WebServer;
|
import org.springframework.boot.web.server.WebServer;
|
||||||
import org.springframework.http.server.reactive.UndertowHttpHandlerAdapter;
|
import org.springframework.http.server.reactive.UndertowHttpHandlerAdapter;
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link ReactiveWebServerFactory} that can be used to create {@link UndertowWebServer}s.
|
* {@link ReactiveWebServerFactory} that can be used to create {@link UndertowWebServer}s.
|
||||||
|
|
@ -54,29 +36,7 @@ import org.springframework.util.Assert;
|
||||||
public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerFactory
|
public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerFactory
|
||||||
implements ConfigurableUndertowWebServerFactory {
|
implements ConfigurableUndertowWebServerFactory {
|
||||||
|
|
||||||
private Set<UndertowBuilderCustomizer> builderCustomizers = new LinkedHashSet<>();
|
private UndertowWebServerFactoryDelegate delegate = new UndertowWebServerFactoryDelegate();
|
||||||
|
|
||||||
private Integer bufferSize;
|
|
||||||
|
|
||||||
private Integer ioThreads;
|
|
||||||
|
|
||||||
private Integer workerThreads;
|
|
||||||
|
|
||||||
private Boolean directBuffers;
|
|
||||||
|
|
||||||
private File accessLogDirectory;
|
|
||||||
|
|
||||||
private String accessLogPattern;
|
|
||||||
|
|
||||||
private String accessLogPrefix;
|
|
||||||
|
|
||||||
private String accessLogSuffix;
|
|
||||||
|
|
||||||
private boolean accessLogEnabled = false;
|
|
||||||
|
|
||||||
private boolean accessLogRotate = true;
|
|
||||||
|
|
||||||
private boolean useForwardHeaders;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link UndertowReactiveWebServerFactory} instance.
|
* Create a new {@link UndertowReactiveWebServerFactory} instance.
|
||||||
|
|
@ -94,187 +54,13 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WebServer getWebServer(org.springframework.http.server.reactive.HttpHandler httpHandler) {
|
|
||||||
Undertow.Builder builder = createBuilder(getPort());
|
|
||||||
HttpHandler handler = new UndertowHttpHandlerAdapter(httpHandler);
|
|
||||||
if (this.useForwardHeaders) {
|
|
||||||
handler = Handlers.proxyPeerAddress(handler);
|
|
||||||
}
|
|
||||||
handler = UndertowCompressionConfigurer.configureCompression(getCompression(), handler);
|
|
||||||
Closeable closeable = null;
|
|
||||||
if (isAccessLogEnabled()) {
|
|
||||||
AccessLogHandlerConfiguration accessLogHandlerConfiguration = configureAccessLogHandler(handler);
|
|
||||||
closeable = accessLogHandlerConfiguration.closeable;
|
|
||||||
handler = accessLogHandlerConfiguration.accessLogHandler;
|
|
||||||
}
|
|
||||||
GracefulShutdown gracefulShutdown = null;
|
|
||||||
GracefulShutdownHandler gracefulShutdownHandler = Handlers.gracefulShutdown(handler);
|
|
||||||
Duration gracePeriod = getShutdown().getGracePeriod();
|
|
||||||
if (gracePeriod != null) {
|
|
||||||
gracefulShutdown = new UndertowGracefulShutdown(gracefulShutdownHandler, gracePeriod);
|
|
||||||
handler = gracefulShutdownHandler;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
gracefulShutdown = GracefulShutdown.IMMEDIATE;
|
|
||||||
}
|
|
||||||
builder.setHandler(handler);
|
|
||||||
return new UndertowWebServer(builder, getPort() >= 0, closeable, gracefulShutdown);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Undertow.Builder createBuilder(int port) {
|
|
||||||
Undertow.Builder builder = Undertow.builder();
|
|
||||||
if (this.bufferSize != null) {
|
|
||||||
builder.setBufferSize(this.bufferSize);
|
|
||||||
}
|
|
||||||
if (this.ioThreads != null) {
|
|
||||||
builder.setIoThreads(this.ioThreads);
|
|
||||||
}
|
|
||||||
if (this.workerThreads != null) {
|
|
||||||
builder.setWorkerThreads(this.workerThreads);
|
|
||||||
}
|
|
||||||
if (this.directBuffers != null) {
|
|
||||||
builder.setDirectBuffers(this.directBuffers);
|
|
||||||
}
|
|
||||||
if (getSsl() != null && getSsl().isEnabled()) {
|
|
||||||
customizeSsl(builder);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
builder.addHttpListener(port, getListenAddress());
|
|
||||||
}
|
|
||||||
builder.setServerOption(UndertowOptions.SHUTDOWN_TIMEOUT, 0);
|
|
||||||
for (UndertowBuilderCustomizer customizer : this.builderCustomizers) {
|
|
||||||
customizer.customize(builder);
|
|
||||||
}
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AccessLogHandlerConfiguration configureAccessLogHandler(HttpHandler handler) {
|
|
||||||
try {
|
|
||||||
createAccessLogDirectoryIfNecessary();
|
|
||||||
XnioWorker worker = createWorker();
|
|
||||||
String prefix = (this.accessLogPrefix != null) ? this.accessLogPrefix : "access_log.";
|
|
||||||
DefaultAccessLogReceiver accessLogReceiver = new DefaultAccessLogReceiver(worker, this.accessLogDirectory,
|
|
||||||
prefix, this.accessLogSuffix, this.accessLogRotate);
|
|
||||||
String formatString = ((this.accessLogPattern != null) ? this.accessLogPattern : "common");
|
|
||||||
AccessLogHandler accessLogHandler = new AccessLogHandler(handler, accessLogReceiver, formatString,
|
|
||||||
Undertow.class.getClassLoader());
|
|
||||||
return new AccessLogHandlerConfiguration(accessLogHandler, () -> {
|
|
||||||
try {
|
|
||||||
accessLogReceiver.close();
|
|
||||||
worker.shutdown();
|
|
||||||
worker.awaitTermination(30, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new IllegalStateException(ex);
|
|
||||||
}
|
|
||||||
catch (InterruptedException ex) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new IllegalStateException("Failed to create AccessLogHandler", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createAccessLogDirectoryIfNecessary() {
|
|
||||||
Assert.state(this.accessLogDirectory != null, "Access log directory is not set");
|
|
||||||
if (!this.accessLogDirectory.isDirectory() && !this.accessLogDirectory.mkdirs()) {
|
|
||||||
throw new IllegalStateException("Failed to create access log directory '" + this.accessLogDirectory + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private XnioWorker createWorker() throws IOException {
|
|
||||||
Xnio xnio = Xnio.getInstance(Undertow.class.getClassLoader());
|
|
||||||
return xnio.createWorker(OptionMap.builder().set(Options.THREAD_DAEMON, true).getMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void customizeSsl(Undertow.Builder builder) {
|
|
||||||
new SslBuilderCustomizer(getPort(), getAddress(), getSsl(), getSslStoreProvider()).customize(builder);
|
|
||||||
if (getHttp2() != null) {
|
|
||||||
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, getHttp2().isEnabled());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getListenAddress() {
|
|
||||||
if (getAddress() == null) {
|
|
||||||
return "0.0.0.0";
|
|
||||||
}
|
|
||||||
return getAddress().getHostAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAccessLogDirectory(File accessLogDirectory) {
|
|
||||||
this.accessLogDirectory = accessLogDirectory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAccessLogPattern(String accessLogPattern) {
|
|
||||||
this.accessLogPattern = accessLogPattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAccessLogPrefix(String accessLogPrefix) {
|
|
||||||
this.accessLogPrefix = accessLogPrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAccessLogSuffix(String accessLogSuffix) {
|
|
||||||
this.accessLogSuffix = accessLogSuffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAccessLogEnabled() {
|
|
||||||
return this.accessLogEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAccessLogEnabled(boolean accessLogEnabled) {
|
|
||||||
this.accessLogEnabled = accessLogEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAccessLogRotate(boolean accessLogRotate) {
|
|
||||||
this.accessLogRotate = accessLogRotate;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final boolean isUseForwardHeaders() {
|
|
||||||
return this.useForwardHeaders;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUseForwardHeaders(boolean useForwardHeaders) {
|
|
||||||
this.useForwardHeaders = useForwardHeaders;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBufferSize(Integer bufferSize) {
|
|
||||||
this.bufferSize = bufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setIoThreads(Integer ioThreads) {
|
|
||||||
this.ioThreads = ioThreads;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setWorkerThreads(Integer workerThreads) {
|
|
||||||
this.workerThreads = workerThreads;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUseDirectBuffers(Boolean directBuffers) {
|
|
||||||
this.directBuffers = directBuffers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set {@link UndertowBuilderCustomizer}s that should be applied to the Undertow
|
|
||||||
* {@link io.undertow.Undertow.Builder Builder}. Calling this method will replace any
|
|
||||||
* existing customizers.
|
|
||||||
* @param customizers the customizers to set
|
|
||||||
*/
|
|
||||||
public void setBuilderCustomizers(Collection<? extends UndertowBuilderCustomizer> customizers) {
|
public void setBuilderCustomizers(Collection<? extends UndertowBuilderCustomizer> customizers) {
|
||||||
Assert.notNull(customizers, "Customizers must not be null");
|
this.delegate.setBuilderCustomizers(customizers);
|
||||||
this.builderCustomizers = new LinkedHashSet<>(customizers);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addBuilderCustomizers(UndertowBuilderCustomizer... customizers) {
|
||||||
|
this.delegate.addBuilderCustomizers(customizers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -283,31 +69,78 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF
|
||||||
* @return the customizers that will be applied
|
* @return the customizers that will be applied
|
||||||
*/
|
*/
|
||||||
public Collection<UndertowBuilderCustomizer> getBuilderCustomizers() {
|
public Collection<UndertowBuilderCustomizer> getBuilderCustomizers() {
|
||||||
return this.builderCustomizers;
|
return this.delegate.getBuilderCustomizers();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add {@link UndertowBuilderCustomizer}s that should be used to customize the
|
|
||||||
* Undertow {@link io.undertow.Undertow.Builder Builder}.
|
|
||||||
* @param customizers the customizers to add
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void addBuilderCustomizers(UndertowBuilderCustomizer... customizers) {
|
public void setBufferSize(Integer bufferSize) {
|
||||||
Assert.notNull(customizers, "Customizers must not be null");
|
this.delegate.setBufferSize(bufferSize);
|
||||||
this.builderCustomizers.addAll(Arrays.asList(customizers));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class AccessLogHandlerConfiguration {
|
@Override
|
||||||
|
public void setIoThreads(Integer ioThreads) {
|
||||||
private final AccessLogHandler accessLogHandler;
|
this.delegate.setIoThreads(ioThreads);
|
||||||
|
|
||||||
private final Closeable closeable;
|
|
||||||
|
|
||||||
private AccessLogHandlerConfiguration(AccessLogHandler accessLogHandler, Closeable closeable) {
|
|
||||||
this.accessLogHandler = accessLogHandler;
|
|
||||||
this.closeable = closeable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setWorkerThreads(Integer workerThreads) {
|
||||||
|
this.delegate.setWorkerThreads(workerThreads);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUseDirectBuffers(Boolean directBuffers) {
|
||||||
|
this.delegate.setUseDirectBuffers(directBuffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUseForwardHeaders(boolean useForwardHeaders) {
|
||||||
|
this.delegate.setUseForwardHeaders(useForwardHeaders);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final boolean isUseForwardHeaders() {
|
||||||
|
return this.delegate.isUseForwardHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAccessLogDirectory(File accessLogDirectory) {
|
||||||
|
this.delegate.setAccessLogDirectory(accessLogDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAccessLogPattern(String accessLogPattern) {
|
||||||
|
this.delegate.setAccessLogPattern(accessLogPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAccessLogPrefix(String accessLogPrefix) {
|
||||||
|
this.delegate.setAccessLogPrefix(accessLogPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAccessLogSuffix(String accessLogSuffix) {
|
||||||
|
this.delegate.setAccessLogSuffix(accessLogSuffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAccessLogEnabled() {
|
||||||
|
return this.delegate.isAccessLogEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAccessLogEnabled(boolean accessLogEnabled) {
|
||||||
|
this.delegate.setAccessLogEnabled(accessLogEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAccessLogRotate(boolean accessLogRotate) {
|
||||||
|
this.delegate.setAccessLogRotate(accessLogRotate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WebServer getWebServer(org.springframework.http.server.reactive.HttpHandler httpHandler) {
|
||||||
|
Undertow.Builder builder = this.delegate.createBuilder(this);
|
||||||
|
List<HttpHandlerFactory> httpHandlerFactories = this.delegate.createHttpHandlerFactories(this,
|
||||||
|
(next) -> new UndertowHttpHandlerAdapter(httpHandler));
|
||||||
|
return new UndertowWebServer(builder, httpHandlerFactories, getPort() >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,31 +16,13 @@
|
||||||
|
|
||||||
package org.springframework.boot.web.embedded.undertow;
|
package org.springframework.boot.web.embedded.undertow;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
|
|
||||||
import io.undertow.Handlers;
|
import io.undertow.Handlers;
|
||||||
import io.undertow.Undertow;
|
|
||||||
import io.undertow.Undertow.Builder;
|
import io.undertow.Undertow.Builder;
|
||||||
import io.undertow.server.HttpHandler;
|
import io.undertow.server.HttpHandler;
|
||||||
import io.undertow.server.handlers.GracefulShutdownHandler;
|
|
||||||
import io.undertow.servlet.api.DeploymentManager;
|
import io.undertow.servlet.api.DeploymentManager;
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.xnio.channels.BoundChannel;
|
|
||||||
|
|
||||||
import org.springframework.boot.web.server.Compression;
|
import org.springframework.boot.web.server.Compression;
|
||||||
import org.springframework.boot.web.server.GracefulShutdown;
|
|
||||||
import org.springframework.boot.web.server.PortInUseException;
|
|
||||||
import org.springframework.boot.web.server.WebServer;
|
import org.springframework.boot.web.server.WebServer;
|
||||||
import org.springframework.boot.web.server.WebServerException;
|
|
||||||
import org.springframework.util.ReflectionUtils;
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -56,33 +38,11 @@ import org.springframework.util.StringUtils;
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
* @see UndertowServletWebServerFactory
|
* @see UndertowServletWebServerFactory
|
||||||
*/
|
*/
|
||||||
public class UndertowServletWebServer implements WebServer {
|
public class UndertowServletWebServer extends UndertowWebServer {
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(UndertowServletWebServer.class);
|
|
||||||
|
|
||||||
private final Object monitor = new Object();
|
|
||||||
|
|
||||||
private final Builder builder;
|
|
||||||
|
|
||||||
private final DeploymentManager manager;
|
|
||||||
|
|
||||||
private final String contextPath;
|
private final String contextPath;
|
||||||
|
|
||||||
private final boolean useForwardHeaders;
|
private final DeploymentManager manager;
|
||||||
|
|
||||||
private final boolean autoStart;
|
|
||||||
|
|
||||||
private final Compression compression;
|
|
||||||
|
|
||||||
private final String serverHeader;
|
|
||||||
|
|
||||||
private final Duration shutdownGracePeriod;
|
|
||||||
|
|
||||||
private Undertow undertow;
|
|
||||||
|
|
||||||
private volatile boolean started = false;
|
|
||||||
|
|
||||||
private volatile GracefulShutdown gracefulShutdown;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link UndertowServletWebServer} instance.
|
* Create a new {@link UndertowServletWebServer} instance.
|
||||||
|
|
@ -91,7 +51,10 @@ public class UndertowServletWebServer implements WebServer {
|
||||||
* @param contextPath the root context path
|
* @param contextPath the root context path
|
||||||
* @param autoStart if the server should be started
|
* @param autoStart if the server should be started
|
||||||
* @param compression compression configuration
|
* @param compression compression configuration
|
||||||
|
* @deprecated since 2.3.0 in favor of
|
||||||
|
* {@link #UndertowServletWebServer(io.undertow.Undertow.Builder, Iterable, String, boolean)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public UndertowServletWebServer(Builder builder, DeploymentManager manager, String contextPath, boolean autoStart,
|
public UndertowServletWebServer(Builder builder, DeploymentManager manager, String contextPath, boolean autoStart,
|
||||||
Compression compression) {
|
Compression compression) {
|
||||||
this(builder, manager, contextPath, false, autoStart, compression);
|
this(builder, manager, contextPath, false, autoStart, compression);
|
||||||
|
|
@ -105,7 +68,10 @@ public class UndertowServletWebServer implements WebServer {
|
||||||
* @param useForwardHeaders if x-forward headers should be used
|
* @param useForwardHeaders if x-forward headers should be used
|
||||||
* @param autoStart if the server should be started
|
* @param autoStart if the server should be started
|
||||||
* @param compression compression configuration
|
* @param compression compression configuration
|
||||||
|
* @deprecated since 2.3.0 in favor of
|
||||||
|
* {@link #UndertowServletWebServer(io.undertow.Undertow.Builder, Iterable, String, boolean)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public UndertowServletWebServer(Builder builder, DeploymentManager manager, String contextPath,
|
public UndertowServletWebServer(Builder builder, DeploymentManager manager, String contextPath,
|
||||||
boolean useForwardHeaders, boolean autoStart, Compression compression) {
|
boolean useForwardHeaders, boolean autoStart, Compression compression) {
|
||||||
this(builder, manager, contextPath, useForwardHeaders, autoStart, compression, null);
|
this(builder, manager, contextPath, useForwardHeaders, autoStart, compression, null);
|
||||||
|
|
@ -120,274 +86,60 @@ public class UndertowServletWebServer implements WebServer {
|
||||||
* @param autoStart if the server should be started
|
* @param autoStart if the server should be started
|
||||||
* @param compression compression configuration
|
* @param compression compression configuration
|
||||||
* @param serverHeader string to be used in HTTP header
|
* @param serverHeader string to be used in HTTP header
|
||||||
|
* @deprecated since 2.3.0 in favor of
|
||||||
|
* {@link #UndertowServletWebServer(io.undertow.Undertow.Builder, Iterable, String, boolean)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public UndertowServletWebServer(Builder builder, DeploymentManager manager, String contextPath,
|
public UndertowServletWebServer(Builder builder, DeploymentManager manager, String contextPath,
|
||||||
boolean useForwardHeaders, boolean autoStart, Compression compression, String serverHeader) {
|
boolean useForwardHeaders, boolean autoStart, Compression compression, String serverHeader) {
|
||||||
this(builder, manager, contextPath, useForwardHeaders, autoStart, compression, serverHeader, null);
|
this(builder, UndertowWebServerFactoryDelegate.createHttpHandlerFactories(compression, useForwardHeaders,
|
||||||
|
serverHeader, null, new DeploymentManagerHttpHandlerFactory(manager)), contextPath, autoStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link UndertowServletWebServer} instance.
|
* Create a new {@link UndertowServletWebServer} instance.
|
||||||
* @param builder the builder
|
* @param builder the builder
|
||||||
* @param manager the deployment manager
|
* @param httpHandlerFactories the handler factories
|
||||||
* @param contextPath the root context path
|
* @param contextPath the root context path
|
||||||
* @param useForwardHeaders if x-forward headers should be used
|
|
||||||
* @param autoStart if the server should be started
|
* @param autoStart if the server should be started
|
||||||
* @param compression compression configuration
|
|
||||||
* @param serverHeader string to be used in HTTP header
|
|
||||||
* @param shutdownGracePeriod the period to wait for activity to cease when shutting
|
|
||||||
* down the server gracefully
|
|
||||||
* @since 2.3.0
|
* @since 2.3.0
|
||||||
*/
|
*/
|
||||||
public UndertowServletWebServer(Builder builder, DeploymentManager manager, String contextPath,
|
public UndertowServletWebServer(Builder builder, Iterable<HttpHandlerFactory> httpHandlerFactories,
|
||||||
boolean useForwardHeaders, boolean autoStart, Compression compression, String serverHeader,
|
String contextPath, boolean autoStart) {
|
||||||
Duration shutdownGracePeriod) {
|
super(builder, httpHandlerFactories, autoStart);
|
||||||
this.builder = builder;
|
|
||||||
this.manager = manager;
|
|
||||||
this.contextPath = contextPath;
|
this.contextPath = contextPath;
|
||||||
this.useForwardHeaders = useForwardHeaders;
|
this.manager = findManager(httpHandlerFactories);
|
||||||
this.autoStart = autoStart;
|
|
||||||
this.compression = compression;
|
|
||||||
this.serverHeader = serverHeader;
|
|
||||||
this.shutdownGracePeriod = shutdownGracePeriod;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private DeploymentManager findManager(Iterable<HttpHandlerFactory> httpHandlerFactories) {
|
||||||
public void start() throws WebServerException {
|
for (HttpHandlerFactory httpHandlerFactory : httpHandlerFactories) {
|
||||||
synchronized (this.monitor) {
|
if (httpHandlerFactory instanceof DeploymentManagerHttpHandlerFactory) {
|
||||||
if (this.started) {
|
return ((DeploymentManagerHttpHandlerFactory) httpHandlerFactory).getDeploymentManager();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
if (!this.autoStart) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.undertow == null) {
|
|
||||||
this.undertow = createUndertowServer();
|
|
||||||
}
|
|
||||||
this.undertow.start();
|
|
||||||
this.started = true;
|
|
||||||
UndertowServletWebServer.logger.info("Undertow started on port(s) " + getPortsDescription()
|
|
||||||
+ " with context path '" + this.contextPath + "'");
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
try {
|
|
||||||
PortInUseException.ifPortBindingException(ex, (bindException) -> {
|
|
||||||
List<Port> failedPorts = getConfiguredPorts();
|
|
||||||
failedPorts.removeAll(getActualPorts());
|
|
||||||
if (failedPorts.size() == 1) {
|
|
||||||
throw new PortInUseException(failedPorts.get(0).getNumber());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
throw new WebServerException("Unable to start embedded Undertow", ex);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
stopSilently();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DeploymentManager getDeploymentManager() {
|
|
||||||
synchronized (this.monitor) {
|
|
||||||
return this.manager;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stopSilently() {
|
|
||||||
try {
|
|
||||||
if (this.undertow != null) {
|
|
||||||
this.undertow.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Undertow createUndertowServer() throws ServletException {
|
|
||||||
HttpHandler httpHandler = this.manager.start();
|
|
||||||
httpHandler = getContextHandler(httpHandler);
|
|
||||||
if (this.useForwardHeaders) {
|
|
||||||
httpHandler = Handlers.proxyPeerAddress(httpHandler);
|
|
||||||
}
|
|
||||||
if (StringUtils.hasText(this.serverHeader)) {
|
|
||||||
httpHandler = Handlers.header(httpHandler, "Server", this.serverHeader);
|
|
||||||
}
|
|
||||||
if (this.shutdownGracePeriod != null) {
|
|
||||||
GracefulShutdownHandler gracefulShutdownHandler = Handlers.gracefulShutdown(httpHandler);
|
|
||||||
this.gracefulShutdown = new UndertowGracefulShutdown(gracefulShutdownHandler, this.shutdownGracePeriod);
|
|
||||||
httpHandler = gracefulShutdownHandler;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.gracefulShutdown = GracefulShutdown.IMMEDIATE;
|
|
||||||
}
|
|
||||||
this.builder.setHandler(httpHandler);
|
|
||||||
return this.builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpHandler getContextHandler(HttpHandler httpHandler) {
|
|
||||||
HttpHandler contextHandler = UndertowCompressionConfigurer.configureCompression(this.compression, httpHandler);
|
|
||||||
if (StringUtils.isEmpty(this.contextPath)) {
|
|
||||||
return contextHandler;
|
|
||||||
}
|
|
||||||
return Handlers.path().addPrefixPath(this.contextPath, contextHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getPortsDescription() {
|
|
||||||
List<Port> ports = getActualPorts();
|
|
||||||
if (!ports.isEmpty()) {
|
|
||||||
return StringUtils.collectionToDelimitedString(ports, " ");
|
|
||||||
}
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Port> getActualPorts() {
|
|
||||||
List<Port> ports = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
if (!this.autoStart) {
|
|
||||||
ports.add(new Port(-1, "unknown"));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (BoundChannel channel : extractChannels()) {
|
|
||||||
ports.add(getPortFromChannel(channel));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
// Continue
|
|
||||||
}
|
|
||||||
return ports;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private List<BoundChannel> extractChannels() {
|
|
||||||
Field channelsField = ReflectionUtils.findField(Undertow.class, "channels");
|
|
||||||
ReflectionUtils.makeAccessible(channelsField);
|
|
||||||
return (List<BoundChannel>) ReflectionUtils.getField(channelsField, this.undertow);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Port getPortFromChannel(BoundChannel channel) {
|
|
||||||
SocketAddress socketAddress = channel.getLocalAddress();
|
|
||||||
if (socketAddress instanceof InetSocketAddress) {
|
|
||||||
String protocol = (ReflectionUtils.findField(channel.getClass(), "ssl") != null) ? "https" : "http";
|
|
||||||
return new Port(((InetSocketAddress) socketAddress).getPort(), protocol);
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Port> getConfiguredPorts() {
|
@Override
|
||||||
List<Port> ports = new ArrayList<>();
|
protected HttpHandler createHttpHandler() {
|
||||||
for (Object listener : extractListeners()) {
|
HttpHandler handler = super.createHttpHandler();
|
||||||
try {
|
if (!StringUtils.isEmpty(this.contextPath)) {
|
||||||
Port port = getPortFromListener(listener);
|
handler = Handlers.path().addPrefixPath(this.contextPath, handler);
|
||||||
if (port.getNumber() != 0) {
|
|
||||||
ports.add(port);
|
|
||||||
}
|
}
|
||||||
}
|
return handler;
|
||||||
catch (Exception ex) {
|
|
||||||
// Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ports;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private List<Object> extractListeners() {
|
|
||||||
Field listenersField = ReflectionUtils.findField(Undertow.class, "listeners");
|
|
||||||
ReflectionUtils.makeAccessible(listenersField);
|
|
||||||
return (List<Object>) ReflectionUtils.getField(listenersField, this.undertow);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Port getPortFromListener(Object listener) {
|
|
||||||
Field typeField = ReflectionUtils.findField(listener.getClass(), "type");
|
|
||||||
ReflectionUtils.makeAccessible(typeField);
|
|
||||||
String protocol = ReflectionUtils.getField(typeField, listener).toString();
|
|
||||||
Field portField = ReflectionUtils.findField(listener.getClass(), "port");
|
|
||||||
ReflectionUtils.makeAccessible(portField);
|
|
||||||
int port = (Integer) ReflectionUtils.getField(portField, listener);
|
|
||||||
return new Port(port, protocol);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() throws WebServerException {
|
protected String getStartLogMessage() {
|
||||||
synchronized (this.monitor) {
|
String message = super.getStartLogMessage();
|
||||||
if (!this.started) {
|
if (StringUtils.hasText(this.contextPath)) {
|
||||||
return;
|
message += " with context path '" + this.contextPath + "'";
|
||||||
}
|
|
||||||
this.started = false;
|
|
||||||
try {
|
|
||||||
this.manager.stop();
|
|
||||||
this.manager.undeploy();
|
|
||||||
this.undertow.stop();
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
throw new WebServerException("Unable to stop undertow", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public DeploymentManager getDeploymentManager() {
|
||||||
public int getPort() {
|
return this.manager;
|
||||||
List<Port> ports = getActualPorts();
|
|
||||||
if (ports.isEmpty()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return ports.get(0).getNumber();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shutDownGracefully() {
|
|
||||||
return this.gracefulShutdown.shutDownGracefully();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean inGracefulShutdown() {
|
|
||||||
return this.gracefulShutdown.isShuttingDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An active Undertow port.
|
|
||||||
*/
|
|
||||||
private static final class Port {
|
|
||||||
|
|
||||||
private final int number;
|
|
||||||
|
|
||||||
private final String protocol;
|
|
||||||
|
|
||||||
private Port(int number, String protocol) {
|
|
||||||
this.number = number;
|
|
||||||
this.protocol = protocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getNumber() {
|
|
||||||
return this.number;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (obj == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (getClass() != obj.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Port other = (Port) obj;
|
|
||||||
return this.number == other.number;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return this.number;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return this.number + " (" + this.protocol + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,29 +25,19 @@ 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.Collections;
|
||||||
import java.util.EventListener;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.servlet.ServletContainerInitializer;
|
import javax.servlet.ServletContainerInitializer;
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
import javax.servlet.ServletContextEvent;
|
|
||||||
import javax.servlet.ServletContextListener;
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
|
||||||
import io.undertow.Undertow;
|
|
||||||
import io.undertow.Undertow.Builder;
|
import io.undertow.Undertow.Builder;
|
||||||
import io.undertow.UndertowOptions;
|
|
||||||
import io.undertow.server.HttpHandler;
|
|
||||||
import io.undertow.server.handlers.accesslog.AccessLogHandler;
|
|
||||||
import io.undertow.server.handlers.accesslog.AccessLogReceiver;
|
|
||||||
import io.undertow.server.handlers.accesslog.DefaultAccessLogReceiver;
|
|
||||||
import io.undertow.server.handlers.resource.FileResourceManager;
|
import io.undertow.server.handlers.resource.FileResourceManager;
|
||||||
import io.undertow.server.handlers.resource.Resource;
|
import io.undertow.server.handlers.resource.Resource;
|
||||||
import io.undertow.server.handlers.resource.ResourceChangeListener;
|
import io.undertow.server.handlers.resource.ResourceChangeListener;
|
||||||
|
|
@ -57,17 +47,12 @@ import io.undertow.server.session.SessionManager;
|
||||||
import io.undertow.servlet.Servlets;
|
import io.undertow.servlet.Servlets;
|
||||||
import io.undertow.servlet.api.DeploymentInfo;
|
import io.undertow.servlet.api.DeploymentInfo;
|
||||||
import io.undertow.servlet.api.DeploymentManager;
|
import io.undertow.servlet.api.DeploymentManager;
|
||||||
import io.undertow.servlet.api.ListenerInfo;
|
|
||||||
import io.undertow.servlet.api.MimeMapping;
|
import io.undertow.servlet.api.MimeMapping;
|
||||||
import io.undertow.servlet.api.ServletContainerInitializerInfo;
|
import io.undertow.servlet.api.ServletContainerInitializerInfo;
|
||||||
import io.undertow.servlet.api.ServletStackTraces;
|
import io.undertow.servlet.api.ServletStackTraces;
|
||||||
import io.undertow.servlet.core.DeploymentImpl;
|
import io.undertow.servlet.core.DeploymentImpl;
|
||||||
import io.undertow.servlet.handlers.DefaultServlet;
|
import io.undertow.servlet.handlers.DefaultServlet;
|
||||||
import io.undertow.servlet.util.ImmediateInstanceFactory;
|
import io.undertow.servlet.util.ImmediateInstanceFactory;
|
||||||
import org.xnio.OptionMap;
|
|
||||||
import org.xnio.Options;
|
|
||||||
import org.xnio.Xnio;
|
|
||||||
import org.xnio.XnioWorker;
|
|
||||||
|
|
||||||
import org.springframework.boot.web.server.ErrorPage;
|
import org.springframework.boot.web.server.ErrorPage;
|
||||||
import org.springframework.boot.web.server.MimeMappings.Mapping;
|
import org.springframework.boot.web.server.MimeMappings.Mapping;
|
||||||
|
|
@ -100,34 +85,12 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
|
||||||
|
|
||||||
private static final Set<Class<?>> NO_CLASSES = Collections.emptySet();
|
private static final Set<Class<?>> NO_CLASSES = Collections.emptySet();
|
||||||
|
|
||||||
private Set<UndertowBuilderCustomizer> builderCustomizers = new LinkedHashSet<>();
|
private UndertowWebServerFactoryDelegate delegate = new UndertowWebServerFactoryDelegate();
|
||||||
|
|
||||||
private Set<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers = new LinkedHashSet<>();
|
private Set<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers = new LinkedHashSet<>();
|
||||||
|
|
||||||
private ResourceLoader resourceLoader;
|
private ResourceLoader resourceLoader;
|
||||||
|
|
||||||
private Integer bufferSize;
|
|
||||||
|
|
||||||
private Integer ioThreads;
|
|
||||||
|
|
||||||
private Integer workerThreads;
|
|
||||||
|
|
||||||
private Boolean directBuffers;
|
|
||||||
|
|
||||||
private File accessLogDirectory;
|
|
||||||
|
|
||||||
private String accessLogPattern;
|
|
||||||
|
|
||||||
private String accessLogPrefix;
|
|
||||||
|
|
||||||
private String accessLogSuffix;
|
|
||||||
|
|
||||||
private boolean accessLogEnabled = false;
|
|
||||||
|
|
||||||
private boolean accessLogRotate = true;
|
|
||||||
|
|
||||||
private boolean useForwardHeaders;
|
|
||||||
|
|
||||||
private boolean eagerInitFilters = true;
|
private boolean eagerInitFilters = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -158,14 +121,14 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
|
||||||
getJsp().setRegistered(false);
|
getJsp().setRegistered(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Set {@link UndertowBuilderCustomizer}s that should be applied to the Undertow
|
|
||||||
* {@link Builder}. Calling this method will replace any existing customizers.
|
|
||||||
* @param customizers the customizers to set
|
|
||||||
*/
|
|
||||||
public void setBuilderCustomizers(Collection<? extends UndertowBuilderCustomizer> customizers) {
|
public void setBuilderCustomizers(Collection<? extends UndertowBuilderCustomizer> customizers) {
|
||||||
Assert.notNull(customizers, "Customizers must not be null");
|
this.delegate.setBuilderCustomizers(customizers);
|
||||||
this.builderCustomizers = new LinkedHashSet<>(customizers);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addBuilderCustomizers(UndertowBuilderCustomizer... customizers) {
|
||||||
|
this.delegate.addBuilderCustomizers(customizers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -174,13 +137,74 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
|
||||||
* @return the customizers that will be applied
|
* @return the customizers that will be applied
|
||||||
*/
|
*/
|
||||||
public Collection<UndertowBuilderCustomizer> getBuilderCustomizers() {
|
public Collection<UndertowBuilderCustomizer> getBuilderCustomizers() {
|
||||||
return this.builderCustomizers;
|
return this.delegate.getBuilderCustomizers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addBuilderCustomizers(UndertowBuilderCustomizer... customizers) {
|
public void setBufferSize(Integer bufferSize) {
|
||||||
Assert.notNull(customizers, "Customizers must not be null");
|
this.delegate.setBufferSize(bufferSize);
|
||||||
this.builderCustomizers.addAll(Arrays.asList(customizers));
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setIoThreads(Integer ioThreads) {
|
||||||
|
this.delegate.setIoThreads(ioThreads);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setWorkerThreads(Integer workerThreads) {
|
||||||
|
this.delegate.setWorkerThreads(workerThreads);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUseDirectBuffers(Boolean directBuffers) {
|
||||||
|
this.delegate.setUseDirectBuffers(directBuffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAccessLogDirectory(File accessLogDirectory) {
|
||||||
|
this.delegate.setAccessLogDirectory(accessLogDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAccessLogPattern(String accessLogPattern) {
|
||||||
|
this.delegate.setAccessLogPattern(accessLogPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAccessLogPrefix(String accessLogPrefix) {
|
||||||
|
this.delegate.setAccessLogPrefix(accessLogPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessLogPrefix() {
|
||||||
|
return this.delegate.getAccessLogPrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAccessLogSuffix(String accessLogSuffix) {
|
||||||
|
this.delegate.setAccessLogSuffix(accessLogSuffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAccessLogEnabled(boolean accessLogEnabled) {
|
||||||
|
this.delegate.setAccessLogEnabled(accessLogEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAccessLogEnabled() {
|
||||||
|
return this.delegate.isAccessLogEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAccessLogRotate(boolean accessLogRotate) {
|
||||||
|
this.delegate.setAccessLogRotate(accessLogRotate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUseForwardHeaders(boolean useForwardHeaders) {
|
||||||
|
this.delegate.setUseForwardHeaders(useForwardHeaders);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final boolean isUseForwardHeaders() {
|
||||||
|
return this.delegate.isUseForwardHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -194,15 +218,6 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
|
||||||
this.deploymentInfoCustomizers = new LinkedHashSet<>(customizers);
|
this.deploymentInfoCustomizers = new LinkedHashSet<>(customizers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a mutable collection of the {@link UndertowDeploymentInfoCustomizer}s that
|
|
||||||
* will be applied to the Undertow {@link DeploymentInfo}.
|
|
||||||
* @return the customizers that will be applied
|
|
||||||
*/
|
|
||||||
public Collection<UndertowDeploymentInfoCustomizer> getDeploymentInfoCustomizers() {
|
|
||||||
return this.deploymentInfoCustomizers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add {@link UndertowDeploymentInfoCustomizer}s that should be used to customize the
|
* Add {@link UndertowDeploymentInfoCustomizer}s that should be used to customize the
|
||||||
* Undertow {@link DeploymentInfo}.
|
* Undertow {@link DeploymentInfo}.
|
||||||
|
|
@ -213,56 +228,47 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
|
||||||
this.deploymentInfoCustomizers.addAll(Arrays.asList(customizers));
|
this.deploymentInfoCustomizers.addAll(Arrays.asList(customizers));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a mutable collection of the {@link UndertowDeploymentInfoCustomizer}s that
|
||||||
|
* will be applied to the Undertow {@link DeploymentInfo}.
|
||||||
|
* @return the customizers that will be applied
|
||||||
|
*/
|
||||||
|
public Collection<UndertowDeploymentInfoCustomizer> getDeploymentInfoCustomizers() {
|
||||||
|
return this.deploymentInfoCustomizers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||||
|
this.resourceLoader = resourceLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if filters should be initialized eagerly.
|
||||||
|
* @return {@code true} if filters are initialized eagerly, otherwise {@code false}.
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public boolean isEagerInitFilters() {
|
||||||
|
return this.eagerInitFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether filters should be initialized eagerly.
|
||||||
|
* @param eagerInitFilters {@code true} if filters are initialized eagerly, otherwise
|
||||||
|
* {@code false}.
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public void setEagerInitFilters(boolean eagerInitFilters) {
|
||||||
|
this.eagerInitFilters = eagerInitFilters;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WebServer getWebServer(ServletContextInitializer... initializers) {
|
public WebServer getWebServer(ServletContextInitializer... initializers) {
|
||||||
DeploymentManager manager = createDeploymentManager(initializers);
|
Builder builder = this.delegate.createBuilder(this);
|
||||||
int port = getPort();
|
DeploymentManager manager = createManager(initializers);
|
||||||
Builder builder = createBuilder(port);
|
return getUndertowWebServer(builder, manager, getPort());
|
||||||
return getUndertowWebServer(builder, manager, port);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Builder createBuilder(int port) {
|
private DeploymentManager createManager(ServletContextInitializer... initializers) {
|
||||||
Builder builder = Undertow.builder();
|
|
||||||
if (this.bufferSize != null) {
|
|
||||||
builder.setBufferSize(this.bufferSize);
|
|
||||||
}
|
|
||||||
if (this.ioThreads != null) {
|
|
||||||
builder.setIoThreads(this.ioThreads);
|
|
||||||
}
|
|
||||||
if (this.workerThreads != null) {
|
|
||||||
builder.setWorkerThreads(this.workerThreads);
|
|
||||||
}
|
|
||||||
if (this.directBuffers != null) {
|
|
||||||
builder.setDirectBuffers(this.directBuffers);
|
|
||||||
}
|
|
||||||
if (getSsl() != null && getSsl().isEnabled()) {
|
|
||||||
customizeSsl(builder);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
builder.addHttpListener(port, getListenAddress());
|
|
||||||
}
|
|
||||||
builder.setServerOption(UndertowOptions.SHUTDOWN_TIMEOUT, 0);
|
|
||||||
for (UndertowBuilderCustomizer customizer : this.builderCustomizers) {
|
|
||||||
customizer.customize(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().isEnabled());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getListenAddress() {
|
|
||||||
if (getAddress() == null) {
|
|
||||||
return "0.0.0.0";
|
|
||||||
}
|
|
||||||
return getAddress().getHostAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
private DeploymentManager createDeploymentManager(ServletContextInitializer... initializers) {
|
|
||||||
DeploymentInfo deployment = Servlets.deployment();
|
DeploymentInfo deployment = Servlets.deployment();
|
||||||
registerServletContainerInitializerToDriveServletContextInitializers(deployment, initializers);
|
registerServletContainerInitializerToDriveServletContextInitializers(deployment, initializers);
|
||||||
deployment.setClassLoader(getServletClassLoader());
|
deployment.setClassLoader(getServletClassLoader());
|
||||||
|
|
@ -281,9 +287,6 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
|
||||||
for (UndertowDeploymentInfoCustomizer customizer : this.deploymentInfoCustomizers) {
|
for (UndertowDeploymentInfoCustomizer customizer : this.deploymentInfoCustomizers) {
|
||||||
customizer.customize(deployment);
|
customizer.customize(deployment);
|
||||||
}
|
}
|
||||||
if (isAccessLogEnabled()) {
|
|
||||||
configureAccessLog(deployment);
|
|
||||||
}
|
|
||||||
if (getSession().isPersistent()) {
|
if (getSession().isPersistent()) {
|
||||||
File dir = getValidSessionStoreDir();
|
File dir = getValidSessionStoreDir();
|
||||||
deployment.setSessionPersistenceManager(new FileSessionPersistence(dir));
|
deployment.setSessionPersistenceManager(new FileSessionPersistence(dir));
|
||||||
|
|
@ -305,42 +308,6 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
|
||||||
return timeoutDuration == null || timeoutDuration.isZero() || timeoutDuration.isNegative();
|
return timeoutDuration == null || timeoutDuration.isZero() || timeoutDuration.isNegative();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void configureAccessLog(DeploymentInfo deploymentInfo) {
|
|
||||||
try {
|
|
||||||
createAccessLogDirectoryIfNecessary();
|
|
||||||
XnioWorker worker = createWorker();
|
|
||||||
String prefix = (this.accessLogPrefix != null) ? this.accessLogPrefix : "access_log.";
|
|
||||||
DefaultAccessLogReceiver accessLogReceiver = new DefaultAccessLogReceiver(worker, this.accessLogDirectory,
|
|
||||||
prefix, this.accessLogSuffix, this.accessLogRotate);
|
|
||||||
EventListener listener = new AccessLogShutdownListener(worker, accessLogReceiver);
|
|
||||||
deploymentInfo.addListener(
|
|
||||||
new ListenerInfo(AccessLogShutdownListener.class, new ImmediateInstanceFactory<>(listener)));
|
|
||||||
deploymentInfo
|
|
||||||
.addInitialHandlerChainWrapper((handler) -> createAccessLogHandler(handler, accessLogReceiver));
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new IllegalStateException("Failed to create AccessLogHandler", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private AccessLogHandler createAccessLogHandler(HttpHandler handler, AccessLogReceiver accessLogReceiver) {
|
|
||||||
createAccessLogDirectoryIfNecessary();
|
|
||||||
String formatString = (this.accessLogPattern != null) ? this.accessLogPattern : "common";
|
|
||||||
return new AccessLogHandler(handler, accessLogReceiver, formatString, Undertow.class.getClassLoader());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createAccessLogDirectoryIfNecessary() {
|
|
||||||
Assert.state(this.accessLogDirectory != null, "Access log directory is not set");
|
|
||||||
if (!this.accessLogDirectory.isDirectory() && !this.accessLogDirectory.mkdirs()) {
|
|
||||||
throw new IllegalStateException("Failed to create access log directory '" + this.accessLogDirectory + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private XnioWorker createWorker() throws IOException {
|
|
||||||
Xnio xnio = Xnio.getInstance(Undertow.class.getClassLoader());
|
|
||||||
return xnio.createWorker(OptionMap.builder().set(Options.THREAD_DAEMON, true).getMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addLocaleMappings(DeploymentInfo deployment) {
|
private void addLocaleMappings(DeploymentInfo deployment) {
|
||||||
getLocaleCharsetMappings().forEach(
|
getLocaleCharsetMappings().forEach(
|
||||||
(locale, charset) -> deployment.addLocaleCharsetMapping(locale.toString(), charset.toString()));
|
(locale, charset) -> deployment.addLocaleCharsetMapping(locale.toString(), charset.toString()));
|
||||||
|
|
@ -449,99 +416,30 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
|
||||||
* @return a new {@link UndertowServletWebServer} instance
|
* @return a new {@link UndertowServletWebServer} instance
|
||||||
*/
|
*/
|
||||||
protected UndertowServletWebServer getUndertowWebServer(Builder builder, DeploymentManager manager, int port) {
|
protected UndertowServletWebServer getUndertowWebServer(Builder builder, DeploymentManager manager, int port) {
|
||||||
return new UndertowServletWebServer(builder, manager, getContextPath(), isUseForwardHeaders(), port >= 0,
|
List<HttpHandlerFactory> httpHandlerFactories = this.delegate.createHttpHandlerFactories(this,
|
||||||
getCompression(), getServerHeader(), getShutdown().getGracePeriod());
|
new DeploymentManagerHttpHandlerFactory(manager));
|
||||||
}
|
return new UndertowServletWebServer(builder, httpHandlerFactories, getContextPath(), port >= 0);
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
|
||||||
this.resourceLoader = resourceLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBufferSize(Integer bufferSize) {
|
|
||||||
this.bufferSize = bufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setIoThreads(Integer ioThreads) {
|
|
||||||
this.ioThreads = ioThreads;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setWorkerThreads(Integer workerThreads) {
|
|
||||||
this.workerThreads = workerThreads;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUseDirectBuffers(Boolean directBuffers) {
|
|
||||||
this.directBuffers = directBuffers;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAccessLogDirectory(File accessLogDirectory) {
|
|
||||||
this.accessLogDirectory = accessLogDirectory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAccessLogPattern(String accessLogPattern) {
|
|
||||||
this.accessLogPattern = accessLogPattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAccessLogPrefix() {
|
|
||||||
return this.accessLogPrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAccessLogPrefix(String accessLogPrefix) {
|
|
||||||
this.accessLogPrefix = accessLogPrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAccessLogSuffix(String accessLogSuffix) {
|
|
||||||
this.accessLogSuffix = accessLogSuffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAccessLogEnabled(boolean accessLogEnabled) {
|
|
||||||
this.accessLogEnabled = accessLogEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAccessLogEnabled() {
|
|
||||||
return this.accessLogEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAccessLogRotate(boolean accessLogRotate) {
|
|
||||||
this.accessLogRotate = accessLogRotate;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final boolean isUseForwardHeaders() {
|
|
||||||
return this.useForwardHeaders;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUseForwardHeaders(boolean useForwardHeaders) {
|
|
||||||
this.useForwardHeaders = useForwardHeaders;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return if filters should be initialized eagerly.
|
* {@link ServletContainerInitializer} to initialize {@link ServletContextInitializer
|
||||||
* @return {@code true} if filters are initialized eagerly, otherwise {@code false}.
|
* ServletContextInitializers}.
|
||||||
* @since 2.0.0
|
|
||||||
*/
|
*/
|
||||||
public boolean isEagerInitFilters() {
|
private static class Initializer implements ServletContainerInitializer {
|
||||||
return this.eagerInitFilters;
|
|
||||||
|
private final ServletContextInitializer[] initializers;
|
||||||
|
|
||||||
|
Initializer(ServletContextInitializer[] initializers) {
|
||||||
|
this.initializers = initializers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
|
||||||
|
for (ServletContextInitializer initializer : this.initializers) {
|
||||||
|
initializer.onStartup(servletContext);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set whether filters should be initialized eagerly.
|
|
||||||
* @param eagerInitFilters {@code true} if filters are initialized eagerly, otherwise
|
|
||||||
* {@code false}.
|
|
||||||
* @since 2.0.0
|
|
||||||
*/
|
|
||||||
public void setEagerInitFilters(boolean eagerInitFilters) {
|
|
||||||
this.eagerInitFilters = eagerInitFilters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -603,26 +501,8 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link ServletContainerInitializer} to initialize {@link ServletContextInitializer
|
* {@link ResourceManager} to hide Spring Boot loader classes.
|
||||||
* ServletContextInitializers}.
|
|
||||||
*/
|
*/
|
||||||
private static class Initializer implements ServletContainerInitializer {
|
|
||||||
|
|
||||||
private final ServletContextInitializer[] initializers;
|
|
||||||
|
|
||||||
Initializer(ServletContextInitializer[] initializers) {
|
|
||||||
this.initializers = initializers;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
|
|
||||||
for (ServletContextInitializer initializer : this.initializers) {
|
|
||||||
initializer.onStartup(servletContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class LoaderHidingResourceManager implements ResourceManager {
|
private static final class LoaderHidingResourceManager implements ResourceManager {
|
||||||
|
|
||||||
private final ResourceManager delegate;
|
private final ResourceManager delegate;
|
||||||
|
|
@ -661,36 +541,4 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class AccessLogShutdownListener implements ServletContextListener {
|
|
||||||
|
|
||||||
private final XnioWorker worker;
|
|
||||||
|
|
||||||
private final DefaultAccessLogReceiver accessLogReceiver;
|
|
||||||
|
|
||||||
AccessLogShutdownListener(XnioWorker worker, DefaultAccessLogReceiver accessLogReceiver) {
|
|
||||||
this.worker = worker;
|
|
||||||
this.accessLogReceiver = accessLogReceiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void contextInitialized(ServletContextEvent sce) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void contextDestroyed(ServletContextEvent sce) {
|
|
||||||
try {
|
|
||||||
this.accessLogReceiver.close();
|
|
||||||
this.worker.shutdown();
|
|
||||||
this.worker.awaitTermination(30, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new IllegalStateException(ex);
|
|
||||||
}
|
|
||||||
catch (InterruptedException ex) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,17 @@
|
||||||
package org.springframework.boot.web.embedded.undertow;
|
package org.springframework.boot.web.embedded.undertow;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import io.undertow.Undertow;
|
import io.undertow.Undertow;
|
||||||
|
import io.undertow.server.HttpHandler;
|
||||||
|
import io.undertow.server.HttpServerExchange;
|
||||||
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.xnio.channels.BoundChannel;
|
import org.xnio.channels.BoundChannel;
|
||||||
|
|
@ -32,6 +36,7 @@ import org.springframework.boot.web.server.GracefulShutdown;
|
||||||
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;
|
||||||
import org.springframework.boot.web.server.WebServerException;
|
import org.springframework.boot.web.server.WebServerException;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
|
@ -55,16 +60,18 @@ public class UndertowWebServer implements WebServer {
|
||||||
|
|
||||||
private final Undertow.Builder builder;
|
private final Undertow.Builder builder;
|
||||||
|
|
||||||
|
private final Iterable<HttpHandlerFactory> httpHandlerFactories;
|
||||||
|
|
||||||
private final boolean autoStart;
|
private final boolean autoStart;
|
||||||
|
|
||||||
private final Closeable closeable;
|
|
||||||
|
|
||||||
private final GracefulShutdown gracefulShutdown;
|
|
||||||
|
|
||||||
private Undertow undertow;
|
private Undertow undertow;
|
||||||
|
|
||||||
private volatile boolean started = false;
|
private volatile boolean started = false;
|
||||||
|
|
||||||
|
private volatile GracefulShutdown gracefulShutdown;
|
||||||
|
|
||||||
|
private volatile List<Closeable> closeables;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link UndertowWebServer} instance.
|
* Create a new {@link UndertowWebServer} instance.
|
||||||
* @param builder the builder
|
* @param builder the builder
|
||||||
|
|
@ -80,25 +87,26 @@ public class UndertowWebServer implements WebServer {
|
||||||
* @param autoStart if the server should be started
|
* @param autoStart if the server should be started
|
||||||
* @param closeable called when the server is stopped
|
* @param closeable called when the server is stopped
|
||||||
* @since 2.0.4
|
* @since 2.0.4
|
||||||
|
* @deprecated since 2.3.0 in favor of
|
||||||
|
* {@link #UndertowWebServer(io.undertow.Undertow.Builder, Iterable, boolean)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public UndertowWebServer(Undertow.Builder builder, boolean autoStart, Closeable closeable) {
|
public UndertowWebServer(Undertow.Builder builder, boolean autoStart, Closeable closeable) {
|
||||||
this(builder, autoStart, closeable, GracefulShutdown.IMMEDIATE);
|
this(builder, Collections.singleton(new CloseableHttpHandlerFactory(closeable)), autoStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link UndertowWebServer} instance.
|
* Create a new {@link UndertowWebServer} instance.
|
||||||
* @param builder the builder
|
* @param builder the builder
|
||||||
|
* @param httpHandlerFactories the handler factories
|
||||||
* @param autoStart if the server should be started
|
* @param autoStart if the server should be started
|
||||||
* @param closeable called when the server is stopped
|
|
||||||
* @param gracefulShutdown handler for graceful shutdown
|
|
||||||
* @since 2.3.0
|
* @since 2.3.0
|
||||||
*/
|
*/
|
||||||
public UndertowWebServer(Undertow.Builder builder, boolean autoStart, Closeable closeable,
|
public UndertowWebServer(Undertow.Builder builder, Iterable<HttpHandlerFactory> httpHandlerFactories,
|
||||||
GracefulShutdown gracefulShutdown) {
|
boolean autoStart) {
|
||||||
this.builder = builder;
|
this.builder = builder;
|
||||||
|
this.httpHandlerFactories = httpHandlerFactories;
|
||||||
this.autoStart = autoStart;
|
this.autoStart = autoStart;
|
||||||
this.closeable = closeable;
|
|
||||||
this.gracefulShutdown = gracefulShutdown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -112,11 +120,12 @@ public class UndertowWebServer implements WebServer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.undertow == null) {
|
if (this.undertow == null) {
|
||||||
this.undertow = this.builder.build();
|
this.undertow = createUndertowServer();
|
||||||
}
|
}
|
||||||
this.undertow.start();
|
this.undertow.start();
|
||||||
this.started = true;
|
this.started = true;
|
||||||
logger.info("Undertow started on port(s) " + getPortsDescription());
|
String message = getStartLogMessage();
|
||||||
|
logger.info(message);
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -140,7 +149,7 @@ public class UndertowWebServer implements WebServer {
|
||||||
try {
|
try {
|
||||||
if (this.undertow != null) {
|
if (this.undertow != null) {
|
||||||
this.undertow.stop();
|
this.undertow.stop();
|
||||||
this.closeable.close();
|
this.closeables.forEach(this::closeSilently);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
|
|
@ -148,6 +157,37 @@ public class UndertowWebServer implements WebServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void closeSilently(Closeable closeable) {
|
||||||
|
try {
|
||||||
|
closeable.close();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Undertow createUndertowServer() {
|
||||||
|
this.closeables = new ArrayList<>();
|
||||||
|
this.gracefulShutdown = null;
|
||||||
|
HttpHandler handler = createHttpHandler();
|
||||||
|
this.builder.setHandler(handler);
|
||||||
|
return this.builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected HttpHandler createHttpHandler() {
|
||||||
|
HttpHandler handler = null;
|
||||||
|
for (HttpHandlerFactory factory : this.httpHandlerFactories) {
|
||||||
|
handler = factory.getHandler(handler);
|
||||||
|
if (handler instanceof Closeable) {
|
||||||
|
this.closeables.add((Closeable) handler);
|
||||||
|
}
|
||||||
|
if (handler instanceof GracefulShutdown) {
|
||||||
|
Assert.isNull(this.gracefulShutdown, "Only a single GracefulShutdown handler can be defined");
|
||||||
|
this.gracefulShutdown = (GracefulShutdown) handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
private String getPortsDescription() {
|
private String getPortsDescription() {
|
||||||
List<UndertowWebServer.Port> ports = getActualPorts();
|
List<UndertowWebServer.Port> ports = getActualPorts();
|
||||||
if (!ports.isEmpty()) {
|
if (!ports.isEmpty()) {
|
||||||
|
|
@ -156,11 +196,11 @@ public class UndertowWebServer implements WebServer {
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<UndertowWebServer.Port> getActualPorts() {
|
private List<Port> getActualPorts() {
|
||||||
List<UndertowWebServer.Port> ports = new ArrayList<>();
|
List<Port> ports = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
if (!this.autoStart) {
|
if (!this.autoStart) {
|
||||||
ports.add(new UndertowWebServer.Port(-1, "unknown"));
|
ports.add(new Port(-1, "unknown"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (BoundChannel channel : extractChannels()) {
|
for (BoundChannel channel : extractChannels()) {
|
||||||
|
|
@ -192,10 +232,13 @@ public class UndertowWebServer implements WebServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<UndertowWebServer.Port> getConfiguredPorts() {
|
private List<UndertowWebServer.Port> getConfiguredPorts() {
|
||||||
List<UndertowWebServer.Port> ports = new ArrayList<>();
|
List<Port> ports = new ArrayList<>();
|
||||||
for (Object listener : extractListeners()) {
|
for (Object listener : extractListeners()) {
|
||||||
try {
|
try {
|
||||||
ports.add(getPortFromListener(listener));
|
Port port = getPortFromListener(listener);
|
||||||
|
if (port.getNumber() != 0) {
|
||||||
|
ports.add(port);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
// Continue
|
// Continue
|
||||||
|
|
@ -230,8 +273,8 @@ public class UndertowWebServer implements WebServer {
|
||||||
this.started = false;
|
this.started = false;
|
||||||
try {
|
try {
|
||||||
this.undertow.stop();
|
this.undertow.stop();
|
||||||
if (this.closeable != null) {
|
for (Closeable closeable : this.closeables) {
|
||||||
this.closeable.close();
|
closeable.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
|
|
@ -242,7 +285,7 @@ public class UndertowWebServer implements WebServer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPort() {
|
public int getPort() {
|
||||||
List<UndertowWebServer.Port> ports = getActualPorts();
|
List<Port> ports = getActualPorts();
|
||||||
if (ports.isEmpty()) {
|
if (ports.isEmpty()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -251,11 +294,15 @@ public class UndertowWebServer implements WebServer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shutDownGracefully() {
|
public boolean shutDownGracefully() {
|
||||||
return (this.gracefulShutdown != null) && this.gracefulShutdown.shutDownGracefully();
|
return (this.gracefulShutdown != null) ? this.gracefulShutdown.shutDownGracefully() : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean inGracefulShutdown() {
|
boolean inGracefulShutdown() {
|
||||||
return (this.gracefulShutdown != null) && this.gracefulShutdown.isShuttingDown();
|
return (this.gracefulShutdown != null) ? this.gracefulShutdown.isShuttingDown() : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getStartLogMessage() {
|
||||||
|
return "Undertow started on port(s) " + getPortsDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -303,4 +350,44 @@ public class UndertowWebServer implements WebServer {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link HttpHandlerFactory} to wrap a closable.
|
||||||
|
*/
|
||||||
|
private static final class CloseableHttpHandlerFactory implements HttpHandlerFactory {
|
||||||
|
|
||||||
|
private final Closeable closeable;
|
||||||
|
|
||||||
|
private CloseableHttpHandlerFactory(Closeable closeable) {
|
||||||
|
this.closeable = closeable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpHandler getHandler(HttpHandler next) {
|
||||||
|
if (this.closeable == null) {
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
return new CloseableHttpHandler() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||||
|
next.handleRequest(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
CloseableHttpHandlerFactory.this.closeable.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Closeable} {@link HttpHandler}.
|
||||||
|
*/
|
||||||
|
private interface CloseableHttpHandler extends HttpHandler, Closeable {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,209 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.web.embedded.undertow;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import io.undertow.Handlers;
|
||||||
|
import io.undertow.Undertow;
|
||||||
|
import io.undertow.Undertow.Builder;
|
||||||
|
import io.undertow.UndertowOptions;
|
||||||
|
|
||||||
|
import org.springframework.boot.web.server.AbstractConfigurableWebServerFactory;
|
||||||
|
import org.springframework.boot.web.server.Compression;
|
||||||
|
import org.springframework.boot.web.server.Http2;
|
||||||
|
import org.springframework.boot.web.server.Ssl;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegate class used by {@link UndertowServletWebServerFactory} and
|
||||||
|
* {@link UndertowReactiveWebServerFactory}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
class UndertowWebServerFactoryDelegate {
|
||||||
|
|
||||||
|
private Set<UndertowBuilderCustomizer> builderCustomizers = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
private Integer bufferSize;
|
||||||
|
|
||||||
|
private Integer ioThreads;
|
||||||
|
|
||||||
|
private Integer workerThreads;
|
||||||
|
|
||||||
|
private Boolean directBuffers;
|
||||||
|
|
||||||
|
private File accessLogDirectory;
|
||||||
|
|
||||||
|
private String accessLogPattern;
|
||||||
|
|
||||||
|
private String accessLogPrefix;
|
||||||
|
|
||||||
|
private String accessLogSuffix;
|
||||||
|
|
||||||
|
private boolean accessLogEnabled = false;
|
||||||
|
|
||||||
|
private boolean accessLogRotate = true;
|
||||||
|
|
||||||
|
private boolean useForwardHeaders;
|
||||||
|
|
||||||
|
void setBuilderCustomizers(Collection<? extends UndertowBuilderCustomizer> customizers) {
|
||||||
|
Assert.notNull(customizers, "Customizers must not be null");
|
||||||
|
this.builderCustomizers = new LinkedHashSet<>(customizers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addBuilderCustomizers(UndertowBuilderCustomizer... customizers) {
|
||||||
|
Assert.notNull(customizers, "Customizers must not be null");
|
||||||
|
this.builderCustomizers.addAll(Arrays.asList(customizers));
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<UndertowBuilderCustomizer> getBuilderCustomizers() {
|
||||||
|
return this.builderCustomizers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBufferSize(Integer bufferSize) {
|
||||||
|
this.bufferSize = bufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setIoThreads(Integer ioThreads) {
|
||||||
|
this.ioThreads = ioThreads;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setWorkerThreads(Integer workerThreads) {
|
||||||
|
this.workerThreads = workerThreads;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUseDirectBuffers(Boolean directBuffers) {
|
||||||
|
this.directBuffers = directBuffers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAccessLogDirectory(File accessLogDirectory) {
|
||||||
|
this.accessLogDirectory = accessLogDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAccessLogPattern(String accessLogPattern) {
|
||||||
|
this.accessLogPattern = accessLogPattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAccessLogPrefix(String accessLogPrefix) {
|
||||||
|
this.accessLogPrefix = accessLogPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getAccessLogPrefix() {
|
||||||
|
return this.accessLogPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAccessLogSuffix(String accessLogSuffix) {
|
||||||
|
this.accessLogSuffix = accessLogSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAccessLogEnabled(boolean accessLogEnabled) {
|
||||||
|
this.accessLogEnabled = accessLogEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isAccessLogEnabled() {
|
||||||
|
return this.accessLogEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAccessLogRotate(boolean accessLogRotate) {
|
||||||
|
this.accessLogRotate = accessLogRotate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUseForwardHeaders(boolean useForwardHeaders) {
|
||||||
|
this.useForwardHeaders = useForwardHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isUseForwardHeaders() {
|
||||||
|
return this.useForwardHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder createBuilder(AbstractConfigurableWebServerFactory factory) {
|
||||||
|
Ssl ssl = factory.getSsl();
|
||||||
|
InetAddress address = factory.getAddress();
|
||||||
|
int port = factory.getPort();
|
||||||
|
Builder builder = Undertow.builder();
|
||||||
|
if (this.bufferSize != null) {
|
||||||
|
builder.setBufferSize(this.bufferSize);
|
||||||
|
}
|
||||||
|
if (this.ioThreads != null) {
|
||||||
|
builder.setIoThreads(this.ioThreads);
|
||||||
|
}
|
||||||
|
if (this.workerThreads != null) {
|
||||||
|
builder.setWorkerThreads(this.workerThreads);
|
||||||
|
}
|
||||||
|
if (this.directBuffers != null) {
|
||||||
|
builder.setDirectBuffers(this.directBuffers);
|
||||||
|
}
|
||||||
|
if (ssl != null && ssl.isEnabled()) {
|
||||||
|
new SslBuilderCustomizer(factory.getPort(), address, ssl, factory.getSslStoreProvider()).customize(builder);
|
||||||
|
Http2 http2 = factory.getHttp2();
|
||||||
|
if (http2 != null) {
|
||||||
|
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, http2.isEnabled());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
builder.addHttpListener(port, (address != null) ? address.getHostAddress() : "0.0.0.0");
|
||||||
|
}
|
||||||
|
builder.setServerOption(UndertowOptions.SHUTDOWN_TIMEOUT, 0);
|
||||||
|
for (UndertowBuilderCustomizer customizer : this.builderCustomizers) {
|
||||||
|
customizer.customize(builder);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<HttpHandlerFactory> createHttpHandlerFactories(AbstractConfigurableWebServerFactory webServerFactory,
|
||||||
|
HttpHandlerFactory... initialHttpHandlerFactories) {
|
||||||
|
List<HttpHandlerFactory> factories = createHttpHandlerFactories(webServerFactory.getCompression(),
|
||||||
|
this.useForwardHeaders, webServerFactory.getServerHeader(),
|
||||||
|
webServerFactory.getShutdown().getGracePeriod(), initialHttpHandlerFactories);
|
||||||
|
if (isAccessLogEnabled()) {
|
||||||
|
factories.add(new AccessLogHttpHandlerFactory(this.accessLogDirectory, this.accessLogPattern,
|
||||||
|
this.accessLogPrefix, this.accessLogSuffix, this.accessLogRotate));
|
||||||
|
}
|
||||||
|
return factories;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<HttpHandlerFactory> createHttpHandlerFactories(Compression compression, boolean useForwardHeaders,
|
||||||
|
String serverHeader, Duration shutdownGracePeriod, HttpHandlerFactory... initialHttpHandlerFactories) {
|
||||||
|
List<HttpHandlerFactory> factories = new ArrayList<HttpHandlerFactory>();
|
||||||
|
factories.addAll(Arrays.asList(initialHttpHandlerFactories));
|
||||||
|
if (compression != null && compression.getEnabled()) {
|
||||||
|
factories.add(new CompressionHttpHandlerFactory(compression));
|
||||||
|
}
|
||||||
|
if (useForwardHeaders) {
|
||||||
|
factories.add(Handlers::proxyPeerAddress);
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(serverHeader)) {
|
||||||
|
factories.add((next) -> Handlers.header(next, "Server", serverHeader));
|
||||||
|
}
|
||||||
|
if (shutdownGracePeriod != null) {
|
||||||
|
factories.add((next) -> new GracefulShutdownHttpHandler(next, shutdownGracePeriod));
|
||||||
|
}
|
||||||
|
return factories;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue