/*
* Copyright 2012-2017 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
*
* http://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.autoconfigure.web;
import java.io.File;
import java.net.InetAddress;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.boot.web.server.Compression;
import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.servlet.server.Jsp;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* {@link ConfigurationProperties} for a web server (e.g. port and path settings).
*
* @author Dave Syer
* @author Stephane Nicoll
* @author Andy Wilkinson
* @author Ivan Sopov
* @author Marcos Barbero
* @author Eddú Meléndez
* @author Quinten De Swaef
* @author Venil Noronha
* @author Aurélien Leboulanger
* @author Brian Clozel
* @author Olivier Lamy
*/
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
/**
* Server HTTP port.
*/
private Integer port;
/**
* Network address to which the server should bind to.
*/
private InetAddress address;
/**
* Display name of the application.
*/
private String displayName = "application";
@NestedConfigurationProperty
private ErrorProperties error = new ErrorProperties();
/**
* If X-Forwarded-* headers should be applied to the HttpRequest.
*/
private Boolean useForwardHeaders;
/**
* Value to use for the Server response header (no header is sent if empty).
*/
private String serverHeader;
/**
* Maximum size in bytes of the HTTP message header.
*/
private int maxHttpHeaderSize = 0; // bytes
/**
* Time in milliseconds that connectors will wait for another HTTP request before
* closing the connection. When not set, the connector's server-specific default will
* be used. Use a value of -1 to indicate no (i.e. infinite) timeout.
*/
private Integer connectionTimeout;
private Session session = new Session();
@NestedConfigurationProperty
private Ssl ssl;
@NestedConfigurationProperty
private Compression compression = new Compression();
private Servlet servlet = new Servlet();
private final Tomcat tomcat = new Tomcat();
private final Jetty jetty = new Jetty();
private final Undertow undertow = new Undertow();
public Integer getPort() {
return this.port;
}
public void setPort(Integer port) {
this.port = port;
}
public InetAddress getAddress() {
return this.address;
}
public void setAddress(InetAddress address) {
this.address = address;
}
public String getDisplayName() {
return this.displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public Boolean isUseForwardHeaders() {
return this.useForwardHeaders;
}
public void setUseForwardHeaders(Boolean useForwardHeaders) {
this.useForwardHeaders = useForwardHeaders;
}
public String getServerHeader() {
return this.serverHeader;
}
public void setServerHeader(String serverHeader) {
this.serverHeader = serverHeader;
}
public int getMaxHttpHeaderSize() {
return this.maxHttpHeaderSize;
}
public void setMaxHttpHeaderSize(int maxHttpHeaderSize) {
this.maxHttpHeaderSize = maxHttpHeaderSize;
}
public Integer getConnectionTimeout() {
return this.connectionTimeout;
}
public void setConnectionTimeout(Integer connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
public ErrorProperties getError() {
return this.error;
}
public Session getSession() {
return this.session;
}
public void setSession(Session session) {
this.session = session;
}
public Ssl getSsl() {
return this.ssl;
}
public void setSsl(Ssl ssl) {
this.ssl = ssl;
}
public Compression getCompression() {
return this.compression;
}
public Servlet getServlet() {
return this.servlet;
}
public void setServlet(Servlet servlet) {
this.servlet = servlet;
}
public Tomcat getTomcat() {
return this.tomcat;
}
public Jetty getJetty() {
return this.jetty;
}
public Undertow getUndertow() {
return this.undertow;
}
/**
* Servlet properties.
*/
public class Servlet {
/**
* Servlet context init parameters.
*/
private final Map<String, String> contextParameters = new HashMap<>();
/**
* Context path of the application.
*/
private String contextPath;
/**
* Path of the main dispatcher servlet.
*/
private String path = "/";
@NestedConfigurationProperty
private Jsp jsp = new Jsp();
public String getContextPath() {
return this.contextPath;
}
public void setContextPath(String contextPath) {
this.contextPath = cleanContextPath(contextPath);
}
private String cleanContextPath(String contextPath) {
if (StringUtils.hasText(contextPath) && contextPath.endsWith("/")) {
return contextPath.substring(0, contextPath.length() - 1);
}
return contextPath;
}
public String getPath() {
return this.path;
}
public void setPath(String path) {
Assert.notNull(path, "Path must not be null");
this.path = path;
}
public Map<String, String> getContextParameters() {
return this.contextParameters;
}
public Jsp getJsp() {
return this.jsp;
}
public void setJsp(Jsp jsp) {
this.jsp = jsp;
}
public String getServletMapping() {
if (this.path.equals("") || this.path.equals("/")) {
return "/";
}
if (this.path.contains("*")) {
return this.path;
}
if (this.path.endsWith("/")) {
return this.path + "*";
}
return this.path + "/*";
}
public String getPath(String path) {
String prefix = getServletPrefix();
if (!path.startsWith("/")) {
path = "/" + path;
}
return prefix + path;
}
public String getServletPrefix() {
String result = this.path;
if (result.contains("*")) {
result = result.substring(0, result.indexOf("*"));
}
if (result.endsWith("/")) {
result = result.substring(0, result.length() - 1);
}
return result;
}
public String[] getPathsArray(Collection<String> paths) {
String[] result = new String[paths.size()];
int i = 0;
for (String path : paths) {
result[i++] = getPath(path);
}
return result;
}
public String[] getPathsArray(String[] paths) {
String[] result = new String[paths.length];
int i = 0;
for (String path : paths) {
result[i++] = getPath(path);
}
return result;
}
}
/**
* Session properties.
*/
public static class Session {
/**
* Session timeout in seconds.
*/
private Integer timeout;
/**
* Session tracking modes (one or more of the following: "cookie", "url", "ssl").
*/
private Set<SessionTrackingMode> trackingModes;
/**
* Persist session data between restarts.
*/
private boolean persistent;
/**
* Directory used to store session data.
*/
private File storeDir;
private Cookie cookie = new Cookie();
public Cookie getCookie() {
return this.cookie;
}
public Integer getTimeout() {
return this.timeout;
}
public void setTimeout(Integer sessionTimeout) {
this.timeout = sessionTimeout;
}
public Set<SessionTrackingMode> getTrackingModes() {
return this.trackingModes;
}
public void setTrackingModes(Set<SessionTrackingMode> trackingModes) {
this.trackingModes = trackingModes;
}
public boolean isPersistent() {
return this.persistent;
}
public void setPersistent(boolean persistent) {
this.persistent = persistent;
}
public File getStoreDir() {
return this.storeDir;
}
public void setStoreDir(File storeDir) {
this.storeDir = storeDir;
}
/**
* Cookie properties.
*/
public static class Cookie {
/**
* Session cookie name.
*/
private String name;
/**
* Domain for the session cookie.
*/
private String domain;
/**
* Path of the session cookie.
*/
private String path;
/**
* Comment for the session cookie.
*/
private String comment;
/**
* "HttpOnly" flag for the session cookie.
*/
private Boolean httpOnly;
/**
* "Secure" flag for the session cookie.
*/
private Boolean secure;
/**
* Maximum age of the session cookie in seconds.
*/
private Integer maxAge;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getDomain() {
return this.domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
public String getPath() {
return this.path;
}
public void setPath(String path) {
this.path = path;
}
public String getComment() {
return this.comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public Boolean getHttpOnly() {
return this.httpOnly;
}
public void setHttpOnly(Boolean httpOnly) {
this.httpOnly = httpOnly;
}
public Boolean getSecure() {
return this.secure;
}
public void setSecure(Boolean secure) {
this.secure = secure;
}
public Integer getMaxAge() {
return this.maxAge;
}
public void setMaxAge(Integer maxAge) {
this.maxAge = maxAge;
}
}
/**
* Available session tracking modes (mirrors
* {@link javax.servlet.SessionTrackingMode}.
*/
public enum SessionTrackingMode {
/**
* Send a cookie in response to the client's first request.
*/
COOKIE,
/**
* Rewrite the URL to append a session ID.
*/
URL,
/**
* Use SSL build-in mechanism to track the session.
*/
SSL
}
}
/**
* Tomcat properties.
*/
public static class Tomcat {
/**
* Access log configuration.
*/
private final Accesslog accesslog = new Accesslog();
/**
* Regular expression that matches proxies that are to be trusted.
*/
private String internalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 10/8
+ "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" // 192.168/16
+ "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" // 169.254/16
+ "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 127/8
+ "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" // 172.16/12
+ "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|"
+ "172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}";
/**
* Header that holds the incoming protocol, usually named "X-Forwarded-Proto".
*/
private String protocolHeader;
/**
* Value of the protocol header that indicates that the incoming request uses SSL.
*/
private String protocolHeaderHttpsValue = "https";
/**
* Name of the HTTP header used to override the original port value.
*/
private String portHeader = "X-Forwarded-Port";
/**
* Name of the http header from which the remote ip is extracted..
*/
private String remoteIpHeader;
/**
* Tomcat base directory. If not specified a temporary directory will be used.
*/
private File basedir;
/**
* Delay in seconds between the invocation of backgroundProcess methods.
*/
private int backgroundProcessorDelay = 30; // seconds
/**
* Maximum amount of worker threads.
*/
private int maxThreads = 0; // Number of threads in protocol handler
/**
* Minimum amount of worker threads.
*/
private int minSpareThreads = 0; // Minimum spare threads in protocol handler
/**
* Maximum size in bytes of the HTTP post content.
*/
private int maxHttpPostSize = 0; // bytes
/**
* Maximum size in bytes of the HTTP message header.
*/
private int maxHttpHeaderSize = 0; // bytes
/**
* Whether requests to the context root should be redirected by appending a / to
* the path.
*/
private Boolean redirectContextRoot;
/**
* Character encoding to use to decode the URI.
*/
private Charset uriEncoding;
/**
* Maximum number of connections that the server will accept and process at any
* given time. Once the limit has been reached, the operating system may still
* accept connections based on the "acceptCount" property.
*/
private int maxConnections = 0;
/**
* Maximum queue length for incoming connection requests when all possible request
* processing threads are in use.
*/
private int acceptCount = 0;
/**
* Comma-separated list of additional patterns that match jars to ignore for TLD
* scanning. The special '?' and '*' characters can be used in the pattern to
* match one and only one character and zero or more characters respectively.
*/
private List<String> additionalTldSkipPatterns = new ArrayList<>();
public int getMaxThreads() {
return this.maxThreads;
}
public void setMaxThreads(int maxThreads) {
this.maxThreads = maxThreads;
}
public int getMinSpareThreads() {
return this.minSpareThreads;
}
public void setMinSpareThreads(int minSpareThreads) {
this.minSpareThreads = minSpareThreads;
}
public int getMaxHttpPostSize() {
return this.maxHttpPostSize;
}
public void setMaxHttpPostSize(int maxHttpPostSize) {
this.maxHttpPostSize = maxHttpPostSize;
}
public Accesslog getAccesslog() {
return this.accesslog;
}
public int getBackgroundProcessorDelay() {
return this.backgroundProcessorDelay;
}
public void setBackgroundProcessorDelay(int backgroundProcessorDelay) {
this.backgroundProcessorDelay = backgroundProcessorDelay;
}
public File getBasedir() {
return this.basedir;
}
public void setBasedir(File basedir) {
this.basedir = basedir;
}
public String getInternalProxies() {
return this.internalProxies;
}
public void setInternalProxies(String internalProxies) {
this.internalProxies = internalProxies;
}
public String getProtocolHeader() {
return this.protocolHeader;
}
public void setProtocolHeader(String protocolHeader) {
this.protocolHeader = protocolHeader;
}
public String getProtocolHeaderHttpsValue() {
return this.protocolHeaderHttpsValue;
}
public void setProtocolHeaderHttpsValue(String protocolHeaderHttpsValue) {
this.protocolHeaderHttpsValue = protocolHeaderHttpsValue;
}
public String getPortHeader() {
return this.portHeader;
}
public void setPortHeader(String portHeader) {
this.portHeader = portHeader;
}
public Boolean getRedirectContextRoot() {
return this.redirectContextRoot;
}
public void setRedirectContextRoot(Boolean redirectContextRoot) {
this.redirectContextRoot = redirectContextRoot;
}
public String getRemoteIpHeader() {
return this.remoteIpHeader;
}
public void setRemoteIpHeader(String remoteIpHeader) {
this.remoteIpHeader = remoteIpHeader;
}
public Charset getUriEncoding() {
return this.uriEncoding;
}
public void setUriEncoding(Charset uriEncoding) {
this.uriEncoding = uriEncoding;
}
public int getMaxConnections() {
return this.maxConnections;
}
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
public int getMaxHttpHeaderSize() {
return this.maxHttpHeaderSize;
}
public void setMaxHttpHeaderSize(int maxHttpHeaderSize) {
this.maxHttpHeaderSize = maxHttpHeaderSize;
}
public int getAcceptCount() {
return this.acceptCount;
}
public void setAcceptCount(int acceptCount) {
this.acceptCount = acceptCount;
}
public List<String> getAdditionalTldSkipPatterns() {
return this.additionalTldSkipPatterns;
}
public void setAdditionalTldSkipPatterns(List<String> additionalTldSkipPatterns) {
this.additionalTldSkipPatterns = additionalTldSkipPatterns;
}
/**
* Tomcat access log properties.
*/
public static class Accesslog {
/**
* Enable access log.
*/
private boolean enabled = false;
/**
* Format pattern for access logs.
*/
private String pattern = "common";
/**
* Directory in which log files are created. Can be relative to the tomcat
* base dir or absolute.
*/
private String directory = "logs";
/**
* Log file name prefix.
*/
protected String prefix = "access_log";
/**
* Log file name suffix.
*/
private String suffix = ".log";
/**
* Enable access log rotation.
*/
private boolean rotate = true;
/**
* Defer inclusion of the date stamp in the file name until rotate time.
*/
private boolean renameOnRotate;
/**
* Date format to place in log file name.
*/
private String fileDateFormat = ".yyyy-MM-dd";
/**
* Set request attributes for IP address, Hostname, protocol and port used for
* the request.
*/
private boolean requestAttributesEnabled;
/**
* Buffer output such that it is only flushed periodically.
*/
private boolean buffered = true;
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getPattern() {
return this.pattern;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
public String getDirectory() {
return this.directory;
}
public void setDirectory(String directory) {
this.directory = directory;
}
public String getPrefix() {
return this.prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return this.suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public boolean isRotate() {
return this.rotate;
}
public void setRotate(boolean rotate) {
this.rotate = rotate;
}
public boolean isRenameOnRotate() {
return this.renameOnRotate;
}
public void setRenameOnRotate(boolean renameOnRotate) {
this.renameOnRotate = renameOnRotate;
}
public String getFileDateFormat() {
return this.fileDateFormat;
}
public void setFileDateFormat(String fileDateFormat) {
this.fileDateFormat = fileDateFormat;
}
public boolean isRequestAttributesEnabled() {
return this.requestAttributesEnabled;
}
public void setRequestAttributesEnabled(boolean requestAttributesEnabled) {
this.requestAttributesEnabled = requestAttributesEnabled;
}
public boolean isBuffered() {
return this.buffered;
}
public void setBuffered(boolean buffered) {
this.buffered = buffered;
}
}
}
/**
* Jetty properties.
*/
public static class Jetty {
/**
* Access log configuration.
*/
private final Accesslog accesslog = new Accesslog();
/**
* Maximum size in bytes of the HTTP post or put content.
*/
private int maxHttpPostSize = 0; // bytes
/**
* Number of acceptor threads to use.
*/
private Integer acceptors;
/**
* Number of selector threads to use.
*/
private Integer selectors;
public Accesslog getAccesslog() {
return this.accesslog;
}
public int getMaxHttpPostSize() {
return this.maxHttpPostSize;
}
public void setMaxHttpPostSize(int maxHttpPostSize) {
this.maxHttpPostSize = maxHttpPostSize;
}
public Integer getAcceptors() {
return this.acceptors;
}
public void setAcceptors(Integer acceptors) {
this.acceptors = acceptors;
}
public Integer getSelectors() {
return this.selectors;
}
public void setSelectors(Integer selectors) {
this.selectors = selectors;
}
/**
* Jetty access log properties.
*/
public static class Accesslog {
/**
* Enable access log.
*/
private boolean enabled = false;
/**
* Log filename. If not specified, logs will be redirected to "System.err".
*/
private String filename;
/**
* Date format to place in log file name.
*/
private String fileDateFormat;
/**
* Number of days before rotated log files are deleted.
*/
private int retentionPeriod = 31; // no days
/**
* Append to log.
*/
private boolean append;
/**
* Enable extended NCSA format.
*/
private boolean extendedFormat;
/**
* Timestamp format of the request log.
*/
private String dateFormat = "dd/MMM/yyyy:HH:mm:ss Z";
/**
* Locale of the request log.
*/
private Locale locale;
/**
* Timezone of the request log.
*/
private TimeZone timeZone = TimeZone.getTimeZone("GMT");
/**
* Enable logging of the request cookies.
*/
private boolean logCookies;
/**
* Enable logging of the request hostname.
*/
private boolean logServer;
/**
* Enable logging of request processing time.
*/
private boolean logLatency;
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getFilename() {
return this.filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public String getFileDateFormat() {
return this.fileDateFormat;
}
public void setFileDateFormat(String fileDateFormat) {
this.fileDateFormat = fileDateFormat;
}
public int getRetentionPeriod() {
return this.retentionPeriod;
}
public void setRetentionPeriod(int retentionPeriod) {
this.retentionPeriod = retentionPeriod;
}
public boolean isAppend() {
return this.append;
}
public void setAppend(boolean append) {
this.append = append;
}
public boolean isExtendedFormat() {
return this.extendedFormat;
}
public void setExtendedFormat(boolean extendedFormat) {
this.extendedFormat = extendedFormat;
}
public String getDateFormat() {
return this.dateFormat;
}
public void setDateFormat(String dateFormat) {
this.dateFormat = dateFormat;
}
public Locale getLocale() {
return this.locale;
}
public void setLocale(Locale locale) {
this.locale = locale;
}
public TimeZone getTimeZone() {
return this.timeZone;
}
public void setTimeZone(TimeZone timeZone) {
this.timeZone = timeZone;
}
public boolean isLogCookies() {
return this.logCookies;
}
public void setLogCookies(boolean logCookies) {
this.logCookies = logCookies;
}
public boolean isLogServer() {
return this.logServer;
}
public void setLogServer(boolean logServer) {
this.logServer = logServer;
}
public boolean isLogLatency() {
return this.logLatency;
}
public void setLogLatency(boolean logLatency) {
this.logLatency = logLatency;
}
}
}
/**
* Undertow properties.
*/
public static class Undertow {
/**
* Maximum size in bytes of the HTTP post content.
*/
private long maxHttpPostSize = 0; // bytes
/**
* Size of each buffer in bytes.
*/
private Integer bufferSize;
/**
* Number of I/O threads to create for the worker.
*/
private Integer ioThreads;
/**
* Number of worker threads.
*/
private Integer workerThreads;
/**
* Allocate buffers outside the Java heap.
*/
private Boolean directBuffers;
private final Accesslog accesslog = new Accesslog();
public long getMaxHttpPostSize() {
return this.maxHttpPostSize;
}
public void setMaxHttpPostSize(long maxHttpPostSize) {
this.maxHttpPostSize = maxHttpPostSize;
}
public Integer getBufferSize() {
return this.bufferSize;
}
public void setBufferSize(Integer bufferSize) {
this.bufferSize = bufferSize;
}
public Integer getIoThreads() {
return this.ioThreads;
}
public void setIoThreads(Integer ioThreads) {
this.ioThreads = ioThreads;
}
public Integer getWorkerThreads() {
return this.workerThreads;
}
public void setWorkerThreads(Integer workerThreads) {
this.workerThreads = workerThreads;
}
public Boolean getDirectBuffers() {
return this.directBuffers;
}
public void setDirectBuffers(Boolean directBuffers) {
this.directBuffers = directBuffers;
}
public Accesslog getAccesslog() {
return this.accesslog;
}
/**
* Undertow access log properties.
*/
public static class Accesslog {
/**
* Enable access log.
*/
private Boolean enabled;
/**
* Format pattern for access logs.
*/
private String pattern = "common";
/**
* Log file name prefix.
*/
protected String prefix = "access_log.";
/**
* Log file name suffix.
*/
private String suffix = "log";
/**
* Undertow access log directory.
*/
private File dir = new File("logs");
/**
* Enable access log rotation.
*/
private boolean rotate = true;
public Boolean getEnabled() {
return this.enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public String getPattern() {
return this.pattern;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
public String getPrefix() {
return this.prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return this.suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public File getDir() {
return this.dir;
}
public void setDir(File dir) {
this.dir = dir;
}
public boolean isRotate() {
return this.rotate;
}
public void setRotate(boolean rotate) {
this.rotate = rotate;
}
}
}
}