KNOWN_MODES = Arrays.asList(RFC6265, RFC2965);
+ private static final AtomicInteger __custom = new AtomicInteger();
public static CookieCompliance valueOf(String name)
{
@@ -77,6 +98,71 @@ public class CookieCompliance implements ComplianceViolation.Mode
return null;
}
+ /**
+ * Create compliance set from string.
+ *
+ * Format: <BASE>[,[-]<violation>]...
+ *
+ * BASE is one of:
+ *
+ * - 0
- No {@link CookieCompliance.Violation}s
+ * - *
- All {@link CookieCompliance.Violation}s
+ * - <name>
- The name of a static instance of CookieCompliance (e.g. {@link CookieCompliance#RFC6265}).
+ *
+ *
+ * The remainder of the list can contain then names of {@link CookieCompliance.Violation}s to include them in the mode, or prefixed
+ * with a '-' to exclude them from the mode. Examples are:
+ *
+ *
+ * - {@code 0,RESERVED_NAMES_NOT_DOLLAR_PREFIXED}
- Only allow {@link CookieCompliance.Violation#RESERVED_NAMES_NOT_DOLLAR_PREFIXED}
+ * - {@code *,-RESERVED_NAMES_NOT_DOLLAR_PREFIXED}
- Allow all violations, except {@link CookieCompliance.Violation#RESERVED_NAMES_NOT_DOLLAR_PREFIXED}
+ * - {@code RFC2965,RESERVED_NAMES_NOT_DOLLAR_PREFIXED}
- Same as RFC2965, but allows {@link CookieCompliance.Violation#RESERVED_NAMES_NOT_DOLLAR_PREFIXED}
+ *
+ *
+ * @param spec A string describing the compliance
+ * @return the compliance from the string spec
+ */
+ public static CookieCompliance from(String spec)
+ {
+ Set violations;
+ String[] elements = spec.split("\\s*,\\s*");
+ switch (elements[0])
+ {
+ case "0":
+ violations = noneOf(Violation.class);
+ break;
+
+ case "*":
+ violations = allOf(Violation.class);
+ break;
+
+ default:
+ {
+ CookieCompliance mode = valueOf(elements[0]);
+ violations = (mode == null) ? noneOf(Violation.class) : copyOf(mode.getAllowed());
+ break;
+ }
+ }
+
+ for (int i = 1; i < elements.length; i++)
+ {
+ String element = elements[i];
+ boolean exclude = element.startsWith("-");
+ if (exclude)
+ element = element.substring(1);
+ Violation section = Violation.valueOf(element);
+ if (exclude)
+ violations.remove(section);
+ else
+ violations.add(section);
+ }
+
+ CookieCompliance compliance = new CookieCompliance("CUSTOM" + __custom.getAndIncrement(), violations);
+ if (LOG.isDebugEnabled())
+ LOG.debug("CookieCompliance from {}->{}", spec, compliance);
+ return compliance;
+ }
+
private final String _name;
private final Set _violations;
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java
index 9b2bb8ef3d8..ed17725bbc5 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java
@@ -43,13 +43,66 @@ public final class HttpCompliance implements ComplianceViolation.Mode
// the relevant section of the RFC is not strictly adhered to.
public enum Violation implements ComplianceViolation
{
+ /**
+ * The HTTP RFC(s) require that field names are case-insensitive, so for example the fields "{@code Content-Type: text/xml}"
+ * and "{@code content-type: text/xml}" are considered equivalent. Jetty has been optimized to take advantage of this by
+ * looking up field names in a case insensitive cache and will by default provide the standard capitalisation of a field
+ * name rather than create a new string with the actual capitalisation received. However, some applications have been
+ * written to expect a specific capitalisation of field, so deployments of such applications must include this violation
+ * in their {@link HttpCompliance} mode to prevent Jetty altering the case of the fields received. Jetty itself will still
+ * match and handle fields names insensitively and this violation only affects how the names are reported to the application.
+ * There is a small performance and garbage impact of using this mode.
+ */
CASE_SENSITIVE_FIELD_NAME("https://tools.ietf.org/html/rfc7230#section-3.2", "Field name is case-insensitive"),
+
+ /**
+ * The HTTP RFC(s) require that method names are case sensitive, so that "{@code Get}" and "{@code GET}" are considered
+ * different methods. Jetty releases prior to 9.4 used a case insensitive cache to match method names, thus this requirement
+ * was violated. Deployments which wish to retain this legacy violation can include this violation in the
+ * {@link HttpCompliance} mode.
+ */
CASE_INSENSITIVE_METHOD("https://tools.ietf.org/html/rfc7230#section-3.1.1", "Method is case-sensitive"),
+
+ /**
+ * Since RFC 7230, the expectation that HTTP/0.9 is supported has been removed from the specification. If a deployment
+ * wished to accept HTTP/0.9 requests, then it can include this violation in it's {@link HttpCompliance} mode.
+ */
HTTP_0_9("https://tools.ietf.org/html/rfc7230#appendix-A.2", "HTTP/0.9 not supported"),
+
+ /**
+ * Since RFC 7230, the HTTP protocol no longer supports
+ * line folding, which allows a field value to be provided over several lines. Deployments that wish to receive folder
+ * field values may include this violation in their {@link HttpCompliance} mode.
+ */
MULTILINE_FIELD_VALUE("https://tools.ietf.org/html/rfc7230#section-3.2.4", "Line Folding not supported"),
- MULTIPLE_CONTENT_LENGTHS("https://tools.ietf.org/html/rfc7230#section-3.3.1", "Multiple Content-Lengths"),
+
+ /**
+ * Since RFC 7230, the HTTP protocol has required that
+ * a request is invalid if it contains multiple {@code Content-Length} fields or values. The request may be treated
+ * as invalid even if the multiple values are the same. A deployment may include this violation to allow multiple
+ * {@code Content-Length} values to be received, but only if they are identical.
+ */
+ MULTIPLE_CONTENT_LENGTHS("https://tools.ietf.org/html/rfc7230#section-3.3.2", "Multiple Content-Lengths"),
+
+ /**
+ * Since RFC 7230, the HTTP protocol has required that
+ * a request is invalid if it contains both a {@code Transfer-Encoding} field and {@code Content-Length} field.
+ * A deployment may include this violation to allow both fields to be in a received request.
+ */
TRANSFER_ENCODING_WITH_CONTENT_LENGTH("https://tools.ietf.org/html/rfc7230#section-3.3.1", "Transfer-Encoding and Content-Length"),
+
+ /**
+ * Since RFC 7230, the HTTP protocol has required that
+ * a request header field has no white space after the field name and before the ':'.
+ * A deployment may include this violation to allow such fields to be in a received request.
+ */
WHITESPACE_AFTER_FIELD_NAME("https://tools.ietf.org/html/rfc7230#section-3.2.4", "Whitespace not allowed after field name"),
+
+ /**
+ * Prior to RFC 7230, the HTTP protocol allowed a header
+ * line of a single token with neither a colon nor value following, to be interpreted as a field name with no value.
+ * A deployment may include this violation to allow such fields to be in a received request.
+ */
NO_COLON_AFTER_FIELD_NAME("https://tools.ietf.org/html/rfc7230#section-3.2", "Fields must have a Colon");
private final String url;
@@ -80,21 +133,51 @@ public final class HttpCompliance implements ComplianceViolation.Mode
}
}
+ /**
+ * The request attribute which may be set to record any allowed HTTP violations.
+ */
public static final String VIOLATIONS_ATTR = "org.eclipse.jetty.http.compliance.violations";
+ /**
+ * The HttpCompliance mode that supports RFC 7230
+ * with no known violations.
+ */
public static final HttpCompliance RFC7230 = new HttpCompliance("RFC7230", noneOf(Violation.class));
+
+ /**
+ * The HttpCompliance mode that supports RFC 7230
+ * with only the violations that differ from {@link #RFC7230}.
+ */
public static final HttpCompliance RFC2616 = new HttpCompliance("RFC2616", of(Violation.HTTP_0_9, Violation.MULTILINE_FIELD_VALUE));
+
+ /**
+ * A legacy HttpCompliance mode that allows all violations except case-insensitive methods.
+ */
public static final HttpCompliance LEGACY = new HttpCompliance("LEGACY", complementOf(of(Violation.CASE_INSENSITIVE_METHOD)));
+
+ /**
+ * A legacy HttpCompliance mode that supports {@link #RFC2616}, but that also allows: case-insensitive methods;
+ * colons after field names; {@code Transfer-Encoding} with {@code Content-Length} fields; and multiple {@code Content-Length} values.
+ */
public static final HttpCompliance RFC2616_LEGACY = RFC2616.with("RFC2616_LEGACY",
Violation.CASE_INSENSITIVE_METHOD,
Violation.NO_COLON_AFTER_FIELD_NAME,
Violation.TRANSFER_ENCODING_WITH_CONTENT_LENGTH,
Violation.MULTIPLE_CONTENT_LENGTHS);
+
+ /**
+ * A legacy HttpCompliance mode that supports {@link #RFC7230}, but with case-insensitive methods allowed.
+ */
public static final HttpCompliance RFC7230_LEGACY = RFC7230.with("RFC7230_LEGACY", Violation.CASE_INSENSITIVE_METHOD);
private static final List KNOWN_MODES = Arrays.asList(RFC7230, RFC2616, LEGACY, RFC2616_LEGACY, RFC7230_LEGACY);
private static final AtomicInteger __custom = new AtomicInteger();
+ /**
+ * Get a known compliance mode by name.
+ * @param name The name of a known {@link HttpCompliance} mode.
+ * @return The mode matching the name.
+ */
public static HttpCompliance valueOf(String name)
{
for (HttpCompliance compliance : KNOWN_MODES)
@@ -107,7 +190,7 @@ public final class HttpCompliance implements ComplianceViolation.Mode
}
/**
- * Create compliance set from string.
+ * Create compliance mode from a String description.
*
* Format:
*
@@ -121,11 +204,11 @@ public final class HttpCompliance implements ComplianceViolation.Mode
*
*
* The remainder of the list can contain then names of {@link Violation}s to include them in the mode, or prefixed
- * with a '-' to exclude thm from the mode.
+ * with a '-' to exclude them from the mode.
*
*
- * @param spec A string in the format of a comma separated list starting with one of the following strings:
- * @return the compliance from the string spec
+ * @param spec A string describing the compliance
+ * @return the HttpCompliance instance derived from the string description
*/
public static HttpCompliance from(String spec)
{
@@ -186,11 +269,6 @@ public final class HttpCompliance implements ComplianceViolation.Mode
return _name;
}
- /**
- * Get the set of {@link Violation}s allowed by this compliance mode.
- *
- * @return The immutable set of {@link Violation}s allowed by this compliance mode.
- */
@Override
public Set getAllowed()
{
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/UriCompliance.java b/jetty-http/src/main/java/org/eclipse/jetty/http/UriCompliance.java
index 15120cefd74..f9d0504283a 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/UriCompliance.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/UriCompliance.java
@@ -40,7 +40,8 @@ public final class UriCompliance implements ComplianceViolation.Mode
/**
* These are URI compliance "violations", which may be allowed by the compliance mode. These are actual
- * violations of the RFC, as they represent additional requirements in excess of the strict compliance of rfc3986.
+ * violations of the RFC, as they represent additional requirements in excess of the strict compliance of
+ * RFC 3986.
* A compliance mode that contains one or more of these Violations, allows request to violate the corresponding
* additional requirement.
*/
@@ -104,10 +105,9 @@ public final class UriCompliance implements ComplianceViolation.Mode
}
/**
- * The default compliance mode that extends RFC3986 compliance with additional violations to avoid most ambiguous URIs.
- * This mode does allow {@link Violation#AMBIGUOUS_PATH_SEPARATOR}, but disallows
- * {@link Violation#AMBIGUOUS_PATH_PARAMETER}, {@link Violation#AMBIGUOUS_PATH_SEGMENT} and {@link Violation#AMBIGUOUS_PATH_ENCODING}.
- * Ambiguous paths are not allowed by {@link Violation#NON_CANONICAL_AMBIGUOUS_PATHS}.
+ * The default compliance mode that extends RFC3986 compliance with
+ * additional violations to avoid most ambiguous URIs.
+ * This mode does allow {@link Violation#AMBIGUOUS_PATH_SEPARATOR}, but disallows all out {@link Violation}s.
*/
public static final UriCompliance DEFAULT = new UriCompliance("DEFAULT", of(Violation.AMBIGUOUS_PATH_SEPARATOR));
@@ -124,11 +124,18 @@ public final class UriCompliance implements ComplianceViolation.Mode
Violation.UTF16_ENCODINGS));
/**
- * Compliance mode that exactly follows RFC3986, including allowing all additional ambiguous URI Violations,
+ * Compliance mode that exactly follows RFC3986,
+ * including allowing all additional ambiguous URI Violations,
* except {@link Violation#NON_CANONICAL_AMBIGUOUS_PATHS}, thus ambiguous paths are canonicalized for safety.
*/
public static final UriCompliance RFC3986 = new UriCompliance("RFC3986", complementOf(of(Violation.NON_CANONICAL_AMBIGUOUS_PATHS)));
+ /**
+ * Compliance mode that follows RFC3986
+ * plus it does not allow any ambiguous URI {@link Violation}s.
+ */
+ public static final UriCompliance RFC3986_UNAMBIGUOUS = new UriCompliance("RFC3986_UNAMBIGUOUS", noneOf(Violation.class));
+
/**
* Compliance mode that allows all URI Violations, including allowing ambiguous paths in non canonicalized form.
*/
@@ -181,20 +188,20 @@ public final class UriCompliance implements ComplianceViolation.Mode
*
* - 0
- No {@link Violation}s
* - *
- All {@link Violation}s
- * - <name>
- The name of a static instance of {@link UriCompliance} (e.g. {@link UriCompliance#RFC3986}).
+ *
- <name>
- The name of a static instance of UriCompliance (e.g. {@link UriCompliance#RFC3986}).
*
*
* The remainder of the list can contain then names of {@link Violation}s to include them in the mode, or prefixed
- * with a '-' to exclude thm from the mode. Examples are:
+ * with a '-' to exclude them from the mode. Examples are:
*
*
- * 0,AMBIGUOUS_PATH_PARAMETER
- Only allow {@link Violation#AMBIGUOUS_PATH_PARAMETER}
- * *,-AMBIGUOUS_PATH_PARAMETER
- Only all except {@link Violation#AMBIGUOUS_PATH_PARAMETER}
- * RFC3986,AMBIGUOUS_PATH_PARAMETER
- Same as RFC3986 plus {@link Violation#AMBIGUOUS_PATH_PARAMETER}
+ * - {@code 0,AMBIGUOUS_PATH_PARAMETER}
- Only allow {@link Violation#AMBIGUOUS_PATH_PARAMETER}
+ * - {@code *,-AMBIGUOUS_PATH_PARAMETER}
- Only all except {@link Violation#AMBIGUOUS_PATH_PARAMETER}
+ * - {@code RFC3986,AMBIGUOUS_PATH_PARAMETER}
- Same as RFC3986 plus {@link Violation#AMBIGUOUS_PATH_PARAMETER}
*
*
- * @param spec A string in the format of a comma separated list starting with one of the following strings:
- * @return the compliance from the string spec
+ * @param spec A string describing the compliance
+ * @return the UriCompliance instance derived from the string description
*/
public static UriCompliance from(String spec)
{
diff --git a/jetty-server/src/main/config/etc/jetty.xml b/jetty-server/src/main/config/etc/jetty.xml
index 42dcefc69ad..b2d16df274c 100644
--- a/jetty-server/src/main/config/etc/jetty.xml
+++ b/jetty-server/src/main/config/etc/jetty.xml
@@ -76,8 +76,8 @@
-
-
+
+
diff --git a/jetty-server/src/main/config/modules/server.mod b/jetty-server/src/main/config/modules/server.mod
index a18517fbb52..8748e6526b0 100644
--- a/jetty-server/src/main/config/modules/server.mod
+++ b/jetty-server/src/main/config/modules/server.mod
@@ -25,6 +25,7 @@ lib/jetty-io-${jetty.version}.jar
etc/jetty.xml
[ini-template]
+# tag::documentation-http-config[]
### Common HTTP configuration
## Scheme to use to build URIs for secure redirects
# jetty.httpConfig.secureScheme=https
@@ -59,10 +60,19 @@ etc/jetty.xml
## Maximum number of error dispatches to prevent looping
# jetty.httpConfig.maxErrorDispatches=10
+## Relative Redirect Locations allowed
+# jetty.httpConfig.relativeRedirectAllowed=false
+
+## Whether to use direct ByteBuffers for reading or writing
+# jetty.httpConfig.useInputDirectByteBuffers=true
+# jetty.httpConfig.useOutputDirectByteBuffers=true
+# end::documentation-http-config[]
+
+# tag::documentation-server-compliance[]
## HTTP Compliance: RFC7230, RFC7230_LEGACY, RFC2616, RFC2616_LEGACY, LEGACY
# jetty.httpConfig.compliance=RFC7230
-## URI Compliance: DEFAULT, LEGACY, RFC3986, UNSAFE
+## URI Compliance: DEFAULT, LEGACY, RFC3986, RFC3986_UNAMBIGUOUS, UNSAFE
# jetty.httpConfig.uriCompliance=DEFAULT
## Cookie compliance mode for parsing request Cookie headers: RFC2965, RFC6265
@@ -70,14 +80,9 @@ etc/jetty.xml
## Cookie compliance mode for generating response Set-Cookie: RFC2965, RFC6265
# jetty.httpConfig.responseCookieCompliance=RFC6265
+# end::documentation-server-compliance[]
-## Relative Redirect Locations allowed
-# jetty.httpConfig.relativeRedirectAllowed=false
-
-## Whether to use direct ByteBuffers for reading or writing
-# jetty.httpConfig.useInputDirectByteBuffers=true
-# jetty.httpConfig.useOutputDirectByteBuffers=true
-
+# tag::documentation-server-config[]
### Server configuration
## Whether ctrl+c on the console gracefully stops the Jetty server
# jetty.server.stopAtShutdown=true
@@ -90,9 +95,16 @@ etc/jetty.xml
## Dump the state of the Jetty server, components, and webapps before shutdown
# jetty.server.dumpBeforeStop=false
+# end::documentation-server-config[]
-## Scheduler Configuration
+# tag::documentation-scheduler-config[]
+### Server Scheduler Configuration
+## The scheduler thread name, defaults to "Scheduler-{hashCode()}" if blank.
# jetty.scheduler.name=
-# jetty.scheduler.deamon=false
-# jetty.scheduler.threads=-1
+## Whether the server scheduler threads are daemon.
+# jetty.scheduler.daemon=false
+
+## The number of server scheduler threads.
+# jetty.scheduler.threads=1
+# end::documentation-scheduler-config[]
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
index 6c00316d07d..a1e95370671 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
@@ -1750,6 +1750,8 @@ public class RequestTest
"\r\n";
_connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setUriCompliance(UriCompliance.DEFAULT);
assertThat(_connector.getResponse(request), startsWith("HTTP/1.1 400"));
+ _connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setUriCompliance(UriCompliance.RFC3986_UNAMBIGUOUS);
+ assertThat(_connector.getResponse(request), startsWith("HTTP/1.1 400"));
_connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setUriCompliance(UriCompliance.LEGACY);
assertThat(_connector.getResponse(request), startsWith("HTTP/1.1 200"));
_connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setUriCompliance(UriCompliance.RFC3986);