Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-11.0.x
This commit is contained in:
commit
7df67f3a3c
33
VERSION.txt
33
VERSION.txt
|
@ -302,7 +302,7 @@ jetty-10.0.0 - 02 December 2020
|
|||
+ 5555 NPE for servlet with no mapping
|
||||
+ 5562 ArrayTernaryTrie consumes too much memory
|
||||
+ 5575 Add SEARCH as a known HttpMethod
|
||||
+ 5605 java.io.IOException: unconsumed input during http request parsing
|
||||
+ 5605 java.io.IOException: unconsumed input during http request parsing - Resolves CVE-2020-27218
|
||||
+ 5633 Allow to configure HttpClient request authority
|
||||
+ 5679 Distro argument --list-all-modules does not work
|
||||
+ 5680 No way to see which modules are enabled for the distro
|
||||
|
@ -326,7 +326,7 @@ jetty-10.0.0.beta3 - 21 October 2020
|
|||
+ 5443 Request without Host header fails with NullPointerException in
|
||||
ForwardedRequestCustomizer
|
||||
+ 5448 Request.isSecure() returns false for `https` schemes in Jetty 10
|
||||
+ 5451 Improve Working Directory creation
|
||||
+ 5451 Improve Working Directory creation - Resolves CVE-2020-27216
|
||||
+ 5454 Request error context is not reset
|
||||
+ 5475 Update to spifly 1.3.2 and asm 9
|
||||
+ 5480 NPE from WebInfConfiguration.deconfigure during WebAppContext shutdown
|
||||
|
@ -371,8 +371,8 @@ jetty-9.4.35.v20201120 - 20 November 2020
|
|||
+ 5539 StatisticsServlet output is not valid
|
||||
+ 5562 ArrayTernaryTrie consumes too much memory
|
||||
+ 5575 Add SEARCH as a known HttpMethod
|
||||
+ 5605 CVE-2020-27218 java.io.IOException: unconsumed input during http
|
||||
request parsing
|
||||
+ 5605 java.io.IOException: unconsumed input during http
|
||||
request parsing - Resolves CVE-2020-27218
|
||||
+ 5633 Allow to configure HttpClient request authority
|
||||
|
||||
jetty-9.4.34.v20201102 - 02 November 2020
|
||||
|
@ -396,7 +396,7 @@ jetty-9.4.33.v20201020 - 20 October 2020
|
|||
produced by ForwardedHeader
|
||||
+ 5443 Request without Host header fails with NullPointerException in
|
||||
ForwardedRequestCustomizer
|
||||
+ 5451 Improve Working Directory creation
|
||||
+ 5451 Improve Working Directory creation - Resolves CVE-2020-27216
|
||||
+ 5454 Request error context is not reset
|
||||
+ 5475 Update to spifly 1.3.2 and asm 9
|
||||
+ 5480 NPE from WebInfConfiguration.deconfigure during WebAppContext shutdown
|
||||
|
@ -629,7 +629,7 @@ jetty-9.4.30.v20200611 - 11 June 2020
|
|||
+ 4923 SecureRequestCustomizer.SslAttributes does not cache cert chain like
|
||||
before
|
||||
+ 4929 HttpClient: HttpCookieStore.Empty prevents sending cookies
|
||||
+ 4936 Response header overflow leads to buffer corruptions
|
||||
+ 4936 Response header overflow leads to buffer corruptions - Resolves CVE-2019-17638
|
||||
|
||||
jetty-9.4.29.v20200521 - 21 May 2020
|
||||
+ 2188 Lock contention creating HTTP/2 streams
|
||||
|
@ -766,7 +766,7 @@ jetty-9.4.24.v20191120 - 20 November 2019
|
|||
+ 3083 The ini-template for jetty.console-capture.dir does not match the
|
||||
default value
|
||||
+ 4128 OpenIdCredetials can't decode JWT ID token
|
||||
+ 4334 Better test ErrorHandler changes
|
||||
+ 4334 Better test ErrorHandler changes - Resolves CVE-2019-17632
|
||||
|
||||
jetty-9.4.23.v20191118 - 18 November 2019
|
||||
+ 1485 Add systemd service file
|
||||
|
@ -856,6 +856,7 @@ jetty-9.4.22.v20191022 - 22 October 2019
|
|||
inclusion of sessionid
|
||||
|
||||
jetty-9.4.21.v20190926 - 26 September 2019
|
||||
+ Includes fixes for CVE-2019-9511, CVE-2019-9512, CVE-2019-9514, CVE-2019-9515, CVE-2019-9516, and CVE-2019-9518
|
||||
+ 97 Permanent UnavailableException thrown during servlet request handling
|
||||
should cause servlet destroy
|
||||
+ 137 Support OAuth
|
||||
|
@ -1001,8 +1002,8 @@ jetty-9.4.18.v20190429 - 29 April 2019
|
|||
jetty-9.4.17.v20190418 - 18 April 2019
|
||||
+ 2140 Infinispan and hazelcast changes to scavenge zombie expired sessions
|
||||
+ 3464 Split SslContextFactory into Client and Server
|
||||
+ 3549 Directory Listing on Windows reveals Resource Base path
|
||||
+ 3555 DefaultHandler Reveals Base Resource Path of each Context
|
||||
+ 3549 Directory Listing on Windows reveals Resource Base path - Resolves CVE-2019-10246
|
||||
+ 3555 DefaultHandler Reveals Base Resource Path of each Context - Resolves CVE-2019-10247
|
||||
|
||||
jetty-9.4.16.v20190411 - 11 April 2019
|
||||
+ 1861 Limit total bytes pooled by ByteBufferPools
|
||||
|
@ -1010,7 +1011,7 @@ jetty-9.4.16.v20190411 - 11 April 2019
|
|||
+ 3159 WebSocket permessage-deflate RSV1 validity check
|
||||
+ 3274 OSGi versions of java.base classes in
|
||||
org.apache.felix:org.osgi.foundation:jar conflicts with new rules on Java 9+
|
||||
+ 3319 Modernize Directory Listing: HTML5 and Sorting
|
||||
+ 3319 Modernize Directory Listing: HTML5 and Sorting - Resolves CVE-2019-10241
|
||||
+ 3361 HandlerCollection.addHandler is lacking synchronization
|
||||
+ 3373 OutOfMemoryError: Java heap space in GZIPContentDecoder
|
||||
+ 3389 Websockets jsr356 willDecode not invoked during decoding
|
||||
|
@ -1083,8 +1084,8 @@ jetty-9.3.28.v20191105 - 05 November 2019
|
|||
+ 4217 SslConnection.DecryptedEnpoint.flush eternal busy loop
|
||||
|
||||
jetty-9.3.27.v20190418 - 18 April 2019
|
||||
+ 3549 Directory Listing on Windows reveals Resource Base path
|
||||
+ 3555 DefaultHandler Reveals Base Resource Path of each Context
|
||||
+ 3549 Directory Listing on Windows reveals Resource Base path - Resolves CVE-2019-10246
|
||||
+ 3555 DefaultHandler Reveals Base Resource Path of each Context - Resolves CVE-2019-10247
|
||||
|
||||
jetty-9.3.26.v20190403 - 03 April 2019
|
||||
+ 2954 Improve cause reporting for HttpClient failures
|
||||
|
@ -1092,17 +1093,17 @@ jetty-9.3.26.v20190403 - 03 April 2019
|
|||
org.apache.felix:org.osgi.foundation:jar conflicts with new rules on Java 9+
|
||||
+ 3302 Support host:port in X-Forwarded-For header in
|
||||
ForwardedRequestCustomizer
|
||||
+ 3319 Allow reverse sort for directory listed files
|
||||
+ 3319 Allow reverse sort for directory listed files - Resolves CVE-2019-10241
|
||||
|
||||
jetty-9.2.29.v20191105 - 05 November 2019
|
||||
+ 4217 SslConnection.DecryptedEnpoint.flush eternal busy loop
|
||||
|
||||
jetty-9.2.28.v20190418 - 18 April 2019
|
||||
+ 3549 Directory Listing on Windows reveals Resource Base path
|
||||
+ 3555 DefaultHandler Reveals Base Resource Path of each Context
|
||||
+ 3549 Directory Listing on Windows reveals Resource Base path - Resolves CVE-2019-10246
|
||||
+ 3555 DefaultHandler Reveals Base Resource Path of each Context - Resolves CVE-2019-10247
|
||||
|
||||
jetty-9.2.27.v20190403 - 03 April 2019
|
||||
+ 3319 Refactored Directory Listing to modernize and avoid XSS
|
||||
+ 3319 Refactored Directory Listing to modernize and avoid XSS - Resolves CVE-2019-10241
|
||||
|
||||
jetty-9.4.14.v20181114 - 14 November 2018
|
||||
+ 3097 Duplicated programmatic Servlet Listeners causing duplicate calls
|
||||
|
|
|
@ -20,6 +20,9 @@ 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;
|
||||
|
@ -34,6 +37,7 @@ import static java.util.EnumSet.of;
|
|||
*/
|
||||
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
|
||||
// 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"),
|
||||
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"),
|
||||
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");
|
||||
NO_COLON_AFTER_FIELD_NAME("https://tools.ietf.org/html/rfc7230#section-3.2", "Fields must have a Colon");
|
||||
|
||||
private final String url;
|
||||
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 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",
|
||||
Violation.CASE_INSENSITIVE_METHOD,
|
||||
Violation.NO_COLON_AFTER_FIELD_NAME,
|
||||
Violation.TRANSFER_ENCODING_WITH_CONTENT_LENGTH,
|
||||
Violation.MULTIPLE_CONTENT_LENGTHS,
|
||||
Violation.AMBIGUOUS_PATH_SEGMENTS);
|
||||
public static final HttpCompliance RFC7230_LEGACY = RFC7230.with("RFC7230_LEGACY", Violation.CASE_INSENSITIVE_METHOD, Violation.AMBIGUOUS_PATH_SEGMENTS);
|
||||
Violation.MULTIPLE_CONTENT_LENGTHS);
|
||||
public static final HttpCompliance RFC7230_LEGACY = RFC7230.with("RFC7230_LEGACY", Violation.CASE_INSENSITIVE_METHOD);
|
||||
|
||||
private static final List<HttpCompliance> KNOWN_MODES = Arrays.asList(RFC7230, RFC2616, LEGACY, RFC2616_LEGACY, RFC7230_LEGACY);
|
||||
private static final AtomicInteger __custom = new AtomicInteger();
|
||||
|
@ -100,6 +102,7 @@ public final class HttpCompliance implements ComplianceViolation.Mode
|
|||
if (compliance.getName().equals(name))
|
||||
return compliance;
|
||||
}
|
||||
LOG.warn("Unknown HttpCompliance mode {}", name);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -141,10 +144,7 @@ public final class HttpCompliance implements ComplianceViolation.Mode
|
|||
default:
|
||||
{
|
||||
HttpCompliance mode = HttpCompliance.valueOf(elements[0]);
|
||||
if (mode == null)
|
||||
sections = noneOf(Violation.class);
|
||||
else
|
||||
sections = copyOf(mode.getAllowed());
|
||||
sections = (mode == null) ? noneOf(HttpCompliance.Violation.class) : copyOf(mode.getAllowed());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ package org.eclipse.jetty.http;
|
|||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import org.eclipse.jetty.util.HostPort;
|
||||
import org.eclipse.jetty.util.Index;
|
||||
|
@ -47,6 +48,12 @@ import org.eclipse.jetty.util.UrlEncoded;
|
|||
*/
|
||||
public interface HttpURI
|
||||
{
|
||||
enum Ambiguous
|
||||
{
|
||||
SEGMENT,
|
||||
SEPARATOR
|
||||
}
|
||||
|
||||
static Mutable build()
|
||||
{
|
||||
return new Mutable();
|
||||
|
@ -131,8 +138,21 @@ public interface HttpURI
|
|||
|
||||
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();
|
||||
|
||||
/**
|
||||
* @return True if the URI has a possibly ambiguous separator of %2f
|
||||
*/
|
||||
boolean hasAmbiguousSeparator();
|
||||
|
||||
default URI toURI()
|
||||
{
|
||||
try
|
||||
|
@ -158,7 +178,7 @@ public interface HttpURI
|
|||
private final String _fragment;
|
||||
private String _uri;
|
||||
private String _decodedPath;
|
||||
private boolean _ambiguousSegment;
|
||||
private final EnumSet<Mutable.Ambiguous> _ambiguous = EnumSet.noneOf(Mutable.Ambiguous.class);
|
||||
|
||||
private Immutable(Mutable builder)
|
||||
{
|
||||
|
@ -172,7 +192,7 @@ public interface HttpURI
|
|||
_fragment = builder._fragment;
|
||||
_uri = builder._uri;
|
||||
_decodedPath = builder._decodedPath;
|
||||
_ambiguousSegment = builder._ambiguousSegment;
|
||||
_ambiguous.addAll(builder._ambiguous);
|
||||
}
|
||||
|
||||
private Immutable(String uri)
|
||||
|
@ -336,10 +356,22 @@ public interface HttpURI
|
|||
return !StringUtil.isEmpty(_scheme);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAmbiguous()
|
||||
{
|
||||
return !_ambiguous.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAmbiguousSegment()
|
||||
{
|
||||
return _ambiguousSegment;
|
||||
return _ambiguous.contains(Mutable.Ambiguous.SEGMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAmbiguousSeparator()
|
||||
{
|
||||
return _ambiguous.contains(Mutable.Ambiguous.SEPARATOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -411,7 +443,7 @@ public interface HttpURI
|
|||
private String _fragment;
|
||||
private String _uri;
|
||||
private String _decodedPath;
|
||||
private boolean _ambiguousSegment;
|
||||
private final EnumSet<Ambiguous> _ambiguous = EnumSet.noneOf(Ambiguous.class);
|
||||
|
||||
private Mutable()
|
||||
{
|
||||
|
@ -535,7 +567,7 @@ public interface HttpURI
|
|||
_fragment = null;
|
||||
_uri = null;
|
||||
_decodedPath = null;
|
||||
_ambiguousSegment = false;
|
||||
_ambiguous.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -659,10 +691,22 @@ public interface HttpURI
|
|||
return _scheme != null && !_scheme.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAmbiguous()
|
||||
{
|
||||
return !_ambiguous.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAmbiguousSegment()
|
||||
{
|
||||
return _ambiguousSegment;
|
||||
return _ambiguous.contains(Mutable.Ambiguous.SEGMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAmbiguousSeparator()
|
||||
{
|
||||
return _ambiguous.contains(Mutable.Ambiguous.SEPARATOR);
|
||||
}
|
||||
|
||||
public Mutable normalize()
|
||||
|
@ -766,7 +810,10 @@ public interface HttpURI
|
|||
_query = uri.getQuery();
|
||||
_uri = null;
|
||||
_decodedPath = uri.getDecodedPath();
|
||||
_ambiguousSegment = uri.hasAmbiguousSegment();
|
||||
if (uri.hasAmbiguousSeparator())
|
||||
_ambiguous.add(Ambiguous.SEPARATOR);
|
||||
if (uri.hasAmbiguousSegment())
|
||||
_ambiguous.add(Ambiguous.SEGMENT);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -1062,7 +1109,8 @@ public interface HttpURI
|
|||
break;
|
||||
case 'f':
|
||||
case 'F':
|
||||
_ambiguousSegment |= (escapedSlash == 2);
|
||||
if (escapedSlash == 2)
|
||||
_ambiguous.add(Ambiguous.SEPARATOR);
|
||||
escapedSlash = 0;
|
||||
break;
|
||||
default:
|
||||
|
@ -1183,16 +1231,18 @@ public interface HttpURI
|
|||
*
|
||||
* 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.
|
||||
*
|
||||
* @param uri The URI string
|
||||
* @param segment The inclusive starting index of the segment (excluding any '/')
|
||||
* @param end The exclusive end index of the segment
|
||||
*/
|
||||
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);
|
||||
_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.nullValue;
|
||||
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.fail;
|
||||
|
||||
public class HttpURITest
|
||||
{
|
||||
@Test
|
||||
public void testBuilder() throws Exception
|
||||
public void testBuilder()
|
||||
{
|
||||
HttpURI uri = HttpURI.build()
|
||||
.scheme("http")
|
||||
|
@ -76,7 +77,7 @@ public class HttpURITest
|
|||
}
|
||||
|
||||
@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");
|
||||
|
||||
|
@ -93,7 +94,7 @@ public class HttpURITest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidAddress() throws Exception
|
||||
public void testInvalidAddress()
|
||||
{
|
||||
assertInvalidURI("http://[ffff::1:8080/", "Invalid URL; no closing ']' -- should throw exception");
|
||||
assertInvalidURI("**", "only '*', not '**'");
|
||||
|
@ -163,19 +164,19 @@ public class HttpURITest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testAt() throws Exception
|
||||
public void testAt()
|
||||
{
|
||||
HttpURI uri = HttpURI.from("/@foo/bar");
|
||||
assertEquals("/@foo/bar", uri.getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParams() throws Exception
|
||||
public void testParams()
|
||||
{
|
||||
HttpURI uri = HttpURI.from("/foo/bar");
|
||||
assertEquals("/foo/bar", uri.getPath());
|
||||
assertEquals("/foo/bar", uri.getDecodedPath());
|
||||
assertEquals(null, uri.getParam());
|
||||
assertNull(uri.getParam());
|
||||
|
||||
uri = HttpURI.from("/foo/bar;jsessionid=12345");
|
||||
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("/f00/bar", uri.getDecodedPath());
|
||||
assertEquals("p2", uri.getParam());
|
||||
assertEquals(null, uri.getQuery());
|
||||
assertNull(uri.getQuery());
|
||||
|
||||
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());
|
||||
|
@ -241,7 +242,7 @@ public class HttpURITest
|
|||
assertEquals("/f%30%30;p0/bar;p1;p2", uri.getPath());
|
||||
assertEquals("/f00/bar", uri.getDecodedPath());
|
||||
assertEquals("p2", uri.getParam());
|
||||
assertEquals(null, uri.getQuery());
|
||||
assertNull(uri.getQuery());
|
||||
|
||||
uri = builder.query("other=123456").asImmutable();
|
||||
assertEquals("http://host:8888/f%30%30;p0/bar;p1;p2?other=123456", uri.toString());
|
||||
|
@ -252,7 +253,7 @@ public class HttpURITest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSchemeAndOrAuthority() throws Exception
|
||||
public void testSchemeAndOrAuthority()
|
||||
{
|
||||
HttpURI.Mutable builder = HttpURI.build("/path/info");
|
||||
HttpURI uri = builder.asImmutable();
|
||||
|
@ -272,7 +273,7 @@ public class HttpURITest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testBasicAuthCredentials() throws Exception
|
||||
public void testBasicAuthCredentials()
|
||||
{
|
||||
HttpURI uri = HttpURI.from("http://user:password@example.com:8888/blah");
|
||||
assertEquals("http://user:password@example.com:8888/blah", uri.toString());
|
||||
|
@ -281,7 +282,7 @@ public class HttpURITest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testCanonicalDecoded() throws Exception
|
||||
public void testCanonicalDecoded()
|
||||
{
|
||||
HttpURI uri = HttpURI.from("/path/.info");
|
||||
assertEquals("/path/.info", uri.getDecodedPath());
|
||||
|
@ -319,74 +320,79 @@ public class HttpURITest
|
|||
return Arrays.stream(new Object[][]
|
||||
{
|
||||
// Simple path example
|
||||
{"http://host/path/info", "/path/info", false},
|
||||
{"//host/path/info", "/path/info", false},
|
||||
{"/path/info", "/path/info", false},
|
||||
{"http://host/path/info", "/path/info", false, false},
|
||||
{"//host/path/info", "/path/info", false, false},
|
||||
{"/path/info", "/path/info", false, false},
|
||||
|
||||
// legal non ambiguous relative paths
|
||||
{"http://host/../path/info", null, false},
|
||||
{"http://host/path/../info", "/info", false},
|
||||
{"http://host/path/./info", "/path/info", false},
|
||||
{"//host/path/../info", "/info", false},
|
||||
{"//host/path/./info", "/path/info", false},
|
||||
{"/path/../info", "/info", false},
|
||||
{"/path/./info", "/path/info", false},
|
||||
{"path/../info", "info", false},
|
||||
{"path/./info", "path/info", false},
|
||||
{"http://host/../path/info", null, false, false},
|
||||
{"http://host/path/../info", "/info", false, false},
|
||||
{"http://host/path/./info", "/path/info", false, false},
|
||||
{"//host/path/../info", "/info", false, false},
|
||||
{"//host/path/./info", "/path/info", false, false},
|
||||
{"/path/../info", "/info", false, false},
|
||||
{"/path/./info", "/path/info", false, false},
|
||||
{"path/../info", "info", false, false},
|
||||
{"path/./info", "path/info", false, false},
|
||||
|
||||
// illegal paths
|
||||
{"//host/../path/info", null, false},
|
||||
{"/../path/info", null, false},
|
||||
{"../path/info", null, false},
|
||||
{"/path/%XX/info", null, false},
|
||||
{"/path/%2/F/info", null, false},
|
||||
{"//host/../path/info", null, false, false},
|
||||
{"/../path/info", null, false, false},
|
||||
{"../path/info", null, false, false},
|
||||
{"/path/%XX/info", null, false, false},
|
||||
{"/path/%2/F/info", null, false, false},
|
||||
|
||||
// ambiguous dot encodings or parameter inclusions
|
||||
{"scheme://host/path/%2e/info", "/path/./info", true},
|
||||
{"scheme:/path/%2e/info", "/path/./info", true},
|
||||
{"/path/%2e/info", "/path/./info", true},
|
||||
{"path/%2e/info/", "path/./info/", true},
|
||||
{"/path/%2e%2e/info", "/path/../info", true},
|
||||
{"/path/%2e%2e;/info", "/path/../info", true},
|
||||
{"/path/%2e%2e;param/info", "/path/../info", true},
|
||||
{"/path/%2e%2e;param;other/info;other", "/path/../info", true},
|
||||
{"/path/.;/info", "/path/./info", true},
|
||||
{"/path/.;param/info", "/path/./info", true},
|
||||
{"/path/..;/info", "/path/../info", true},
|
||||
{"/path/..;param/info", "/path/../info", true},
|
||||
{"%2e/info", "./info", true},
|
||||
{"%2e%2e/info", "../info", true},
|
||||
{"%2e%2e;/info", "../info", true},
|
||||
{".;/info", "./info", true},
|
||||
{".;param/info", "./info", true},
|
||||
{"..;/info", "../info", true},
|
||||
{"..;param/info", "../info", true},
|
||||
{"%2e", ".", true},
|
||||
{"%2e.", "..", true},
|
||||
{".%2e", "..", true},
|
||||
{"%2e%2e", "..", true},
|
||||
{"scheme://host/path/%2e/info", "/path/./info", true, false},
|
||||
{"scheme:/path/%2e/info", "/path/./info", true, false},
|
||||
{"/path/%2e/info", "/path/./info", true, false},
|
||||
{"path/%2e/info/", "path/./info/", true, false},
|
||||
{"/path/%2e%2e/info", "/path/../info", true, false},
|
||||
{"/path/%2e%2e;/info", "/path/../info", true, false},
|
||||
{"/path/%2e%2e;param/info", "/path/../info", true, false},
|
||||
{"/path/%2e%2e;param;other/info;other", "/path/../info", true, false},
|
||||
{"/path/.;/info", "/path/./info", true, false},
|
||||
{"/path/.;param/info", "/path/./info", true, false},
|
||||
{"/path/..;/info", "/path/../info", true, false},
|
||||
{"/path/..;param/info", "/path/../info", true, false},
|
||||
{"%2e/info", "./info", true, false},
|
||||
{"%2e%2e/info", "../info", true, false},
|
||||
{"%2e%2e;/info", "../info", true, false},
|
||||
{".;/info", "./info", true, false},
|
||||
{".;param/info", "./info", true, false},
|
||||
{"..;/info", "../info", true, false},
|
||||
{"..;param/info", "../info", true, false},
|
||||
{"%2e", ".", true, false},
|
||||
{"%2e.", "..", true, false},
|
||||
{".%2e", "..", true, false},
|
||||
{"%2e%2e", "..", true, false},
|
||||
|
||||
// ambiguous segment separators
|
||||
{"/path/%2f/info", "/path///info", true},
|
||||
{"%2f/info", "//info", true},
|
||||
{"%2F/info", "//info", true},
|
||||
{"/path/%2f/info", "/path///info", false, true},
|
||||
{"%2f/info", "//info", false, true},
|
||||
{"%2F/info", "//info", false, true},
|
||||
{"/path/%2f../info", "/path//../info", false, true},
|
||||
{"/path/%2f/..;/info", "/path///../info", true, true},
|
||||
|
||||
// Non ascii characters
|
||||
// @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/\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32\uD83C\uDF32", "/\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, false},
|
||||
// @checkstyle-enable-check : AvoidEscapedUnicodeCharactersCheck
|
||||
}).map(Arguments::of);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("decodePathTests")
|
||||
public void testDecodedPath(String input, String decodedPath, boolean ambiguous)
|
||||
public void testDecodedPath(String input, String decodedPath, boolean ambiguousSegment, boolean ambiguousSeparator)
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpURI uri = HttpURI.from(input);
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
<Set name="maxErrorDispatches" property="jetty.httpConfig.maxErrorDispatches"/>
|
||||
<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="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="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>
|
||||
|
|
|
@ -62,6 +62,9 @@ etc/jetty.xml
|
|||
## HTTP Compliance: RFC7230, RFC7230_LEGACY, RFC2616, RFC2616_LEGACY, LEGACY
|
||||
# jetty.httpConfig.compliance=RFC7230
|
||||
|
||||
## URI Compliance: SAFE, STRICT
|
||||
# jetty.httpConfig.uriCompliance=SAFE
|
||||
|
||||
## Cookie compliance mode for parsing request Cookie headers: RFC2965, 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.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http.UriCompliance;
|
||||
import org.eclipse.jetty.util.Index;
|
||||
import org.eclipse.jetty.util.Jetty;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
|
@ -69,6 +70,7 @@ public class HttpConfiguration implements Dumpable
|
|||
private long _minRequestDataRate;
|
||||
private long _minResponseDataRate;
|
||||
private HttpCompliance _httpCompliance = HttpCompliance.RFC7230;
|
||||
private UriCompliance _uriCompliance = UriCompliance.SAFE;
|
||||
private CookieCompliance _requestCookieCompliance = CookieCompliance.RFC6265;
|
||||
private CookieCompliance _responseCookieCompliance = CookieCompliance.RFC6265;
|
||||
private boolean _notifyRemoteAsyncErrors = true;
|
||||
|
@ -570,6 +572,16 @@ public class HttpConfiguration implements Dumpable
|
|||
_httpCompliance = httpCompliance;
|
||||
}
|
||||
|
||||
public UriCompliance getUriCompliance()
|
||||
{
|
||||
return _uriCompliance;
|
||||
}
|
||||
|
||||
public void setUriCompliance(UriCompliance uriCompliance)
|
||||
{
|
||||
_uriCompliance = uriCompliance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The CookieCompliance used for parsing request {@code Cookie} headers.
|
||||
* @see #getResponseCookieCompliance()
|
||||
|
@ -579,6 +591,14 @@ public class HttpConfiguration implements Dumpable
|
|||
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
|
||||
* @see #getRequestCookieCompliance()
|
||||
|
@ -588,14 +608,6 @@ public class HttpConfiguration implements Dumpable
|
|||
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
|
||||
*/
|
||||
|
|
|
@ -81,6 +81,7 @@ import org.eclipse.jetty.http.HttpURI;
|
|||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.http.UriCompliance;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
import org.eclipse.jetty.io.RuntimeIOException;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
|
@ -1691,8 +1692,14 @@ public class Request implements HttpServletRequest
|
|||
_httpFields = request.getFields();
|
||||
final HttpURI uri = request.getURI();
|
||||
|
||||
if (uri.hasAmbiguousSegment() && !_channel.getHttpConfiguration().getHttpCompliance().allows(HttpCompliance.Violation.AMBIGUOUS_PATH_SEGMENTS))
|
||||
if (uri.isAmbiguous())
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -31,7 +31,6 @@ import java.util.Arrays;
|
|||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
@ -59,6 +58,7 @@ import org.eclipse.jetty.http.HttpURI;
|
|||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.http.UriCompliance;
|
||||
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
|
||||
import org.eclipse.jetty.logging.StacklessLogging;
|
||||
import org.eclipse.jetty.server.LocalConnector.LocalEndPoint;
|
||||
|
@ -150,10 +150,7 @@ public class RequestTest
|
|||
ContextHandler handler = new CharEncodingContextHandler();
|
||||
_server.setHandler(handler);
|
||||
handler.setHandler(_handler);
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response)
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -168,7 +165,6 @@ public class RequestTest
|
|||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
_server.start();
|
||||
|
||||
|
@ -203,10 +199,7 @@ public class RequestTest
|
|||
@Test
|
||||
public void testParamExtraction() throws Exception
|
||||
{
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response)
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -220,7 +213,6 @@ public class RequestTest
|
|||
String rawQuery = request.getQueryString();
|
||||
return rawQuery.equals("param=aaa%ZZbbb&other=value");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//Send a request with query string with illegal hex code to cause
|
||||
|
@ -238,15 +230,11 @@ public class RequestTest
|
|||
@Test
|
||||
public void testParamExtractionBadSequence() throws Exception
|
||||
{
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response)
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
request.getParameterMap();
|
||||
// should have thrown a BadMessageException
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//Send a request with query string with illegal hex code to cause
|
||||
|
@ -264,15 +252,11 @@ public class RequestTest
|
|||
@Test
|
||||
public void testParamExtractionTimeout() throws Exception
|
||||
{
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response)
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
request.getParameterMap();
|
||||
// should have thrown a BadMessageException
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//Send a request with query string with illegal hex code to cause
|
||||
|
@ -295,10 +279,7 @@ public class RequestTest
|
|||
@Test
|
||||
public void testEmptyHeaders() throws Exception
|
||||
{
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response)
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
assertNotNull(request.getLocale());
|
||||
assertTrue(request.getLocales().hasMoreElements()); // Default locale
|
||||
|
@ -312,7 +293,6 @@ public class RequestTest
|
|||
assertThrows(IllegalArgumentException.class, () -> request.getDateHeader("Name"));
|
||||
assertEquals(-1, request.getDateHeader("Other"));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
String request = "GET /? HTTP/1.1\r\n" +
|
||||
|
@ -331,10 +311,7 @@ public class RequestTest
|
|||
@Test
|
||||
public void testMultiPartNoConfig() throws Exception
|
||||
{
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response)
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -351,7 +328,6 @@ public class RequestTest
|
|||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
String multipart = "--AaB03x\r\n" +
|
||||
|
@ -380,10 +356,7 @@ public class RequestTest
|
|||
@Test
|
||||
public void testLocale() throws Exception
|
||||
{
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
assertThat(request.getLocale().getLanguage(), is("da"));
|
||||
Enumeration<Locale> locales = request.getLocales();
|
||||
|
@ -401,7 +374,6 @@ public class RequestTest
|
|||
assertThat(locale.getCountry(), is(""));
|
||||
assertFalse(locales.hasMoreElements());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
String request = "GET / HTTP/1.1\r\n" +
|
||||
|
@ -522,10 +494,7 @@ public class RequestTest
|
|||
@Test
|
||||
public void testBadUtf8ParamExtraction() throws Exception
|
||||
{
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response)
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -539,7 +508,6 @@ public class RequestTest
|
|||
String rawQuery = request.getQueryString();
|
||||
return rawQuery.equals("param=aaa%E7bbb");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//Send a request with query string with illegal hex code to cause
|
||||
|
@ -558,10 +526,7 @@ public class RequestTest
|
|||
@Test
|
||||
public void testEncodedParamExtraction() throws Exception
|
||||
{
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response)
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -573,7 +538,6 @@ public class RequestTest
|
|||
{
|
||||
return e.getCode() == 415;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//Send a request with encoded form content
|
||||
|
@ -703,15 +667,11 @@ public class RequestTest
|
|||
public void testContentTypeEncoding() throws Exception
|
||||
{
|
||||
final ArrayList<String> results = new ArrayList<>();
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response)
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
results.add(request.getContentType());
|
||||
results.add(request.getCharacterEncoding());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
LocalEndPoint endp = _connector.executeRequest(
|
||||
|
@ -744,7 +704,7 @@ public class RequestTest
|
|||
|
||||
int i = 0;
|
||||
assertEquals("text/test", results.get(i++));
|
||||
assertEquals(null, results.get(i++));
|
||||
assertNull(results.get(i++));
|
||||
|
||||
assertEquals("text/html;charset=utf8", results.get(i++));
|
||||
assertEquals("utf-8", results.get(i++));
|
||||
|
@ -753,27 +713,22 @@ public class RequestTest
|
|||
assertEquals("utf-8", results.get(i++));
|
||||
|
||||
assertTrue(results.get(i++).startsWith("text/html"));
|
||||
assertEquals(" x=z; ", results.get(i++));
|
||||
assertEquals(" x=z; ", results.get(i));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHostPort() throws Exception
|
||||
{
|
||||
final ArrayList<String> results = new ArrayList<>();
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response)
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
results.add(request.getRequestURL().toString());
|
||||
results.add(request.getRemoteAddr());
|
||||
results.add(request.getServerName());
|
||||
results.add(String.valueOf(request.getServerPort()));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
results.clear();
|
||||
String response = _connector.getResponse(
|
||||
"GET / HTTP/1.1\n" +
|
||||
"Host: myhost\n" +
|
||||
|
@ -784,7 +739,7 @@ public class RequestTest
|
|||
assertEquals("http://myhost/", results.get(i++));
|
||||
assertEquals("0.0.0.0", results.get(i++));
|
||||
assertEquals("myhost", results.get(i++));
|
||||
assertEquals("80", results.get(i++));
|
||||
assertEquals("80", results.get(i));
|
||||
|
||||
results.clear();
|
||||
response = _connector.getResponse(
|
||||
|
@ -797,7 +752,7 @@ public class RequestTest
|
|||
assertEquals("http://myhost:8888/", results.get(i++));
|
||||
assertEquals("0.0.0.0", results.get(i++));
|
||||
assertEquals("myhost", results.get(i++));
|
||||
assertEquals("8888", results.get(i++));
|
||||
assertEquals("8888", results.get(i));
|
||||
|
||||
results.clear();
|
||||
response = _connector.getResponse(
|
||||
|
@ -808,7 +763,7 @@ public class RequestTest
|
|||
assertEquals("http://myhost:8888/", results.get(i++));
|
||||
assertEquals("0.0.0.0", results.get(i++));
|
||||
assertEquals("myhost", results.get(i++));
|
||||
assertEquals("8888", results.get(i++));
|
||||
assertEquals("8888", results.get(i));
|
||||
|
||||
results.clear();
|
||||
response = _connector.getResponse(
|
||||
|
@ -821,7 +776,7 @@ public class RequestTest
|
|||
assertEquals("http://myhost:8888/", results.get(i++));
|
||||
assertEquals("0.0.0.0", results.get(i++));
|
||||
assertEquals("myhost", results.get(i++));
|
||||
assertEquals("8888", results.get(i++));
|
||||
assertEquals("8888", results.get(i));
|
||||
|
||||
results.clear();
|
||||
response = _connector.getResponse(
|
||||
|
@ -835,7 +790,7 @@ public class RequestTest
|
|||
assertEquals("http://1.2.3.4/", results.get(i++));
|
||||
assertEquals("0.0.0.0", results.get(i++));
|
||||
assertEquals("1.2.3.4", results.get(i++));
|
||||
assertEquals("80", results.get(i++));
|
||||
assertEquals("80", results.get(i));
|
||||
|
||||
results.clear();
|
||||
response = _connector.getResponse(
|
||||
|
@ -848,7 +803,7 @@ public class RequestTest
|
|||
assertEquals("http://1.2.3.4:8888/", results.get(i++));
|
||||
assertEquals("0.0.0.0", results.get(i++));
|
||||
assertEquals("1.2.3.4", results.get(i++));
|
||||
assertEquals("8888", results.get(i++));
|
||||
assertEquals("8888", results.get(i));
|
||||
|
||||
results.clear();
|
||||
response = _connector.getResponse(
|
||||
|
@ -861,7 +816,7 @@ public class RequestTest
|
|||
assertEquals("http://[::1]/", results.get(i++));
|
||||
assertEquals("0.0.0.0", results.get(i++));
|
||||
assertEquals("[::1]", results.get(i++));
|
||||
assertEquals("80", results.get(i++));
|
||||
assertEquals("80", results.get(i));
|
||||
|
||||
results.clear();
|
||||
response = _connector.getResponse(
|
||||
|
@ -874,7 +829,7 @@ public class RequestTest
|
|||
assertEquals("http://[::1]:8888/", results.get(i++));
|
||||
assertEquals("0.0.0.0", results.get(i++));
|
||||
assertEquals("[::1]", results.get(i++));
|
||||
assertEquals("8888", results.get(i++));
|
||||
assertEquals("8888", results.get(i));
|
||||
|
||||
results.clear();
|
||||
response = _connector.getResponse(
|
||||
|
@ -889,7 +844,7 @@ public class RequestTest
|
|||
assertEquals("https://[::1]/", results.get(i++));
|
||||
assertEquals("remote", results.get(i++));
|
||||
assertEquals("[::1]", results.get(i++));
|
||||
assertEquals("443", results.get(i++));
|
||||
assertEquals("443", results.get(i));
|
||||
|
||||
results.clear();
|
||||
response = _connector.getResponse(
|
||||
|
@ -904,7 +859,7 @@ public class RequestTest
|
|||
assertEquals("https://[::1]:8888/", results.get(i++));
|
||||
assertEquals("remote", results.get(i++));
|
||||
assertEquals("[::1]", results.get(i++));
|
||||
assertEquals("8888", results.get(i++));
|
||||
assertEquals("8888", results.get(i));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -912,10 +867,7 @@ public class RequestTest
|
|||
{
|
||||
final AtomicInteger length = new AtomicInteger();
|
||||
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
int len = request.getContentLength();
|
||||
ServletInputStream in = request.getInputStream();
|
||||
|
@ -930,10 +882,9 @@ public class RequestTest
|
|||
|
||||
length.set(len);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
String content = "";
|
||||
StringBuilder content = new StringBuilder();
|
||||
|
||||
for (int l = 0; l < 1024; l++)
|
||||
{
|
||||
|
@ -949,21 +900,17 @@ public class RequestTest
|
|||
LOG.debug(response);
|
||||
assertThat(response, containsString(" 200 OK"));
|
||||
assertEquals(l, length.get());
|
||||
content += "x";
|
||||
content.append("x");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodedForm() throws Exception
|
||||
{
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
String actual = request.getParameter("name2");
|
||||
return "test2".equals(actual);
|
||||
}
|
||||
};
|
||||
|
||||
String content = "name1=test&name2=test2&name3=&name4=test";
|
||||
|
@ -981,14 +928,7 @@ public class RequestTest
|
|||
@Test
|
||||
public void testEncodedFormUnknownMethod() throws Exception
|
||||
{
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
return request.getParameter("name1") == null && request.getParameter("name2") == null && request.getParameter("name3") == null;
|
||||
}
|
||||
};
|
||||
_handler._checker = (request, response) -> request.getParameter("name1") == null && request.getParameter("name2") == null && request.getParameter("name3") == null;
|
||||
|
||||
String content = "name1=test&name2=test2&name3=&name4=test";
|
||||
String request = "UNKNOWN / HTTP/1.1\r\n" +
|
||||
|
@ -1005,14 +945,10 @@ public class RequestTest
|
|||
@Test
|
||||
public void testEncodedFormExtraMethod() throws Exception
|
||||
{
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
String actual = request.getParameter("name2");
|
||||
return "test2".equals(actual);
|
||||
}
|
||||
};
|
||||
|
||||
_connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().addFormEncodedMethod("Extra");
|
||||
|
@ -1031,17 +967,13 @@ public class RequestTest
|
|||
@Test
|
||||
public void test8859EncodedForm() throws Exception
|
||||
{
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
// Should be "testä"
|
||||
// "test" followed by a LATIN SMALL LETTER A WITH DIAERESIS
|
||||
request.setCharacterEncoding(StandardCharsets.ISO_8859_1.name());
|
||||
String actual = request.getParameter("name2");
|
||||
return "test\u00e4".equals(actual);
|
||||
}
|
||||
};
|
||||
|
||||
String content = "name1=test&name2=test%E4&name3=&name4=test";
|
||||
|
@ -1059,17 +991,13 @@ public class RequestTest
|
|||
@Test
|
||||
public void testUTF8EncodedForm() throws Exception
|
||||
{
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
// http://www.ltg.ed.ac.uk/~richard/utf-8.cgi?input=00e4&mode=hex
|
||||
// Should be "testä"
|
||||
// "test" followed by a LATIN SMALL LETTER A WITH DIAERESIS
|
||||
String actual = request.getParameter("name2");
|
||||
return "test\u00e4".equals(actual);
|
||||
}
|
||||
};
|
||||
|
||||
String content = "name1=test&name2=test%C3%A4&name3=&name4=test";
|
||||
|
@ -1094,8 +1022,7 @@ public class RequestTest
|
|||
Handler handler = new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException,
|
||||
ServletException
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
if (baseRequest.getDispatcherType() != DispatcherType.REQUEST)
|
||||
return;
|
||||
|
@ -1160,8 +1087,7 @@ public class RequestTest
|
|||
Handler handler = new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException,
|
||||
ServletException
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
Reader reader = request.getReader();
|
||||
|
@ -1206,8 +1132,7 @@ public class RequestTest
|
|||
Handler handler = new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException,
|
||||
ServletException
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
Reader reader = request.getReader();
|
||||
|
@ -1243,8 +1168,7 @@ public class RequestTest
|
|||
Handler handler = new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException,
|
||||
ServletException
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
response.sendRedirect("/foo");
|
||||
|
@ -1280,8 +1204,7 @@ public class RequestTest
|
|||
Handler handler = new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException,
|
||||
ServletException
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
InputStream in = request.getInputStream();
|
||||
|
@ -1324,14 +1247,10 @@ public class RequestTest
|
|||
{
|
||||
String response;
|
||||
|
||||
_handler._checker = new RequestTester()
|
||||
_handler._checker = (request, response1) ->
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
response.getOutputStream().println("Hello World");
|
||||
response1.getOutputStream().println("Hello World");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
response = _connector.getResponse(
|
||||
|
@ -1394,16 +1313,12 @@ public class RequestTest
|
|||
assertThat(response, containsString("Connection: keep-alive"));
|
||||
assertThat(response, containsString("Hello World"));
|
||||
|
||||
_handler._checker = new RequestTester()
|
||||
_handler._checker = (request, response12) ->
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
response.setHeader("Connection", "TE");
|
||||
response.addHeader("Connection", "Other");
|
||||
response.getOutputStream().println("Hello World");
|
||||
response12.setHeader("Connection", "TE");
|
||||
response12.addHeader("Connection", "Other");
|
||||
response12.getOutputStream().println("Hello World");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
response = _connector.getResponse(
|
||||
|
@ -1432,22 +1347,17 @@ public class RequestTest
|
|||
{
|
||||
final ArrayList<Cookie> cookies = new ArrayList<>();
|
||||
|
||||
_handler._checker = new RequestTester()
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
jakarta.servlet.http.Cookie[] ca = request.getCookies();
|
||||
Cookie[] ca = request.getCookies();
|
||||
if (ca != null)
|
||||
cookies.addAll(Arrays.asList(ca));
|
||||
response.getOutputStream().println("Hello World");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
String response;
|
||||
|
||||
cookies.clear();
|
||||
response = _connector.getResponse(
|
||||
"GET / HTTP/1.1\n" +
|
||||
"Host: whatever\n" +
|
||||
|
@ -1560,22 +1470,17 @@ public class RequestTest
|
|||
{
|
||||
final ArrayList<Cookie> cookies = new ArrayList<>();
|
||||
|
||||
_handler._checker = new RequestTester()
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
jakarta.servlet.http.Cookie[] ca = request.getCookies();
|
||||
Cookie[] ca = request.getCookies();
|
||||
if (ca != null)
|
||||
cookies.addAll(Arrays.asList(ca));
|
||||
response.getOutputStream().println("Hello World");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
String response;
|
||||
|
||||
cookies.clear();
|
||||
response = _connector.getResponse(
|
||||
"GET / HTTP/1.1\n" +
|
||||
"Host: whatever\n" +
|
||||
|
@ -1596,15 +1501,9 @@ public class RequestTest
|
|||
{
|
||||
final String[] cookie = new String[10];
|
||||
|
||||
_handler._checker = new RequestTester()
|
||||
_handler._checker = (request, response) ->
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
for (int i = 0; i < cookie.length; i++)
|
||||
{
|
||||
cookie[i] = null;
|
||||
}
|
||||
Arrays.fill(cookie, null);
|
||||
|
||||
Cookie[] cookies = request.getCookies();
|
||||
for (int i = 0; cookies != null && i < cookies.length; i++)
|
||||
|
@ -1612,7 +1511,6 @@ public class RequestTest
|
|||
cookie[i] = cookies[i].getValue();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
String request = "POST / HTTP/1.1\r\n" +
|
||||
|
@ -1628,7 +1526,7 @@ public class RequestTest
|
|||
_connector.getResponse(request);
|
||||
|
||||
assertEquals("value", cookie[0]);
|
||||
assertEquals(null, cookie[1]);
|
||||
assertNull(cookie[1]);
|
||||
|
||||
request = "POST / HTTP/1.1\r\n" +
|
||||
"Host: whatever\r\n" +
|
||||
|
@ -1641,8 +1539,8 @@ public class RequestTest
|
|||
"\r\n";
|
||||
|
||||
_connector.getResponse(request);
|
||||
assertEquals(null, cookie[0]);
|
||||
assertEquals(null, cookie[1]);
|
||||
assertNull(cookie[0]);
|
||||
assertNull(cookie[1]);
|
||||
|
||||
request = "POST / HTTP/1.1\r\n" +
|
||||
"Host: whatever\r\n" +
|
||||
|
@ -1659,13 +1557,13 @@ public class RequestTest
|
|||
_connector.getResponse(request);
|
||||
|
||||
assertEquals("value", cookie[0]);
|
||||
assertEquals(null, cookie[1]);
|
||||
assertNull(cookie[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHashDOSKeys() throws Exception
|
||||
{
|
||||
try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
|
||||
try (StacklessLogging ignored = new StacklessLogging(HttpChannel.class))
|
||||
{
|
||||
// Expecting maxFormKeys limit and Closing HttpParser exceptions...
|
||||
_server.setAttribute("org.eclipse.jetty.server.Request.maxFormContentSize", -1);
|
||||
|
@ -1681,7 +1579,7 @@ public class RequestTest
|
|||
// Using real evil keys!
|
||||
try (BufferedReader in = new BufferedReader(new FileReader(evilKeys)))
|
||||
{
|
||||
String key = null;
|
||||
String key;
|
||||
while ((key = in.readLine()) != null)
|
||||
{
|
||||
buf.append("&").append(key).append("=").append("x");
|
||||
|
@ -1698,14 +1596,7 @@ public class RequestTest
|
|||
}
|
||||
buf.append("&c=d");
|
||||
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
return "b".equals(request.getParameter("a")) && request.getParameter("c") == null;
|
||||
}
|
||||
};
|
||||
_handler._checker = (request, response) -> "b".equals(request.getParameter("a")) && request.getParameter("c") == null;
|
||||
|
||||
String request = "POST / HTTP/1.1\r\n" +
|
||||
"Host: whatever\r\n" +
|
||||
|
@ -1729,7 +1620,7 @@ public class RequestTest
|
|||
@Test
|
||||
public void testHashDOSSize() throws Exception
|
||||
{
|
||||
try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class))
|
||||
try (StacklessLogging ignored = new StacklessLogging(HttpChannel.class))
|
||||
{
|
||||
LOG.info("Expecting maxFormSize limit and too much data exceptions...");
|
||||
_server.setAttribute("org.eclipse.jetty.server.Request.maxFormContentSize", 3396);
|
||||
|
@ -1744,14 +1635,7 @@ public class RequestTest
|
|||
}
|
||||
buf.append("&c=d");
|
||||
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
@Override
|
||||
public boolean check(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
return "b".equals(request.getParameter("a")) && request.getParameter("c") == null;
|
||||
}
|
||||
};
|
||||
_handler._checker = (request, response) -> "b".equals(request.getParameter("a")) && request.getParameter("c") == null;
|
||||
|
||||
String request = "POST / HTTP/1.1\r\n" +
|
||||
"Host: whatever\r\n" +
|
||||
|
@ -1773,7 +1657,7 @@ public class RequestTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testNotSupportedCharacterEncoding() throws UnsupportedEncodingException
|
||||
public void testNotSupportedCharacterEncoding()
|
||||
{
|
||||
Request request = new Request(null, null);
|
||||
assertThrows(UnsupportedEncodingException.class, () -> request.setCharacterEncoding("doesNotExist"));
|
||||
|
@ -1798,7 +1682,7 @@ public class RequestTest
|
|||
|
||||
assertNotNull(request.getScheme());
|
||||
assertNotNull(request.getServerName());
|
||||
assertNotNull(request.getServerPort());
|
||||
request.getServerPort();
|
||||
|
||||
assertNotNull(request.getAttributeNames());
|
||||
assertFalse(request.getAttributeNames().hasMoreElements());
|
||||
|
@ -1812,29 +1696,33 @@ public class RequestTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testAmbiguousPaths() throws Exception
|
||||
public void testAmbiguousSegments() throws Exception
|
||||
{
|
||||
_handler._checker = (request, response) -> true;
|
||||
|
||||
String request = "GET /ambiguous/..;/path HTTP/1.0\r\n" +
|
||||
"Host: whatever\r\n" +
|
||||
"\r\n";
|
||||
|
||||
_connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setHttpCompliance(HttpCompliance.RFC7230);
|
||||
_connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setUriCompliance(UriCompliance.SAFE);
|
||||
assertThat(_connector.getResponse(request), startsWith("HTTP/1.1 400"));
|
||||
|
||||
_connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setHttpCompliance(HttpCompliance.RFC7230_LEGACY);
|
||||
assertThat(_connector.getResponse(request), startsWith("HTTP/1.1 200"));
|
||||
|
||||
_connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setHttpCompliance(HttpCompliance.RFC2616);
|
||||
assertThat(_connector.getResponse(request), startsWith("HTTP/1.1 400"));
|
||||
|
||||
_connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setHttpCompliance(HttpCompliance.RFC2616_LEGACY);
|
||||
_connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setUriCompliance(UriCompliance.STRICT);
|
||||
assertThat(_connector.getResponse(request), startsWith("HTTP/1.1 200"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPushBuilder() throws Exception
|
||||
public void testAmbiguousSeparators() throws Exception
|
||||
{
|
||||
_handler._checker = (request, response) -> true;
|
||||
String request = "GET /ambiguous/%2f/path HTTP/1.0\r\n" +
|
||||
"Host: whatever\r\n" +
|
||||
"\r\n";
|
||||
_connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setUriCompliance(UriCompliance.SAFE);
|
||||
assertThat(_connector.getResponse(request), startsWith("HTTP/1.1 400"));
|
||||
_connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setUriCompliance(UriCompliance.STRICT);
|
||||
assertThat(_connector.getResponse(request), startsWith("HTTP/1.1 200"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPushBuilder()
|
||||
{
|
||||
String uri = "/foo/something";
|
||||
Request request = new TestRequest(null, null);
|
||||
|
@ -1848,42 +1736,15 @@ public class RequestTest
|
|||
PushBuilder builder = request.newPushBuilder();
|
||||
assertNotNull(builder);
|
||||
assertEquals("GET", builder.getMethod());
|
||||
assertThrows(NullPointerException.class, () ->
|
||||
{
|
||||
builder.method(null);
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
{
|
||||
builder.method("");
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
{
|
||||
builder.method(" ");
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
{
|
||||
builder.method("POST");
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
{
|
||||
builder.method("PUT");
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
{
|
||||
builder.method("DELETE");
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
{
|
||||
builder.method("CONNECT");
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
{
|
||||
builder.method("OPTIONS");
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
{
|
||||
builder.method("TRACE");
|
||||
});
|
||||
assertThrows(NullPointerException.class, () -> builder.method(null));
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.method(""));
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.method(" "));
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.method("POST"));
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.method("PUT"));
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.method("DELETE"));
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.method("CONNECT"));
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.method("OPTIONS"));
|
||||
assertThrows(IllegalArgumentException.class, () -> builder.method("TRACE"));
|
||||
assertEquals(TestRequest.TEST_SESSION_ID, builder.getSessionId());
|
||||
builder.path("/foo/something-else.txt");
|
||||
assertEquals("/foo/something-else.txt", builder.getPath());
|
||||
|
@ -1895,7 +1756,7 @@ public class RequestTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testPushBuilderWithIdNoAuth() throws Exception
|
||||
public void testPushBuilderWithIdNoAuth()
|
||||
{
|
||||
String uri = "/foo/something";
|
||||
Request request = new TestRequest(null, null)
|
||||
|
@ -1914,7 +1775,7 @@ public class RequestTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testServletPathMapping() throws Exception
|
||||
public void testServletPathMapping()
|
||||
{
|
||||
ServletPathSpec spec;
|
||||
String uri;
|
||||
|
@ -2016,7 +1877,7 @@ public class RequestTest
|
|||
boolean check(HttpServletRequest request, HttpServletResponse response) throws IOException;
|
||||
}
|
||||
|
||||
private class TestRequest extends Request
|
||||
private static class TestRequest extends Request
|
||||
{
|
||||
public static final String TEST_SESSION_ID = "abc123";
|
||||
Response _response = new Response(null, null);
|
||||
|
@ -2047,16 +1908,7 @@ public class RequestTest
|
|||
@Override
|
||||
public Principal getUserPrincipal()
|
||||
{
|
||||
return new Principal()
|
||||
{
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "user";
|
||||
}
|
||||
|
||||
};
|
||||
return () -> "user";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2072,11 +1924,9 @@ public class RequestTest
|
|||
}
|
||||
}
|
||||
|
||||
private class RequestHandler extends AbstractHandler
|
||||
private static class RequestHandler extends AbstractHandler
|
||||
{
|
||||
private RequestTester _checker;
|
||||
@SuppressWarnings("unused")
|
||||
private String _content;
|
||||
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
|
@ -2086,7 +1936,7 @@ public class RequestTest
|
|||
if (request.getContentLength() > 0 &&
|
||||
!request.getContentType().startsWith(MimeTypes.Type.FORM_ENCODED.asString()) &&
|
||||
!request.getContentType().startsWith("multipart/form-data"))
|
||||
_content = IO.toString(request.getInputStream());
|
||||
assertNotNull(IO.toString(request.getInputStream()));
|
||||
|
||||
if (_checker != null && _checker.check(request, response))
|
||||
response.setStatus(200);
|
||||
|
@ -2095,7 +1945,7 @@ public class RequestTest
|
|||
}
|
||||
}
|
||||
|
||||
private class MultiPartRequestHandler extends AbstractHandler
|
||||
private static class MultiPartRequestHandler extends AbstractHandler
|
||||
{
|
||||
RequestTester checker;
|
||||
File tmpDir;
|
||||
|
@ -2153,7 +2003,7 @@ public class RequestTest
|
|||
}
|
||||
}
|
||||
|
||||
private class BadMultiPartRequestHandler extends AbstractHandler
|
||||
private static class BadMultiPartRequestHandler extends AbstractHandler
|
||||
{
|
||||
final Path tmpDir;
|
||||
|
||||
|
@ -2174,7 +2024,6 @@ public class RequestTest
|
|||
|
||||
try
|
||||
{
|
||||
|
||||
MultipartConfigElement mpce = new MultipartConfigElement(tmpDir.toString(), -1, -1, 2);
|
||||
request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
|
||||
|
||||
|
@ -2188,42 +2037,4 @@ public class RequestTest
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TestUserIdentityScope implements UserIdentity.Scope
|
||||
{
|
||||
private ContextHandler _handler;
|
||||
private String _contextPath;
|
||||
private String _name;
|
||||
|
||||
public TestUserIdentityScope(ContextHandler handler, String contextPath, String name)
|
||||
{
|
||||
_handler = handler;
|
||||
_contextPath = contextPath;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContextHandler getContextHandler()
|
||||
{
|
||||
return _handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContextPath()
|
||||
{
|
||||
return _contextPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getRoleRefMap()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,12 +41,12 @@ import jakarta.servlet.http.HttpServlet;
|
|||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.eclipse.jetty.http.DateGenerator;
|
||||
import org.eclipse.jetty.http.HttpCompliance;
|
||||
import org.eclipse.jetty.http.HttpContent;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.http.UriCompliance;
|
||||
import org.eclipse.jetty.logging.StacklessLogging;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
|
@ -112,7 +112,7 @@ public class DefaultServletTest
|
|||
|
||||
connector = new LocalConnector(server);
|
||||
connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false);
|
||||
connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setHttpCompliance(HttpCompliance.RFC7230_LEGACY); // allow ambiguous path segments
|
||||
connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setUriCompliance(UriCompliance.STRICT); // allow ambiguous path segments
|
||||
|
||||
File extraJarResources = MavenTestingUtils.getTestResourceFile(ODD_JAR);
|
||||
URL[] urls = new URL[]{extraJarResources.toURI().toURL()};
|
||||
|
|
|
@ -29,7 +29,7 @@ import jakarta.servlet.ServletException;
|
|||
import jakarta.servlet.http.HttpServlet;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.eclipse.jetty.http.HttpCompliance;
|
||||
import org.eclipse.jetty.http.UriCompliance;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
|
@ -109,7 +109,7 @@ public class RequestURITest
|
|||
ServerConnector connector = new ServerConnector(server);
|
||||
connector.setPort(0);
|
||||
server.addConnector(connector);
|
||||
connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setHttpCompliance(HttpCompliance.RFC7230_LEGACY); // Allow ambiguous segments
|
||||
connector.getBean(HttpConnectionFactory.class).getHttpConfiguration().setUriCompliance(UriCompliance.STRICT); // Allow ambiguous segments
|
||||
|
||||
ServletContextHandler context = new ServletContextHandler();
|
||||
context.setContextPath("/");
|
||||
|
|
Loading…
Reference in New Issue