// // ======================================================================== // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.server; import java.io.IOException; import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import org.eclipse.jetty.http.CookieCompliance; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.util.Jetty; import org.eclipse.jetty.util.TreeTrie; import org.eclipse.jetty.util.Trie; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; /* ------------------------------------------------------------ */ /** HTTP Configuration. * <p>This class is a holder of HTTP configuration for use by the * {@link HttpChannel} class. Typically a HTTPConfiguration instance * is instantiated and passed to a {@link ConnectionFactory} that can * create HTTP channels (e.g. HTTP, AJP or FCGI).</p> * <p>The configuration held by this class is not for the wire protocol, * but for the interpretation and handling of HTTP requests that could * be transported by a variety of protocols. * </p> */ @ManagedObject("HTTP Configuration") public class HttpConfiguration { public static final String SERVER_VERSION = "Jetty(" + Jetty.VERSION + ")"; private final List<Customizer> _customizers=new CopyOnWriteArrayList<>(); private final Trie<Boolean> _formEncodedMethods = new TreeTrie<>(); private int _outputBufferSize=32*1024; private int _outputAggregationSize=_outputBufferSize/4; private int _requestHeaderSize=8*1024; private int _responseHeaderSize=8*1024; private int _headerCacheSize=512; private int _securePort; private long _idleTimeout=-1; private long _blockingTimeout=-1; private String _secureScheme = HttpScheme.HTTPS.asString(); private boolean _sendServerVersion = true; private boolean _sendXPoweredBy = false; private boolean _sendDateHeader = true; private boolean _delayDispatchUntilContent = true; private boolean _persistentConnectionsEnabled = true; private int _maxErrorDispatches = 10; private boolean _useDirectByteBuffers = false; private long _minRequestDataRate; private CookieCompliance _cookieCompliance = CookieCompliance.RFC6265; /* ------------------------------------------------------------ */ /** * <p>An interface that allows a request object to be customized * for a particular HTTP connector configuration. Unlike Filters, customizer are * applied before the request is submitted for processing and can be specific to the * connector on which the request was received. * * <p>Typically Customizers perform tasks such as: <ul> * <li>process header fields that may be injected by a proxy or load balancer. * <li>setup attributes that may come from the connection/connector such as SSL Session IDs * <li>Allow a request to be marked as secure or authenticated if those have been offloaded * and communicated by header, cookie or other out-of-band mechanism * <li>Set request attributes/fields that are determined by the connector on which the * request was received * </ul> */ public interface Customizer { public void customize(Connector connector, HttpConfiguration channelConfig, Request request); } public interface ConnectionFactory { HttpConfiguration getHttpConfiguration(); } public HttpConfiguration() { _formEncodedMethods.put(HttpMethod.POST.asString(),Boolean.TRUE); _formEncodedMethods.put(HttpMethod.PUT.asString(),Boolean.TRUE); } /* ------------------------------------------------------------ */ /** Create a configuration from another. * @param config The configuration to copy. */ public HttpConfiguration(HttpConfiguration config) { _customizers.addAll(config._customizers); for (String s:config._formEncodedMethods.keySet()) _formEncodedMethods.put(s,Boolean.TRUE); _outputBufferSize=config._outputBufferSize; _outputAggregationSize=config._outputAggregationSize; _requestHeaderSize=config._requestHeaderSize; _responseHeaderSize=config._responseHeaderSize; _headerCacheSize=config._headerCacheSize; _secureScheme=config._secureScheme; _securePort=config._securePort; _idleTimeout=config._idleTimeout; _blockingTimeout=config._blockingTimeout; _sendDateHeader=config._sendDateHeader; _sendServerVersion=config._sendServerVersion; _sendXPoweredBy=config._sendXPoweredBy; _delayDispatchUntilContent=config._delayDispatchUntilContent; _persistentConnectionsEnabled=config._persistentConnectionsEnabled; _maxErrorDispatches=config._maxErrorDispatches; _useDirectByteBuffers=config._useDirectByteBuffers; _minRequestDataRate=config._minRequestDataRate; _cookieCompliance=config._cookieCompliance; } /* ------------------------------------------------------------ */ /** * <p>Add a {@link Customizer} that is invoked for every * request received.</p> * <p>Customiser are often used to interpret optional headers (eg {@link ForwardedRequestCustomizer}) or * optional protocol semantics (eg {@link SecureRequestCustomizer}). * @param customizer A request customizer */ public void addCustomizer(Customizer customizer) { _customizers.add(customizer); } /* ------------------------------------------------------------ */ public List<Customizer> getCustomizers() { return _customizers; } /* ------------------------------------------------------------ */ public <T> T getCustomizer(Class<T> type) { for (Customizer c : _customizers) if (type.isAssignableFrom(c.getClass())) return (T)c; return null; } /* ------------------------------------------------------------ */ @ManagedAttribute("The size in bytes of the output buffer used to aggregate HTTP output") public int getOutputBufferSize() { return _outputBufferSize; } /* ------------------------------------------------------------ */ @ManagedAttribute("The maximum size in bytes for HTTP output to be aggregated") public int getOutputAggregationSize() { return _outputAggregationSize; } /* ------------------------------------------------------------ */ @ManagedAttribute("The maximum allowed size in bytes for a HTTP request header") public int getRequestHeaderSize() { return _requestHeaderSize; } /* ------------------------------------------------------------ */ @ManagedAttribute("The maximum allowed size in bytes for a HTTP response header") public int getResponseHeaderSize() { return _responseHeaderSize; } /* ------------------------------------------------------------ */ @ManagedAttribute("The maximum allowed size in bytes for a HTTP header field cache") public int getHeaderCacheSize() { return _headerCacheSize; } /* ------------------------------------------------------------ */ @ManagedAttribute("The port to which Integral or Confidential security constraints are redirected") public int getSecurePort() { return _securePort; } /* ------------------------------------------------------------ */ @ManagedAttribute("The scheme with which Integral or Confidential security constraints are redirected") public String getSecureScheme() { return _secureScheme; } /* ------------------------------------------------------------ */ @ManagedAttribute("Whether persistent connections are enabled") public boolean isPersistentConnectionsEnabled() { return _persistentConnectionsEnabled; } /* ------------------------------------------------------------ */ /** Get the max idle time in ms. * <p>The max idle time is applied to a HTTP request for IO operations and * delayed dispatch. * @return the max idle time in ms or if == 0 implies an infinite timeout, <0 * implies no HTTP channel timeout and the connection timeout is used instead. */ @ManagedAttribute("The idle timeout in ms for I/O operations during the handling of a HTTP request") public long getIdleTimeout() { return _idleTimeout; } /* ------------------------------------------------------------ */ /** Set the max idle time in ms. * <p>The max idle time is applied to a HTTP request for IO operations and * delayed dispatch. * @param timeoutMs the max idle time in ms or if == 0 implies an infinite timeout, <0 * implies no HTTP channel timeout and the connection timeout is used instead. */ public void setIdleTimeout(long timeoutMs) { _idleTimeout=timeoutMs; } /* ------------------------------------------------------------ */ /** Get the timeout applied to blocking operations. * <p>This timeout is in addition to the {@link Connector#getIdleTimeout()}, and applies * to the total operation (as opposed to the idle timeout that applies to the time no * data is being sent). * @return -1, for no blocking timeout (default), 0 for a blocking timeout equal to the * idle timeout; >0 for a timeout in ms applied to the total blocking operation. */ @ManagedAttribute("Total timeout in ms for blocking I/O operations.") public long getBlockingTimeout() { return _blockingTimeout; } /** * Set the timeout applied to blocking operations. * <p>This timeout is in addition to the {@link Connector#getIdleTimeout()}, and applies * to the total operation (as opposed to the idle timeout that applies to the time no * data is being sent). * @param blockingTimeout -1, for no blocking timeout (default), 0 for a blocking timeout equal to the * idle timeout; >0 for a timeout in ms applied to the total blocking operation. */ public void setBlockingTimeout(long blockingTimeout) { _blockingTimeout = blockingTimeout; } /* ------------------------------------------------------------ */ public void setPersistentConnectionsEnabled(boolean persistentConnectionsEnabled) { _persistentConnectionsEnabled = persistentConnectionsEnabled; } /* ------------------------------------------------------------ */ public void setSendServerVersion (boolean sendServerVersion) { _sendServerVersion = sendServerVersion; } /* ------------------------------------------------------------ */ @ManagedAttribute("Whether to send the Server header in responses") public boolean getSendServerVersion() { return _sendServerVersion; } /* ------------------------------------------------------------ */ public void writePoweredBy(Appendable out,String preamble,String postamble) throws IOException { if (getSendServerVersion()) { if (preamble!=null) out.append(preamble); out.append(Jetty.POWERED_BY); if (postamble!=null) out.append(postamble); } } /* ------------------------------------------------------------ */ public void setSendXPoweredBy (boolean sendXPoweredBy) { _sendXPoweredBy=sendXPoweredBy; } /* ------------------------------------------------------------ */ @ManagedAttribute("Whether to send the X-Powered-By header in responses") public boolean getSendXPoweredBy() { return _sendXPoweredBy; } /* ------------------------------------------------------------ */ public void setSendDateHeader(boolean sendDateHeader) { _sendDateHeader = sendDateHeader; } /* ------------------------------------------------------------ */ @ManagedAttribute("Whether to send the Date header in responses") public boolean getSendDateHeader() { return _sendDateHeader; } /* ------------------------------------------------------------ */ /** * @param delay if true, delay the application dispatch until content is available (default false) */ public void setDelayDispatchUntilContent(boolean delay) { _delayDispatchUntilContent = delay; } /* ------------------------------------------------------------ */ @ManagedAttribute("Whether to delay the application dispatch until content is available") public boolean isDelayDispatchUntilContent() { return _delayDispatchUntilContent; } /* ------------------------------------------------------------ */ /** * @param useDirectByteBuffers if true, use direct byte buffers for requests */ public void setUseDirectByteBuffers(boolean useDirectByteBuffers) { _useDirectByteBuffers = useDirectByteBuffers; } /* ------------------------------------------------------------ */ @ManagedAttribute("Whether to use direct byte buffers for requests") public boolean isUseDirectByteBuffers() { return _useDirectByteBuffers; } /* ------------------------------------------------------------ */ /** * <p>Set the {@link Customizer}s that are invoked for every * request received.</p> * <p>Customizers are often used to interpret optional headers (eg {@link ForwardedRequestCustomizer}) or * optional protocol semantics (eg {@link SecureRequestCustomizer}). * @param customizers the list of customizers */ public void setCustomizers(List<Customizer> customizers) { _customizers.clear(); _customizers.addAll(customizers); } /* ------------------------------------------------------------ */ /** * Set the size of the buffer into which response content is aggregated * before being sent to the client. A larger buffer can improve performance by allowing * a content producer to run without blocking, however larger buffers consume more memory and * may induce some latency before a client starts processing the content. * @param outputBufferSize buffer size in bytes. */ public void setOutputBufferSize(int outputBufferSize) { _outputBufferSize = outputBufferSize; setOutputAggregationSize(outputBufferSize / 4); } /* ------------------------------------------------------------ */ /** * Set the max size of the response content write that is copied into the aggregate buffer. * Writes that are smaller of this size are copied into the aggregate buffer, while * writes that are larger of this size will cause the aggregate buffer to be flushed * and the write to be executed without being copied. * @param outputAggregationSize the max write size that is aggregated */ public void setOutputAggregationSize(int outputAggregationSize) { _outputAggregationSize = outputAggregationSize; } /* ------------------------------------------------------------ */ /** Set the maximum size of a request header. * <p>Larger headers will allow for more and/or larger cookies plus larger form content encoded * in a URL. However, larger headers consume more memory and can make a server more vulnerable to denial of service * attacks.</p> * @param requestHeaderSize Max header size in bytes */ public void setRequestHeaderSize(int requestHeaderSize) { _requestHeaderSize = requestHeaderSize; } /* ------------------------------------------------------------ */ /** Set the maximum size of a response header. * * <p>Larger headers will allow for more and/or larger cookies and longer HTTP headers (eg for redirection). * However, larger headers will also consume more memory.</p> * @param responseHeaderSize Response header size in bytes. */ public void setResponseHeaderSize(int responseHeaderSize) { _responseHeaderSize = responseHeaderSize; } /* ------------------------------------------------------------ */ /** Set the header field cache size. * @param headerCacheSize The size in bytes of the header field cache. */ public void setHeaderCacheSize(int headerCacheSize) { _headerCacheSize = headerCacheSize; } /* ------------------------------------------------------------ */ /** Set the TCP/IP port used for CONFIDENTIAL and INTEGRAL redirections. * @param securePort the secure port to redirect to. */ public void setSecurePort(int securePort) { _securePort = securePort; } /* ------------------------------------------------------------ */ /** Set the URI scheme used for CONFIDENTIAL and INTEGRAL redirections. * @param secureScheme A scheme string like "https" */ public void setSecureScheme(String secureScheme) { _secureScheme = secureScheme; } /* ------------------------------------------------------------ */ @Override public String toString() { return String.format("%s@%x{%d/%d,%d/%d,%s://:%d,%s}", this.getClass().getSimpleName(), hashCode(), _outputBufferSize, _outputAggregationSize, _requestHeaderSize,_responseHeaderSize, _secureScheme,_securePort, _customizers); } /* ------------------------------------------------------------ */ /** Set the form encoded methods. * @param methods HTTP Methods of requests that can be decoded as * x-www-form-urlencoded content to be made available via the * {@link Request#getParameter(String)} and associated APIs */ public void setFormEncodedMethods(String... methods) { _formEncodedMethods.clear(); for (String method:methods) addFormEncodedMethod(method); } /* ------------------------------------------------------------ */ /** * @return Set of HTTP Methods of requests that can be decoded as * x-www-form-urlencoded content to be made available via the * {@link Request#getParameter(String)} and associated APIs */ public Set<String> getFormEncodedMethods() { return _formEncodedMethods.keySet(); } /* ------------------------------------------------------------ */ /** Add a form encoded HTTP Method * @param method HTTP Method of requests that can be decoded as * x-www-form-urlencoded content to be made available via the * {@link Request#getParameter(String)} and associated APIs */ public void addFormEncodedMethod(String method) { _formEncodedMethods.put(method,Boolean.TRUE); } /* ------------------------------------------------------------ */ /** * Test if the method type supports <code>x-www-form-urlencoded</code> content * * @param method the method type * @return True of the requests of this method type can be * decoded as <code>x-www-form-urlencoded</code> content to be made available via the * {@link Request#getParameter(String)} and associated APIs */ public boolean isFormEncodedMethod(String method) { return Boolean.TRUE.equals(_formEncodedMethods.get(method)); } /* ------------------------------------------------------------ */ /** * @return The maximum error dispatches for a request to prevent looping on an error */ @ManagedAttribute("The maximum ERROR dispatches for a request for loop prevention (default 10)") public int getMaxErrorDispatches() { return _maxErrorDispatches; } /* ------------------------------------------------------------ */ /** * @param max The maximum error dispatches for a request to prevent looping on an error */ public void setMaxErrorDispatches(int max) { _maxErrorDispatches=max; } /* ------------------------------------------------------------ */ /** * @return The minimum request data rate in bytes per second; or <=0 for no limit */ @ManagedAttribute("The minimum request content data rate in bytes per second") public long getMinRequestDataRate() { return _minRequestDataRate; } /* ------------------------------------------------------------ */ /** * @param bytesPerSecond The minimum request data rate in bytes per second; or <=0 for no limit */ public void setMinRequestDataRate(long bytesPerSecond) { _minRequestDataRate=bytesPerSecond; } /* ------------------------------------------------------------ */ public CookieCompliance getCookieCompliance() { return _cookieCompliance; } /* ------------------------------------------------------------ */ public void setCookieCompliance(CookieCompliance cookieCompliance) { _cookieCompliance = cookieCompliance==null?CookieCompliance.RFC6265:cookieCompliance; } /* ------------------------------------------------------------ */ public boolean isCookieCompliance(CookieCompliance compliance) { return _cookieCompliance.equals(compliance); } }