Merge remote-tracking branch 'origin/jetty-9.4.x' into jetty-10.0.x
Signed-off-by: gregw <gregw@webtide.com>
This commit is contained in:
commit
c4dbf9748e
|
@ -20,6 +20,9 @@ import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.unmodifiableSet;
|
import static java.util.Collections.unmodifiableSet;
|
||||||
import static java.util.EnumSet.allOf;
|
import static java.util.EnumSet.allOf;
|
||||||
|
@ -34,6 +37,7 @@ import static java.util.EnumSet.of;
|
||||||
*/
|
*/
|
||||||
public final class HttpCompliance implements ComplianceViolation.Mode
|
public final class HttpCompliance implements ComplianceViolation.Mode
|
||||||
{
|
{
|
||||||
|
protected static final Logger LOG = LoggerFactory.getLogger(HttpCompliance.class);
|
||||||
|
|
||||||
// These are compliance violations, which may optionally be allowed by the compliance mode, which mean that
|
// These are compliance violations, which may optionally be allowed by the compliance mode, which mean that
|
||||||
// the relevant section of the RFC is not strictly adhered to.
|
// the relevant section of the RFC is not strictly adhered to.
|
||||||
|
@ -46,8 +50,7 @@ public final class HttpCompliance implements ComplianceViolation.Mode
|
||||||
MULTIPLE_CONTENT_LENGTHS("https://tools.ietf.org/html/rfc7230#section-3.3.1", "Multiple Content-Lengths"),
|
MULTIPLE_CONTENT_LENGTHS("https://tools.ietf.org/html/rfc7230#section-3.3.1", "Multiple Content-Lengths"),
|
||||||
TRANSFER_ENCODING_WITH_CONTENT_LENGTH("https://tools.ietf.org/html/rfc7230#section-3.3.1", "Transfer-Encoding and Content-Length"),
|
TRANSFER_ENCODING_WITH_CONTENT_LENGTH("https://tools.ietf.org/html/rfc7230#section-3.3.1", "Transfer-Encoding and Content-Length"),
|
||||||
WHITESPACE_AFTER_FIELD_NAME("https://tools.ietf.org/html/rfc7230#section-3.2.4", "Whitespace not allowed after field name"),
|
WHITESPACE_AFTER_FIELD_NAME("https://tools.ietf.org/html/rfc7230#section-3.2.4", "Whitespace not allowed after field name"),
|
||||||
NO_COLON_AFTER_FIELD_NAME("https://tools.ietf.org/html/rfc7230#section-3.2", "Fields must have a Colon"),
|
NO_COLON_AFTER_FIELD_NAME("https://tools.ietf.org/html/rfc7230#section-3.2", "Fields must have a Colon");
|
||||||
AMBIGUOUS_PATH_SEGMENTS("https://tools.ietf.org/html/rfc3986#section-3.3", "Ambiguous URI path segments");
|
|
||||||
|
|
||||||
private final String url;
|
private final String url;
|
||||||
private final String description;
|
private final String description;
|
||||||
|
@ -81,14 +84,13 @@ public final class HttpCompliance implements ComplianceViolation.Mode
|
||||||
|
|
||||||
public static final HttpCompliance RFC7230 = new HttpCompliance("RFC7230", noneOf(Violation.class));
|
public static final HttpCompliance RFC7230 = new HttpCompliance("RFC7230", noneOf(Violation.class));
|
||||||
public static final HttpCompliance RFC2616 = new HttpCompliance("RFC2616", of(Violation.HTTP_0_9, Violation.MULTILINE_FIELD_VALUE));
|
public static final HttpCompliance RFC2616 = new HttpCompliance("RFC2616", of(Violation.HTTP_0_9, Violation.MULTILINE_FIELD_VALUE));
|
||||||
public static final HttpCompliance LEGACY = new HttpCompliance("LEGACY", complementOf(of(Violation.CASE_INSENSITIVE_METHOD, Violation.AMBIGUOUS_PATH_SEGMENTS)));
|
public static final HttpCompliance LEGACY = new HttpCompliance("LEGACY", complementOf(of(Violation.CASE_INSENSITIVE_METHOD)));
|
||||||
public static final HttpCompliance RFC2616_LEGACY = RFC2616.with("RFC2616_LEGACY",
|
public static final HttpCompliance RFC2616_LEGACY = RFC2616.with("RFC2616_LEGACY",
|
||||||
Violation.CASE_INSENSITIVE_METHOD,
|
Violation.CASE_INSENSITIVE_METHOD,
|
||||||
Violation.NO_COLON_AFTER_FIELD_NAME,
|
Violation.NO_COLON_AFTER_FIELD_NAME,
|
||||||
Violation.TRANSFER_ENCODING_WITH_CONTENT_LENGTH,
|
Violation.TRANSFER_ENCODING_WITH_CONTENT_LENGTH,
|
||||||
Violation.MULTIPLE_CONTENT_LENGTHS,
|
Violation.MULTIPLE_CONTENT_LENGTHS);
|
||||||
Violation.AMBIGUOUS_PATH_SEGMENTS);
|
public static final HttpCompliance RFC7230_LEGACY = RFC7230.with("RFC7230_LEGACY", Violation.CASE_INSENSITIVE_METHOD);
|
||||||
public static final HttpCompliance RFC7230_LEGACY = RFC7230.with("RFC7230_LEGACY", Violation.CASE_INSENSITIVE_METHOD, Violation.AMBIGUOUS_PATH_SEGMENTS);
|
|
||||||
|
|
||||||
private static final List<HttpCompliance> KNOWN_MODES = Arrays.asList(RFC7230, RFC2616, LEGACY, RFC2616_LEGACY, RFC7230_LEGACY);
|
private static final List<HttpCompliance> KNOWN_MODES = Arrays.asList(RFC7230, RFC2616, LEGACY, RFC2616_LEGACY, RFC7230_LEGACY);
|
||||||
private static final AtomicInteger __custom = new AtomicInteger();
|
private static final AtomicInteger __custom = new AtomicInteger();
|
||||||
|
@ -100,6 +102,7 @@ public final class HttpCompliance implements ComplianceViolation.Mode
|
||||||
if (compliance.getName().equals(name))
|
if (compliance.getName().equals(name))
|
||||||
return compliance;
|
return compliance;
|
||||||
}
|
}
|
||||||
|
LOG.warn("Unknown HttpCompliance mode {}", name);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,10 +144,7 @@ public final class HttpCompliance implements ComplianceViolation.Mode
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
HttpCompliance mode = HttpCompliance.valueOf(elements[0]);
|
HttpCompliance mode = HttpCompliance.valueOf(elements[0]);
|
||||||
if (mode == null)
|
sections = (mode == null) ? noneOf(HttpCompliance.Violation.class) : copyOf(mode.getAllowed());
|
||||||
sections = noneOf(Violation.class);
|
|
||||||
else
|
|
||||||
sections = copyOf(mode.getAllowed());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ package org.eclipse.jetty.http;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.HostPort;
|
import org.eclipse.jetty.util.HostPort;
|
||||||
import org.eclipse.jetty.util.Index;
|
import org.eclipse.jetty.util.Index;
|
||||||
|
@ -47,6 +48,12 @@ import org.eclipse.jetty.util.UrlEncoded;
|
||||||
*/
|
*/
|
||||||
public interface HttpURI
|
public interface HttpURI
|
||||||
{
|
{
|
||||||
|
enum Ambiguous
|
||||||
|
{
|
||||||
|
SEGMENT,
|
||||||
|
SEPARATOR
|
||||||
|
}
|
||||||
|
|
||||||
static Mutable build()
|
static Mutable build()
|
||||||
{
|
{
|
||||||
return new Mutable();
|
return new Mutable();
|
||||||
|
@ -131,8 +138,21 @@ public interface HttpURI
|
||||||
|
|
||||||
boolean isAbsolute();
|
boolean isAbsolute();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return True if the URI has either an {@link #hasAmbiguousSegment()} or {@link #hasAmbiguousSeparator()}.
|
||||||
|
*/
|
||||||
|
boolean isAmbiguous();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return True if the URI has a possibly ambiguous segment like '..;' or '%2e%2e'
|
||||||
|
*/
|
||||||
boolean hasAmbiguousSegment();
|
boolean hasAmbiguousSegment();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return True if the URI has a possibly ambiguous separator of %2f
|
||||||
|
*/
|
||||||
|
boolean hasAmbiguousSeparator();
|
||||||
|
|
||||||
default URI toURI()
|
default URI toURI()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -158,7 +178,7 @@ public interface HttpURI
|
||||||
private final String _fragment;
|
private final String _fragment;
|
||||||
private String _uri;
|
private String _uri;
|
||||||
private String _decodedPath;
|
private String _decodedPath;
|
||||||
private boolean _ambiguousSegment;
|
private final EnumSet<Mutable.Ambiguous> _ambiguous = EnumSet.noneOf(Mutable.Ambiguous.class);
|
||||||
|
|
||||||
private Immutable(Mutable builder)
|
private Immutable(Mutable builder)
|
||||||
{
|
{
|
||||||
|
@ -172,7 +192,7 @@ public interface HttpURI
|
||||||
_fragment = builder._fragment;
|
_fragment = builder._fragment;
|
||||||
_uri = builder._uri;
|
_uri = builder._uri;
|
||||||
_decodedPath = builder._decodedPath;
|
_decodedPath = builder._decodedPath;
|
||||||
_ambiguousSegment = builder._ambiguousSegment;
|
_ambiguous.addAll(builder._ambiguous);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Immutable(String uri)
|
private Immutable(String uri)
|
||||||
|
@ -336,10 +356,22 @@ public interface HttpURI
|
||||||
return !StringUtil.isEmpty(_scheme);
|
return !StringUtil.isEmpty(_scheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAmbiguous()
|
||||||
|
{
|
||||||
|
return !_ambiguous.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasAmbiguousSegment()
|
public boolean hasAmbiguousSegment()
|
||||||
{
|
{
|
||||||
return _ambiguousSegment;
|
return _ambiguous.contains(Mutable.Ambiguous.SEGMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasAmbiguousSeparator()
|
||||||
|
{
|
||||||
|
return _ambiguous.contains(Mutable.Ambiguous.SEPARATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -411,7 +443,7 @@ public interface HttpURI
|
||||||
private String _fragment;
|
private String _fragment;
|
||||||
private String _uri;
|
private String _uri;
|
||||||
private String _decodedPath;
|
private String _decodedPath;
|
||||||
private boolean _ambiguousSegment;
|
private final EnumSet<Ambiguous> _ambiguous = EnumSet.noneOf(Ambiguous.class);
|
||||||
|
|
||||||
private Mutable()
|
private Mutable()
|
||||||
{
|
{
|
||||||
|
@ -535,7 +567,7 @@ public interface HttpURI
|
||||||
_fragment = null;
|
_fragment = null;
|
||||||
_uri = null;
|
_uri = null;
|
||||||
_decodedPath = null;
|
_decodedPath = null;
|
||||||
_ambiguousSegment = false;
|
_ambiguous.clear();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -659,10 +691,22 @@ public interface HttpURI
|
||||||
return _scheme != null && !_scheme.isEmpty();
|
return _scheme != null && !_scheme.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAmbiguous()
|
||||||
|
{
|
||||||
|
return !_ambiguous.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasAmbiguousSegment()
|
public boolean hasAmbiguousSegment()
|
||||||
{
|
{
|
||||||
return _ambiguousSegment;
|
return _ambiguous.contains(Mutable.Ambiguous.SEGMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasAmbiguousSeparator()
|
||||||
|
{
|
||||||
|
return _ambiguous.contains(Mutable.Ambiguous.SEPARATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mutable normalize()
|
public Mutable normalize()
|
||||||
|
@ -766,7 +810,10 @@ public interface HttpURI
|
||||||
_query = uri.getQuery();
|
_query = uri.getQuery();
|
||||||
_uri = null;
|
_uri = null;
|
||||||
_decodedPath = uri.getDecodedPath();
|
_decodedPath = uri.getDecodedPath();
|
||||||
_ambiguousSegment = uri.hasAmbiguousSegment();
|
if (uri.hasAmbiguousSeparator())
|
||||||
|
_ambiguous.add(Ambiguous.SEPARATOR);
|
||||||
|
if (uri.hasAmbiguousSegment())
|
||||||
|
_ambiguous.add(Ambiguous.SEGMENT);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1062,7 +1109,8 @@ public interface HttpURI
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
case 'F':
|
case 'F':
|
||||||
_ambiguousSegment |= (escapedSlash == 2);
|
if (escapedSlash == 2)
|
||||||
|
_ambiguous.add(Ambiguous.SEPARATOR);
|
||||||
escapedSlash = 0;
|
escapedSlash = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1183,17 +1231,19 @@ public interface HttpURI
|
||||||
*
|
*
|
||||||
* An ambiguous path segment is one that is perhaps technically legal, but is considered undesirable to handle
|
* An ambiguous path segment is one that is perhaps technically legal, but is considered undesirable to handle
|
||||||
* due to possible ambiguity. Examples include segments like '..;', '%2e', '%2e%2e' etc.
|
* due to possible ambiguity. Examples include segments like '..;', '%2e', '%2e%2e' etc.
|
||||||
|
*
|
||||||
* @param uri The URI string
|
* @param uri The URI string
|
||||||
* @param segment The inclusive starting index of the segment (excluding any '/')
|
* @param segment The inclusive starting index of the segment (excluding any '/')
|
||||||
* @param end The exclusive end index of the segment
|
* @param end The exclusive end index of the segment
|
||||||
*/
|
*/
|
||||||
private void checkSegment(String uri, int segment, int end, boolean param)
|
private void checkSegment(String uri, int segment, int end, boolean param)
|
||||||
{
|
{
|
||||||
if (!_ambiguousSegment)
|
if (!_ambiguous.contains(Ambiguous.SEGMENT))
|
||||||
{
|
{
|
||||||
Boolean ambiguous = __ambiguousSegments.get(uri, segment, end - segment);
|
Boolean ambiguous = __ambiguousSegments.get(uri, segment, end - segment);
|
||||||
_ambiguousSegment |= ambiguous == Boolean.TRUE || (param && ambiguous == Boolean.FALSE);
|
if (ambiguous == Boolean.TRUE || (param && ambiguous == Boolean.FALSE))
|
||||||
|
_ambiguous.add(Ambiguous.SEGMENT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,238 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||||
|
//
|
||||||
|
// This program and the accompanying materials are made available under the
|
||||||
|
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||||
|
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||||
|
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.http;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.Collections.unmodifiableSet;
|
||||||
|
import static java.util.EnumSet.allOf;
|
||||||
|
import static java.util.EnumSet.noneOf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URI compliance modes for Jetty request handling.
|
||||||
|
* A Compliance mode consists of a set of {@link Violation}s which are applied
|
||||||
|
* when the mode is enabled.
|
||||||
|
*/
|
||||||
|
public final class UriCompliance implements ComplianceViolation.Mode
|
||||||
|
{
|
||||||
|
protected static final Logger LOG = LoggerFactory.getLogger(UriCompliance.class);
|
||||||
|
|
||||||
|
// These are compliance violations, which may optionally be allowed by the compliance mode, which mean that
|
||||||
|
// the relevant section of the RFC is not strictly adhered to.
|
||||||
|
public enum Violation implements ComplianceViolation
|
||||||
|
{
|
||||||
|
AMBIGUOUS_PATH_SEGMENT("https://tools.ietf.org/html/rfc3986#section-3.3", "Ambiguous URI path segment"),
|
||||||
|
AMBIGUOUS_PATH_SEPARATOR("https://tools.ietf.org/html/rfc3986#section-3.3", "Ambiguous URI path separator");
|
||||||
|
|
||||||
|
private final String url;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
Violation(String url, String description)
|
||||||
|
{
|
||||||
|
this.url = url;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return name();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getURL()
|
||||||
|
{
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final UriCompliance SAFE = new UriCompliance("SAFE", noneOf(Violation.class));
|
||||||
|
public static final UriCompliance STRICT = new UriCompliance("STRICT", allOf(Violation.class));
|
||||||
|
private static final List<UriCompliance> KNOWN_MODES = Arrays.asList(SAFE, STRICT);
|
||||||
|
private static final AtomicInteger __custom = new AtomicInteger();
|
||||||
|
|
||||||
|
public static UriCompliance valueOf(String name)
|
||||||
|
{
|
||||||
|
for (UriCompliance compliance : KNOWN_MODES)
|
||||||
|
{
|
||||||
|
if (compliance.getName().equals(name))
|
||||||
|
return compliance;
|
||||||
|
}
|
||||||
|
LOG.warn("Unknown UriCompliance mode {}", name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create compliance set from string.
|
||||||
|
* <p>
|
||||||
|
* Format:
|
||||||
|
* </p>
|
||||||
|
* <dl>
|
||||||
|
* <dt>0</dt><dd>No {@link Violation}s</dd>
|
||||||
|
* <dt>*</dt><dd>All {@link Violation}s</dd>
|
||||||
|
* <dt>RFC2616</dt><dd>The set of {@link Violation}s application to https://tools.ietf.org/html/rfc2616,
|
||||||
|
* but not https://tools.ietf.org/html/rfc7230</dd>
|
||||||
|
* <dt>RFC7230</dt><dd>The set of {@link Violation}s application to https://tools.ietf.org/html/rfc7230</dd>
|
||||||
|
* <dt>name</dt><dd>Any of the known modes defined in {@link UriCompliance#KNOWN_MODES}</dd>
|
||||||
|
* </dl>
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
public static UriCompliance from(String spec)
|
||||||
|
{
|
||||||
|
Set<Violation> sections;
|
||||||
|
String[] elements = spec.split("\\s*,\\s*");
|
||||||
|
switch (elements[0])
|
||||||
|
{
|
||||||
|
case "0":
|
||||||
|
sections = noneOf(Violation.class);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "*":
|
||||||
|
sections = allOf(Violation.class);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
UriCompliance mode = UriCompliance.valueOf(elements[0]);
|
||||||
|
sections = (mode == null) ? noneOf(Violation.class) : copyOf(mode.getAllowed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
sections.remove(section);
|
||||||
|
else
|
||||||
|
sections.add(section);
|
||||||
|
}
|
||||||
|
|
||||||
|
UriCompliance compliance = new UriCompliance("CUSTOM" + __custom.getAndIncrement(), sections);
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("UriCompliance from {}->{}", spec, compliance);
|
||||||
|
return compliance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String _name;
|
||||||
|
private final Set<Violation> _violations;
|
||||||
|
|
||||||
|
private UriCompliance(String name, Set<Violation> violations)
|
||||||
|
{
|
||||||
|
Objects.requireNonNull(violations);
|
||||||
|
_name = name;
|
||||||
|
_violations = unmodifiableSet(violations.isEmpty() ? noneOf(Violation.class) : copyOf(violations));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean allows(ComplianceViolation violation)
|
||||||
|
{
|
||||||
|
return violation instanceof Violation && _violations.contains(violation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
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<Violation> getAllowed()
|
||||||
|
{
|
||||||
|
return _violations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Violation> getKnown()
|
||||||
|
{
|
||||||
|
return EnumSet.allOf(Violation.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new UriCompliance mode that includes the passed {@link Violation}s.
|
||||||
|
*
|
||||||
|
* @param name The name of the new mode
|
||||||
|
* @param violations The violations to include
|
||||||
|
* @return A new {@link UriCompliance} mode.
|
||||||
|
*/
|
||||||
|
public UriCompliance with(String name, Violation... violations)
|
||||||
|
{
|
||||||
|
Set<Violation> union = _violations.isEmpty() ? EnumSet.noneOf(Violation.class) : copyOf(_violations);
|
||||||
|
union.addAll(copyOf(violations));
|
||||||
|
return new UriCompliance(name, union);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new UriCompliance mode that excludes the passed {@link Violation}s.
|
||||||
|
*
|
||||||
|
* @param name The name of the new mode
|
||||||
|
* @param violations The violations to exclude
|
||||||
|
* @return A new {@link UriCompliance} mode.
|
||||||
|
*/
|
||||||
|
public UriCompliance without(String name, Violation... violations)
|
||||||
|
{
|
||||||
|
Set<Violation> remainder = _violations.isEmpty() ? EnumSet.noneOf(Violation.class) : copyOf(_violations);
|
||||||
|
remainder.removeAll(copyOf(violations));
|
||||||
|
return new UriCompliance(name, remainder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return String.format("%s%s", _name, _violations);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<Violation> copyOf(Violation[] violations)
|
||||||
|
{
|
||||||
|
if (violations == null || violations.length == 0)
|
||||||
|
return EnumSet.noneOf(Violation.class);
|
||||||
|
return EnumSet.copyOf(asList(violations));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<Violation> copyOf(Set<Violation> violations)
|
||||||
|
{
|
||||||
|
if (violations == null || violations.isEmpty())
|
||||||
|
return EnumSet.noneOf(Violation.class);
|
||||||
|
return EnumSet.copyOf(violations);
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,13 +25,14 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.nullValue;
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
public class HttpURITest
|
public class HttpURITest
|
||||||
{
|
{
|
||||||
@Test
|
@Test
|
||||||
public void testBuilder() throws Exception
|
public void testBuilder()
|
||||||
{
|
{
|
||||||
HttpURI uri = HttpURI.build()
|
HttpURI uri = HttpURI.build()
|
||||||
.scheme("http")
|
.scheme("http")
|
||||||
|
@ -76,7 +77,7 @@ public class HttpURITest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExample() throws Exception
|
public void testExample()
|
||||||
{
|
{
|
||||||
HttpURI uri = HttpURI.from("http://user:password@host:8888/ignored/../p%61th;ignored/info;param?query=value#fragment");
|
HttpURI uri = HttpURI.from("http://user:password@host:8888/ignored/../p%61th;ignored/info;param?query=value#fragment");
|
||||||
|
|
||||||
|
@ -93,7 +94,7 @@ public class HttpURITest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInvalidAddress() throws Exception
|
public void testInvalidAddress()
|
||||||
{
|
{
|
||||||
assertInvalidURI("http://[ffff::1:8080/", "Invalid URL; no closing ']' -- should throw exception");
|
assertInvalidURI("http://[ffff::1:8080/", "Invalid URL; no closing ']' -- should throw exception");
|
||||||
assertInvalidURI("**", "only '*', not '**'");
|
assertInvalidURI("**", "only '*', not '**'");
|
||||||
|
@ -163,19 +164,19 @@ public class HttpURITest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAt() throws Exception
|
public void testAt()
|
||||||
{
|
{
|
||||||
HttpURI uri = HttpURI.from("/@foo/bar");
|
HttpURI uri = HttpURI.from("/@foo/bar");
|
||||||
assertEquals("/@foo/bar", uri.getPath());
|
assertEquals("/@foo/bar", uri.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParams() throws Exception
|
public void testParams()
|
||||||
{
|
{
|
||||||
HttpURI uri = HttpURI.from("/foo/bar");
|
HttpURI uri = HttpURI.from("/foo/bar");
|
||||||
assertEquals("/foo/bar", uri.getPath());
|
assertEquals("/foo/bar", uri.getPath());
|
||||||
assertEquals("/foo/bar", uri.getDecodedPath());
|
assertEquals("/foo/bar", uri.getDecodedPath());
|
||||||
assertEquals(null, uri.getParam());
|
assertNull(uri.getParam());
|
||||||
|
|
||||||
uri = HttpURI.from("/foo/bar;jsessionid=12345");
|
uri = HttpURI.from("/foo/bar;jsessionid=12345");
|
||||||
assertEquals("/foo/bar;jsessionid=12345", uri.getPath());
|
assertEquals("/foo/bar;jsessionid=12345", uri.getPath());
|
||||||
|
@ -227,7 +228,7 @@ public class HttpURITest
|
||||||
assertEquals("/f%30%30;p0/bar;p1;p2", uri.getPath());
|
assertEquals("/f%30%30;p0/bar;p1;p2", uri.getPath());
|
||||||
assertEquals("/f00/bar", uri.getDecodedPath());
|
assertEquals("/f00/bar", uri.getDecodedPath());
|
||||||
assertEquals("p2", uri.getParam());
|
assertEquals("p2", uri.getParam());
|
||||||
assertEquals(null, uri.getQuery());
|
assertNull(uri.getQuery());
|
||||||
|
|
||||||
uri = builder.pathQuery("/f%30%30;p0/bar;p1;p2?name=value").asImmutable();
|
uri = builder.pathQuery("/f%30%30;p0/bar;p1;p2?name=value").asImmutable();
|
||||||
assertEquals("http://host:8888/f%30%30;p0/bar;p1;p2?name=value", uri.toString());
|
assertEquals("http://host:8888/f%30%30;p0/bar;p1;p2?name=value", uri.toString());
|
||||||
|
@ -241,7 +242,7 @@ public class HttpURITest
|
||||||
assertEquals("/f%30%30;p0/bar;p1;p2", uri.getPath());
|
assertEquals("/f%30%30;p0/bar;p1;p2", uri.getPath());
|
||||||
assertEquals("/f00/bar", uri.getDecodedPath());
|
assertEquals("/f00/bar", uri.getDecodedPath());
|
||||||
assertEquals("p2", uri.getParam());
|
assertEquals("p2", uri.getParam());
|
||||||
assertEquals(null, uri.getQuery());
|
assertNull(uri.getQuery());
|
||||||
|
|
||||||
uri = builder.query("other=123456").asImmutable();
|
uri = builder.query("other=123456").asImmutable();
|
||||||
assertEquals("http://host:8888/f%30%30;p0/bar;p1;p2?other=123456", uri.toString());
|
assertEquals("http://host:8888/f%30%30;p0/bar;p1;p2?other=123456", uri.toString());
|
||||||
|
@ -252,7 +253,7 @@ public class HttpURITest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSchemeAndOrAuthority() throws Exception
|
public void testSchemeAndOrAuthority()
|
||||||
{
|
{
|
||||||
HttpURI.Mutable builder = HttpURI.build("/path/info");
|
HttpURI.Mutable builder = HttpURI.build("/path/info");
|
||||||
HttpURI uri = builder.asImmutable();
|
HttpURI uri = builder.asImmutable();
|
||||||
|
@ -272,7 +273,7 @@ public class HttpURITest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBasicAuthCredentials() throws Exception
|
public void testBasicAuthCredentials()
|
||||||
{
|
{
|
||||||
HttpURI uri = HttpURI.from("http://user:password@example.com:8888/blah");
|
HttpURI uri = HttpURI.from("http://user:password@example.com:8888/blah");
|
||||||
assertEquals("http://user:password@example.com:8888/blah", uri.toString());
|
assertEquals("http://user:password@example.com:8888/blah", uri.toString());
|
||||||
|
@ -281,7 +282,7 @@ public class HttpURITest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCanonicalDecoded() throws Exception
|
public void testCanonicalDecoded()
|
||||||
{
|
{
|
||||||
HttpURI uri = HttpURI.from("/path/.info");
|
HttpURI uri = HttpURI.from("/path/.info");
|
||||||
assertEquals("/path/.info", uri.getDecodedPath());
|
assertEquals("/path/.info", uri.getDecodedPath());
|
||||||
|
@ -319,74 +320,79 @@ public class HttpURITest
|
||||||
return Arrays.stream(new Object[][]
|
return Arrays.stream(new Object[][]
|
||||||
{
|
{
|
||||||
// Simple path example
|
// Simple path example
|
||||||
{"http://host/path/info", "/path/info", false},
|
{"http://host/path/info", "/path/info", false, false},
|
||||||
{"//host/path/info", "/path/info", false},
|
{"//host/path/info", "/path/info", false, false},
|
||||||
{"/path/info", "/path/info", false},
|
{"/path/info", "/path/info", false, false},
|
||||||
|
|
||||||
// legal non ambiguous relative paths
|
// legal non ambiguous relative paths
|
||||||
{"http://host/../path/info", null, false},
|
{"http://host/../path/info", null, false, false},
|
||||||
{"http://host/path/../info", "/info", false},
|
{"http://host/path/../info", "/info", false, false},
|
||||||
{"http://host/path/./info", "/path/info", false},
|
{"http://host/path/./info", "/path/info", false, false},
|
||||||
{"//host/path/../info", "/info", false},
|
{"//host/path/../info", "/info", false, false},
|
||||||
{"//host/path/./info", "/path/info", false},
|
{"//host/path/./info", "/path/info", false, false},
|
||||||
{"/path/../info", "/info", false},
|
{"/path/../info", "/info", false, false},
|
||||||
{"/path/./info", "/path/info", false},
|
{"/path/./info", "/path/info", false, false},
|
||||||
{"path/../info", "info", false},
|
{"path/../info", "info", false, false},
|
||||||
{"path/./info", "path/info", false},
|
{"path/./info", "path/info", false, false},
|
||||||
|
|
||||||
// illegal paths
|
// illegal paths
|
||||||
{"//host/../path/info", null, false},
|
{"//host/../path/info", null, false, false},
|
||||||
{"/../path/info", null, false},
|
{"/../path/info", null, false, false},
|
||||||
{"../path/info", null, false},
|
{"../path/info", null, false, false},
|
||||||
{"/path/%XX/info", null, false},
|
{"/path/%XX/info", null, false, false},
|
||||||
{"/path/%2/F/info", null, false},
|
{"/path/%2/F/info", null, false, false},
|
||||||
|
|
||||||
// ambiguous dot encodings or parameter inclusions
|
// ambiguous dot encodings or parameter inclusions
|
||||||
{"scheme://host/path/%2e/info", "/path/./info", true},
|
{"scheme://host/path/%2e/info", "/path/./info", true, false},
|
||||||
{"scheme:/path/%2e/info", "/path/./info", true},
|
{"scheme:/path/%2e/info", "/path/./info", true, false},
|
||||||
{"/path/%2e/info", "/path/./info", true},
|
{"/path/%2e/info", "/path/./info", true, false},
|
||||||
{"path/%2e/info/", "path/./info/", true},
|
{"path/%2e/info/", "path/./info/", true, false},
|
||||||
{"/path/%2e%2e/info", "/path/../info", true},
|
{"/path/%2e%2e/info", "/path/../info", true, false},
|
||||||
{"/path/%2e%2e;/info", "/path/../info", true},
|
{"/path/%2e%2e;/info", "/path/../info", true, false},
|
||||||
{"/path/%2e%2e;param/info", "/path/../info", true},
|
{"/path/%2e%2e;param/info", "/path/../info", true, false},
|
||||||
{"/path/%2e%2e;param;other/info;other", "/path/../info", true},
|
{"/path/%2e%2e;param;other/info;other", "/path/../info", true, false},
|
||||||
{"/path/.;/info", "/path/./info", true},
|
{"/path/.;/info", "/path/./info", true, false},
|
||||||
{"/path/.;param/info", "/path/./info", true},
|
{"/path/.;param/info", "/path/./info", true, false},
|
||||||
{"/path/..;/info", "/path/../info", true},
|
{"/path/..;/info", "/path/../info", true, false},
|
||||||
{"/path/..;param/info", "/path/../info", true},
|
{"/path/..;param/info", "/path/../info", true, false},
|
||||||
{"%2e/info", "./info", true},
|
{"%2e/info", "./info", true, false},
|
||||||
{"%2e%2e/info", "../info", true},
|
{"%2e%2e/info", "../info", true, false},
|
||||||
{"%2e%2e;/info", "../info", true},
|
{"%2e%2e;/info", "../info", true, false},
|
||||||
{".;/info", "./info", true},
|
{".;/info", "./info", true, false},
|
||||||
{".;param/info", "./info", true},
|
{".;param/info", "./info", true, false},
|
||||||
{"..;/info", "../info", true},
|
{"..;/info", "../info", true, false},
|
||||||
{"..;param/info", "../info", true},
|
{"..;param/info", "../info", true, false},
|
||||||
{"%2e", ".", true},
|
{"%2e", ".", true, false},
|
||||||
{"%2e.", "..", true},
|
{"%2e.", "..", true, false},
|
||||||
{".%2e", "..", true},
|
{".%2e", "..", true, false},
|
||||||
{"%2e%2e", "..", true},
|
{"%2e%2e", "..", true, false},
|
||||||
|
|
||||||
// ambiguous segment separators
|
// ambiguous segment separators
|
||||||
{"/path/%2f/info", "/path///info", true},
|
{"/path/%2f/info", "/path///info", false, true},
|
||||||
{"%2f/info", "//info", true},
|
{"%2f/info", "//info", false, true},
|
||||||
{"%2F/info", "//info", true},
|
{"%2F/info", "//info", false, true},
|
||||||
|
{"/path/%2f../info", "/path//../info", false, true},
|
||||||
|
{"/path/%2f/..;/info", "/path///../info", true, true},
|
||||||
|
|
||||||
// Non ascii characters
|
// Non ascii characters
|
||||||
// @checkstyle-disable-check : AvoidEscapedUnicodeCharactersCheck
|
// @checkstyle-disable-check : AvoidEscapedUnicodeCharactersCheck
|
||||||
{"http://localhost:9000/x\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32", "/x\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32", false},
|
{"http://localhost:9000/x\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32", "/x\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32", false, false},
|
||||||
{"http://localhost:9000/\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32", "/\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32", false},
|
{"http://localhost:9000/\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32", "/\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32", false, false},
|
||||||
// @checkstyle-enable-check : AvoidEscapedUnicodeCharactersCheck
|
// @checkstyle-enable-check : AvoidEscapedUnicodeCharactersCheck
|
||||||
}).map(Arguments::of);
|
}).map(Arguments::of);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("decodePathTests")
|
@MethodSource("decodePathTests")
|
||||||
public void testDecodedPath(String input, String decodedPath, boolean ambiguous)
|
public void testDecodedPath(String input, String decodedPath, boolean ambiguousSegment, boolean ambiguousSeparator)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HttpURI uri = HttpURI.from(input);
|
HttpURI uri = HttpURI.from(input);
|
||||||
assertThat(uri.getDecodedPath(), is(decodedPath));
|
assertThat(uri.getDecodedPath(), is(decodedPath));
|
||||||
assertThat(uri.hasAmbiguousSegment(), is(ambiguous));
|
assertThat(uri.hasAmbiguousSegment(), is(ambiguousSegment));
|
||||||
|
assertThat(uri.hasAmbiguousSeparator(), is(ambiguousSeparator));
|
||||||
|
assertThat(uri.isAmbiguous(), is(ambiguousSegment || ambiguousSeparator));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
<Set name="maxErrorDispatches" property="jetty.httpConfig.maxErrorDispatches"/>
|
<Set name="maxErrorDispatches" property="jetty.httpConfig.maxErrorDispatches"/>
|
||||||
<Set name="persistentConnectionsEnabled" property="jetty.httpConfig.persistentConnectionsEnabled"/>
|
<Set name="persistentConnectionsEnabled" property="jetty.httpConfig.persistentConnectionsEnabled"/>
|
||||||
<Set name="httpCompliance"><Call class="org.eclipse.jetty.http.HttpCompliance" name="from"><Arg><Property name="jetty.httpConfig.compliance" deprecated="jetty.http.compliance" default="RFC7230"/></Arg></Call></Set>
|
<Set name="httpCompliance"><Call class="org.eclipse.jetty.http.HttpCompliance" name="from"><Arg><Property name="jetty.httpConfig.compliance" deprecated="jetty.http.compliance" default="RFC7230"/></Arg></Call></Set>
|
||||||
|
<Set name="uriCompliance"><Call class="org.eclipse.jetty.http.UriCompliance" name="from"><Arg><Property name="jetty.httpConfig.uriCompliance" default="SAFE"/></Arg></Call></Set>
|
||||||
<Set name="requestCookieCompliance"><Call class="org.eclipse.jetty.http.CookieCompliance" name="valueOf"><Arg><Property name="jetty.httpConfig.requestCookieCompliance" default="RFC6265"/></Arg></Call></Set>
|
<Set name="requestCookieCompliance"><Call class="org.eclipse.jetty.http.CookieCompliance" name="valueOf"><Arg><Property name="jetty.httpConfig.requestCookieCompliance" default="RFC6265"/></Arg></Call></Set>
|
||||||
<Set name="responseCookieCompliance"><Call class="org.eclipse.jetty.http.CookieCompliance" name="valueOf"><Arg><Property name="jetty.httpConfig.responseCookieCompliance" default="RFC6265"/></Arg></Call></Set>
|
<Set name="responseCookieCompliance"><Call class="org.eclipse.jetty.http.CookieCompliance" name="valueOf"><Arg><Property name="jetty.httpConfig.responseCookieCompliance" default="RFC6265"/></Arg></Call></Set>
|
||||||
<Set name="relativeRedirectAllowed"><Property name="jetty.httpConfig.relativeRedirectAllowed" default="false"/></Set>
|
<Set name="relativeRedirectAllowed"><Property name="jetty.httpConfig.relativeRedirectAllowed" default="false"/></Set>
|
||||||
|
|
|
@ -62,6 +62,9 @@ etc/jetty.xml
|
||||||
## HTTP Compliance: RFC7230, RFC7230_LEGACY, RFC2616, RFC2616_LEGACY, LEGACY
|
## HTTP Compliance: RFC7230, RFC7230_LEGACY, RFC2616, RFC2616_LEGACY, LEGACY
|
||||||
# jetty.httpConfig.compliance=RFC7230
|
# jetty.httpConfig.compliance=RFC7230
|
||||||
|
|
||||||
|
## URI Compliance: SAFE, STRICT
|
||||||
|
# jetty.httpConfig.uriCompliance=SAFE
|
||||||
|
|
||||||
## Cookie compliance mode for parsing request Cookie headers: RFC2965, RFC6265
|
## Cookie compliance mode for parsing request Cookie headers: RFC2965, RFC6265
|
||||||
# jetty.httpConfig.requestCookieCompliance=RFC6265
|
# jetty.httpConfig.requestCookieCompliance=RFC6265
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.eclipse.jetty.http.CookieCompliance;
|
||||||
import org.eclipse.jetty.http.HttpCompliance;
|
import org.eclipse.jetty.http.HttpCompliance;
|
||||||
import org.eclipse.jetty.http.HttpMethod;
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
import org.eclipse.jetty.http.HttpScheme;
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
|
import org.eclipse.jetty.http.UriCompliance;
|
||||||
import org.eclipse.jetty.util.Index;
|
import org.eclipse.jetty.util.Index;
|
||||||
import org.eclipse.jetty.util.Jetty;
|
import org.eclipse.jetty.util.Jetty;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||||
|
@ -69,6 +70,7 @@ public class HttpConfiguration implements Dumpable
|
||||||
private long _minRequestDataRate;
|
private long _minRequestDataRate;
|
||||||
private long _minResponseDataRate;
|
private long _minResponseDataRate;
|
||||||
private HttpCompliance _httpCompliance = HttpCompliance.RFC7230;
|
private HttpCompliance _httpCompliance = HttpCompliance.RFC7230;
|
||||||
|
private UriCompliance _uriCompliance = UriCompliance.SAFE;
|
||||||
private CookieCompliance _requestCookieCompliance = CookieCompliance.RFC6265;
|
private CookieCompliance _requestCookieCompliance = CookieCompliance.RFC6265;
|
||||||
private CookieCompliance _responseCookieCompliance = CookieCompliance.RFC6265;
|
private CookieCompliance _responseCookieCompliance = CookieCompliance.RFC6265;
|
||||||
private boolean _notifyRemoteAsyncErrors = true;
|
private boolean _notifyRemoteAsyncErrors = true;
|
||||||
|
@ -570,6 +572,16 @@ public class HttpConfiguration implements Dumpable
|
||||||
_httpCompliance = httpCompliance;
|
_httpCompliance = httpCompliance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UriCompliance getUriCompliance()
|
||||||
|
{
|
||||||
|
return _uriCompliance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUriCompliance(UriCompliance uriCompliance)
|
||||||
|
{
|
||||||
|
_uriCompliance = uriCompliance;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The CookieCompliance used for parsing request {@code Cookie} headers.
|
* @return The CookieCompliance used for parsing request {@code Cookie} headers.
|
||||||
* @see #getResponseCookieCompliance()
|
* @see #getResponseCookieCompliance()
|
||||||
|
@ -579,6 +591,14 @@ public class HttpConfiguration implements Dumpable
|
||||||
return _requestCookieCompliance;
|
return _requestCookieCompliance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cookieCompliance The CookieCompliance to use for parsing request {@code Cookie} headers.
|
||||||
|
*/
|
||||||
|
public void setRequestCookieCompliance(CookieCompliance cookieCompliance)
|
||||||
|
{
|
||||||
|
_requestCookieCompliance = cookieCompliance == null ? CookieCompliance.RFC6265 : cookieCompliance;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The CookieCompliance used for generating response {@code Set-Cookie} headers
|
* @return The CookieCompliance used for generating response {@code Set-Cookie} headers
|
||||||
* @see #getRequestCookieCompliance()
|
* @see #getRequestCookieCompliance()
|
||||||
|
@ -588,14 +608,6 @@ public class HttpConfiguration implements Dumpable
|
||||||
return _responseCookieCompliance;
|
return _responseCookieCompliance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param cookieCompliance The CookieCompliance to use for parsing request {@code Cookie} headers.
|
|
||||||
*/
|
|
||||||
public void setRequestCookieCompliance(CookieCompliance cookieCompliance)
|
|
||||||
{
|
|
||||||
_requestCookieCompliance = cookieCompliance == null ? CookieCompliance.RFC6265 : cookieCompliance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param cookieCompliance The CookieCompliance to use for generating response {@code Set-Cookie} headers
|
* @param cookieCompliance The CookieCompliance to use for generating response {@code Set-Cookie} headers
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -81,6 +81,7 @@ import org.eclipse.jetty.http.HttpURI;
|
||||||
import org.eclipse.jetty.http.HttpVersion;
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
import org.eclipse.jetty.http.MetaData;
|
import org.eclipse.jetty.http.MetaData;
|
||||||
import org.eclipse.jetty.http.MimeTypes;
|
import org.eclipse.jetty.http.MimeTypes;
|
||||||
|
import org.eclipse.jetty.http.UriCompliance;
|
||||||
import org.eclipse.jetty.io.Connection;
|
import org.eclipse.jetty.io.Connection;
|
||||||
import org.eclipse.jetty.io.RuntimeIOException;
|
import org.eclipse.jetty.io.RuntimeIOException;
|
||||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||||
|
@ -1690,10 +1691,16 @@ public class Request implements HttpServletRequest
|
||||||
_method = request.getMethod();
|
_method = request.getMethod();
|
||||||
_httpFields = request.getFields();
|
_httpFields = request.getFields();
|
||||||
final HttpURI uri = request.getURI();
|
final HttpURI uri = request.getURI();
|
||||||
|
|
||||||
if (uri.hasAmbiguousSegment() && !_channel.getHttpConfiguration().getHttpCompliance().allows(HttpCompliance.Violation.AMBIGUOUS_PATH_SEGMENTS))
|
if (uri.isAmbiguous())
|
||||||
throw new BadMessageException("Ambiguous segment in URI");
|
{
|
||||||
|
UriCompliance compliance = _channel == null || _channel.getHttpConfiguration() == null ? null : _channel.getHttpConfiguration().getUriCompliance();
|
||||||
|
if (uri.hasAmbiguousSegment() && (compliance == null || !compliance.allows(UriCompliance.Violation.AMBIGUOUS_PATH_SEGMENT)))
|
||||||
|
throw new BadMessageException("Ambiguous segment in URI");
|
||||||
|
if (uri.hasAmbiguousSeparator() && (compliance == null || !compliance.allows(UriCompliance.Violation.AMBIGUOUS_PATH_SEPARATOR)))
|
||||||
|
throw new BadMessageException("Ambiguous segment in URI");
|
||||||
|
}
|
||||||
|
|
||||||
if (uri.isAbsolute() && uri.hasAuthority() && uri.getPath() != null)
|
if (uri.isAbsolute() && uri.hasAuthority() && uri.getPath() != null)
|
||||||
{
|
{
|
||||||
_uri = uri;
|
_uri = uri;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue