com.signalfx.shaded.jetty.http.HttpCompliance Maven / Gradle / Ivy
Show all versions of signalfx-codahale Show documentation
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package com.signalfx.shaded.jetty.http;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
/**
* HTTP compliance modes for Jetty HTTP parsing and handling.
* A Compliance mode consists of a set of {@link HttpComplianceSection}s which are applied
* when the mode is enabled.
*
* Currently the set of modes is an enum and cannot be dynamically extended, but future major releases may convert this
* to a class. To modify modes there are four custom modes that can be modified by setting the property
* com.signalfx.shaded.jetty.http.HttpCompliance.CUSTOMn
(where 'n' is '0', '1', '2' or '3'), to a comma separated
* list of sections. The list should start with one of the following strings:
* - 0
- No {@link HttpComplianceSection}s
* - *
- All {@link HttpComplianceSection}s
* - RFC2616
- The set of {@link HttpComplianceSection}s application to https://tools.ietf.org/html/rfc2616,
* but not https://tools.ietf.org/html/rfc7230
* - RFC7230
- The set of {@link HttpComplianceSection}s application to https://tools.ietf.org/html/rfc7230
*
* The remainder of the list can contain then names of {@link HttpComplianceSection}s to include them in the mode, or prefixed
* with a '-' to exclude thm from the mode. Note that Jetty's modes may have some historic minor differences from the strict
* RFC compliance, for example the RFC2616_LEGACY
HttpCompliance is defined as
* RFC2616,-FIELD_COLON,-METHOD_CASE_SENSITIVE
.
*
* Note also that the {@link EnumSet} return by {@link HttpCompliance#sections()} is mutable, so that modes may
* be altered in code and will affect all usages of the mode.
*/
public enum HttpCompliance // TODO in Jetty-10 convert this enum to a class so that extra custom modes can be defined dynamically
{
/**
* A Legacy compliance mode to match jetty's behavior prior to RFC2616 and RFC7230.
*/
LEGACY(sectionsBySpec("0,METHOD_CASE_SENSITIVE")),
/**
* The legacy RFC2616 support, which excludes
* {@link HttpComplianceSection#METHOD_CASE_SENSITIVE},
* {@link HttpComplianceSection#FIELD_COLON},
* {@link HttpComplianceSection#TRANSFER_ENCODING_WITH_CONTENT_LENGTH},
* {@link HttpComplianceSection#MULTIPLE_CONTENT_LENGTHS},
* {@link HttpComplianceSection#NO_AMBIGUOUS_PATH_SEGMENTS} and
* {@link HttpComplianceSection#NO_AMBIGUOUS_PATH_SEPARATORS}.
*/
RFC2616_LEGACY(sectionsBySpec("RFC2616,-FIELD_COLON,-METHOD_CASE_SENSITIVE,-TRANSFER_ENCODING_WITH_CONTENT_LENGTH,-MULTIPLE_CONTENT_LENGTHS")),
/**
* The strict RFC2616 support mode
*/
RFC2616(sectionsBySpec("RFC2616")),
/**
* Jetty's legacy RFC7230 support, which excludes
* {@link HttpComplianceSection#METHOD_CASE_SENSITIVE}.
*/
RFC7230_LEGACY(sectionsBySpec("RFC7230,-METHOD_CASE_SENSITIVE")),
/**
* The RFC7230 support mode
*/
RFC7230(sectionsBySpec("RFC7230")),
/**
* The RFC7230 support mode with no ambiguous URIs
*/
RFC7230_NO_AMBIGUOUS_URIS(sectionsBySpec("RFC7230,NO_AMBIGUOUS_PATH_SEGMENTS,NO_AMBIGUOUS_PATH_SEPARATORS")),
/**
* Custom compliance mode that can be defined with System property com.signalfx.shaded.jetty.http.HttpCompliance.CUSTOM0
*/
@Deprecated
CUSTOM0(sectionsByProperty("CUSTOM0")),
/**
* Custom compliance mode that can be defined with System property com.signalfx.shaded.jetty.http.HttpCompliance.CUSTOM1
*/
@Deprecated
CUSTOM1(sectionsByProperty("CUSTOM1")),
/**
* Custom compliance mode that can be defined with System property com.signalfx.shaded.jetty.http.HttpCompliance.CUSTOM2
*/
@Deprecated
CUSTOM2(sectionsByProperty("CUSTOM2")),
/**
* Custom compliance mode that can be defined with System property com.signalfx.shaded.jetty.http.HttpCompliance.CUSTOM3
*/
@Deprecated
CUSTOM3(sectionsByProperty("CUSTOM3"));
public static final String VIOLATIONS_ATTR = "com.signalfx.shaded.jetty.http.compliance.violations";
private static EnumSet sectionsByProperty(String property)
{
String s = System.getProperty(HttpCompliance.class.getName() + property);
return sectionsBySpec(s == null ? "*" : s);
}
static EnumSet sectionsBySpec(String spec)
{
EnumSet sections;
String[] elements = spec.split("\\s*,\\s*");
int i = 0;
switch (elements[i])
{
case "0":
sections = EnumSet.noneOf(HttpComplianceSection.class);
i++;
break;
case "RFC2616":
i++;
sections = EnumSet.complementOf(EnumSet.of(
HttpComplianceSection.NO_FIELD_FOLDING,
HttpComplianceSection.NO_HTTP_0_9,
HttpComplianceSection.NO_AMBIGUOUS_PATH_SEGMENTS,
HttpComplianceSection.NO_AMBIGUOUS_PATH_SEPARATORS,
HttpComplianceSection.NO_UTF16_ENCODINGS,
HttpComplianceSection.NO_AMBIGUOUS_EMPTY_SEGMENT,
HttpComplianceSection.NO_AMBIGUOUS_PATH_ENCODING));
break;
case "*":
case "RFC7230":
i++;
sections = EnumSet.complementOf(EnumSet.of(
HttpComplianceSection.NO_AMBIGUOUS_PATH_SEGMENTS,
HttpComplianceSection.NO_AMBIGUOUS_PATH_SEPARATORS,
HttpComplianceSection.NO_UTF16_ENCODINGS,
HttpComplianceSection.NO_AMBIGUOUS_EMPTY_SEGMENT,
HttpComplianceSection.NO_AMBIGUOUS_PATH_ENCODING));
break;
default:
sections = EnumSet.noneOf(HttpComplianceSection.class);
break;
}
while (i < elements.length)
{
String element = elements[i++];
boolean exclude = element.startsWith("-");
if (exclude)
element = element.substring(1);
HttpComplianceSection section = HttpComplianceSection.valueOf(element);
if (exclude)
sections.remove(section);
else
sections.add(section);
}
return sections;
}
private static final Map __required = new HashMap<>();
static
{
for (HttpComplianceSection section : HttpComplianceSection.values())
{
for (HttpCompliance compliance : HttpCompliance.values())
{
if (compliance.sections().contains(section))
{
__required.put(section, compliance);
break;
}
}
}
}
/**
* @param section The section to query
* @return The minimum compliance required to enable the section.
*/
public static HttpCompliance requiredCompliance(HttpComplianceSection section)
{
return __required.get(section);
}
private final EnumSet _sections;
HttpCompliance(EnumSet sections)
{
_sections = sections;
}
/**
* Get the set of {@link HttpComplianceSection}s supported by this compliance mode. This set
* is mutable, so it can be modified. Any modification will affect all usages of the mode
* within the same {@link ClassLoader}.
*
* @return The set of {@link HttpComplianceSection}s supported by this compliance mode.
*/
public EnumSet sections()
{
return _sections;
}
private static final EnumMap __uriViolations = new EnumMap<>(HttpURI.Violation.class);
static
{
// create a map from Violation to compliance in a loop, so that any new violations added are detected with ISE
for (HttpURI.Violation violation : HttpURI.Violation.values())
{
switch (violation)
{
case SEPARATOR:
__uriViolations.put(violation, HttpComplianceSection.NO_AMBIGUOUS_PATH_SEPARATORS);
break;
case SEGMENT:
__uriViolations.put(violation, HttpComplianceSection.NO_AMBIGUOUS_PATH_SEGMENTS);
break;
case PARAM:
__uriViolations.put(violation, HttpComplianceSection.NO_AMBIGUOUS_PATH_PARAMETERS);
break;
case ENCODING:
__uriViolations.put(violation, HttpComplianceSection.NO_AMBIGUOUS_PATH_ENCODING);
break;
case EMPTY:
__uriViolations.put(violation, HttpComplianceSection.NO_AMBIGUOUS_EMPTY_SEGMENT);
break;
case UTF16:
__uriViolations.put(violation, HttpComplianceSection.NO_UTF16_ENCODINGS);
break;
default:
throw new IllegalStateException();
}
}
}
public static String checkUriCompliance(HttpCompliance compliance, HttpURI uri)
{
for (HttpURI.Violation violation : HttpURI.Violation.values())
{
if (uri.hasViolation(violation) && (compliance == null || compliance.sections().contains(__uriViolations.get(violation))))
return violation.getMessage();
}
return null;
}
}