From aa8bd5d8201615b9faacd7675ef97e8adf32167d Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Mon, 23 Nov 2020 14:56:54 +0100 Subject: [PATCH] replace Trie interface and impls usage with new *Index interfaces and Builders Signed-off-by: Ludovic Orban --- .../org/eclipse/jetty/http/HttpGenerator.java | 17 +- .../org/eclipse/jetty/http/HttpHeader.java | 22 +- .../eclipse/jetty/http/HttpHeaderValue.java | 20 +- .../org/eclipse/jetty/http/HttpMethod.java | 33 +- .../org/eclipse/jetty/http/HttpParser.java | 148 +++-- .../org/eclipse/jetty/http/HttpScheme.java | 16 +- .../org/eclipse/jetty/http/HttpVersion.java | 16 +- .../org/eclipse/jetty/http/MimeTypes.java | 35 +- .../jetty/http/pathmap/PathMappings.java | 45 +- .../org/eclipse/jetty/http2/HTTP2Cipher.java | 591 +++++++++--------- .../jetty/http2/hpack/HpackContext.java | 13 +- .../jetty/rewrite/handler/MsieRule.java | 25 +- .../jetty/rewrite/handler/MsieSslRule.java | 24 +- .../server/ForwardedRequestCustomizer.java | 61 +- .../jetty/server/HttpChannelOverHttp.java | 51 +- .../jetty/server/HttpConfiguration.java | 14 +- .../handler/ContextHandlerCollection.java | 79 ++- .../eclipse/jetty/util/ajax/AsyncJSON.java | 10 +- .../org/eclipse/jetty/util/AbstractTrie.java | 104 ++- .../eclipse/jetty/util/ArrayTernaryTrie.java | 129 ++-- .../org/eclipse/jetty/util/ArrayTrie.java | 48 +- .../org/eclipse/jetty/util/EmptyTrie.java | 99 +++ .../java/org/eclipse/jetty/util/Index.java | 364 +++++++++++ .../org/eclipse/jetty/util/StringUtil.java | 20 +- .../java/org/eclipse/jetty/util/TreeTrie.java | 24 +- .../java/org/eclipse/jetty/util/Trie.java | 230 ------- .../org/eclipse/jetty/util/IndexTest.java | 65 ++ .../java/org/eclipse/jetty/util/TrieTest.java | 76 ++- .../eclipse/jetty/webapp/ClassMatcher.java | 12 +- .../jetty/websocket/core/ExtensionConfig.java | 17 +- 30 files changed, 1363 insertions(+), 1045 deletions(-) create mode 100644 jetty-util/src/main/java/org/eclipse/jetty/util/EmptyTrie.java create mode 100644 jetty-util/src/main/java/org/eclipse/jetty/util/Index.java delete mode 100644 jetty-util/src/main/java/org/eclipse/jetty/util/Trie.java create mode 100644 jetty-util/src/test/java/org/eclipse/jetty/util/IndexTest.java diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java index b9c9b04576e1..16e122bb0b09 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java @@ -26,10 +26,9 @@ import java.util.stream.Stream; import org.eclipse.jetty.http.HttpTokens.EndOfContent; -import org.eclipse.jetty.util.ArrayTrie; import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.Trie; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -92,13 +91,11 @@ public enum Result private final int _send; private static final int SEND_SERVER = 0x01; private static final int SEND_XPOWEREDBY = 0x02; - private static final Trie ASSUMED_CONTENT_METHODS = new ArrayTrie<>(8); - - static - { - ASSUMED_CONTENT_METHODS.put(HttpMethod.POST.asString(), Boolean.TRUE); - ASSUMED_CONTENT_METHODS.put(HttpMethod.PUT.asString(), Boolean.TRUE); - } + private static final Index ASSUMED_CONTENT_METHODS = new Index.Builder() + .caseSensitive(false) + .with(HttpMethod.POST.asString(), Boolean.TRUE) + .with(HttpMethod.PUT.asString(), Boolean.TRUE) + .build(); public static void setJettyVersion(String serverVersion) { @@ -679,7 +676,7 @@ else if (contentLength != field.getLongValue()) // Calculate how to end _content and connection, _content length and transfer encoding // settings from http://tools.ietf.org/html/rfc7230#section-3.3.3 - boolean assumedContentRequest = request != null && Boolean.TRUE.equals(ASSUMED_CONTENT_METHODS.get(request.getMethod())); + boolean assumedContentRequest = request != null && ASSUMED_CONTENT_METHODS.get(request.getMethod()) != null; boolean assumedContent = assumedContentRequest || contentType || chunkedHint; boolean noContentRequest = request != null && contentLength <= 0 && !assumedContent; diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java index ee5282638d46..c1414d287822 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeader.java @@ -20,9 +20,8 @@ import java.nio.ByteBuffer; -import org.eclipse.jetty.util.ArrayTrie; +import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.Trie; public enum HttpHeader { @@ -133,21 +132,12 @@ public enum HttpHeader C_AUTHORITY(":authority", true), C_PATH(":path", true), C_STATUS(":status", true), - C_PROTOCOL(":protocol"), + C_PROTOCOL(":protocol"); - UNKNOWN("::UNKNOWN::", true); - - public static final Trie CACHE = new ArrayTrie<>(630); - - static - { - for (HttpHeader header : HttpHeader.values()) - { - if (header != UNKNOWN) - if (!CACHE.put(header.toString(), header)) - throw new IllegalStateException(); - } - } + public static final Index CACHE = new Index.Builder() + .caseSensitive(false) + .withAll(HttpHeader.values(), HttpHeader::toString) + .build(); private final String _string; private final String _lowerCase; diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeaderValue.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeaderValue.java index e4d84eb72a4e..ae475883a6e8 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeaderValue.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeaderValue.java @@ -21,9 +21,8 @@ import java.nio.ByteBuffer; import java.util.EnumSet; -import org.eclipse.jetty.util.ArrayTrie; import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.Trie; +import org.eclipse.jetty.util.Index; /** * @@ -40,19 +39,12 @@ public enum HttpHeaderValue TE("TE"), BYTES("bytes"), NO_CACHE("no-cache"), - UPGRADE("Upgrade"), - UNKNOWN("::UNKNOWN::"); + UPGRADE("Upgrade"); - public static final Trie CACHE = new ArrayTrie(); - - static - { - for (HttpHeaderValue value : HttpHeaderValue.values()) - { - if (value != UNKNOWN) - CACHE.put(value.toString(), value); - } - } + public static final Index CACHE = new Index.Builder() + .caseSensitive(false) + .withAll(HttpHeaderValue.values(), HttpHeaderValue::toString) + .build(); private final String _string; private final ByteBuffer _buffer; diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java index 9e4dd2ee89f8..a329ddd41d14 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpMethod.java @@ -20,10 +20,8 @@ import java.nio.ByteBuffer; -import org.eclipse.jetty.util.ArrayTernaryTrie; -import org.eclipse.jetty.util.ArrayTrie; +import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.Trie; /** * Known HTTP Methods @@ -142,29 +140,24 @@ public String toString() return _method; } - public static final Trie INSENSITIVE_CACHE = new ArrayTrie<>(252); - public static final Trie CACHE = new ArrayTernaryTrie<>(false, 300); - public static final Trie LOOK_AHEAD = new ArrayTernaryTrie<>(false, 330); + public static final Index INSENSITIVE_CACHE = new Index.Builder() + .caseSensitive(false) + .withAll(HttpMethod.values(), HttpMethod::asString) + .build(); + public static final Index CACHE = new Index.Builder() + .caseSensitive(true) + .withAll(HttpMethod.values(), HttpMethod::asString) + .build(); + public static final Index LOOK_AHEAD = new Index.Builder() + .caseSensitive(true) + .withAll(HttpMethod.values(), httpMethod -> httpMethod.asString() + ' ') + .build(); public static final int ACL_AS_INT = ('A' & 0xff) << 24 | ('C' & 0xFF) << 16 | ('L' & 0xFF) << 8 | (' ' & 0xFF); public static final int GET_AS_INT = ('G' & 0xff) << 24 | ('E' & 0xFF) << 16 | ('T' & 0xFF) << 8 | (' ' & 0xFF); public static final int PRI_AS_INT = ('P' & 0xff) << 24 | ('R' & 0xFF) << 16 | ('I' & 0xFF) << 8 | (' ' & 0xFF); public static final int PUT_AS_INT = ('P' & 0xff) << 24 | ('U' & 0xFF) << 16 | ('T' & 0xFF) << 8 | (' ' & 0xFF); public static final int POST_AS_INT = ('P' & 0xff) << 24 | ('O' & 0xFF) << 16 | ('S' & 0xFF) << 8 | ('T' & 0xFF); public static final int HEAD_AS_INT = ('H' & 0xff) << 24 | ('E' & 0xFF) << 16 | ('A' & 0xFF) << 8 | ('D' & 0xFF); - static - { - for (HttpMethod method : HttpMethod.values()) - { - if (!INSENSITIVE_CACHE.put(method.asString(), method)) - throw new IllegalStateException("INSENSITIVE_CACHE too small: " + method); - - if (!CACHE.put(method.asString(), method)) - throw new IllegalStateException("CACHE too small: " + method); - - if (!LOOK_AHEAD.put(method.asString() + ' ', method)) - throw new IllegalStateException("LOOK_AHEAD too small: " + method); - } - } /** * Optimized lookup to find a method name and trailing space in a byte array. diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java index 75b6694483f9..2adfdcd0f87e 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java @@ -21,15 +21,15 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.EnumSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import org.eclipse.jetty.http.HttpTokens.EndOfContent; -import org.eclipse.jetty.util.ArrayTernaryTrie; -import org.eclipse.jetty.util.ArrayTrie; import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.Trie; import org.eclipse.jetty.util.Utf8StringBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,7 +67,7 @@ *

*

* For performance, the parse is heavily dependent on the - * {@link Trie#getBest(ByteBuffer, int, int)} method to look ahead in a + * {@link Index#getBest(ByteBuffer, int, int)} method to look ahead in a * single pass for both the structure ( : and CRLF ) and semantic (which * header and value) of a header. Specifically the static {@link HttpHeader#CACHE} * is used to lookup common combinations of headers and values @@ -106,8 +106,74 @@ public class HttpParser * determine the header name even if the name:value combination is not cached * */ - public static final Trie CACHE = new ArrayTrie<>(2048); - private static final Trie NO_CACHE = Trie.empty(true); + public static final Index CACHE = new Index.Builder() + .caseSensitive(false) + .with(new HttpField(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE)) + .with(new HttpField(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE)) + .with(new HttpField(HttpHeader.CONNECTION, HttpHeaderValue.UPGRADE)) + .with(new HttpField(HttpHeader.ACCEPT_ENCODING, "gzip")) + .with(new HttpField(HttpHeader.ACCEPT_ENCODING, "gzip, deflate")) + .with(new HttpField(HttpHeader.ACCEPT_ENCODING, "gzip, deflate, br")) + .with(new HttpField(HttpHeader.ACCEPT_ENCODING, "gzip,deflate,sdch")) + .with(new HttpField(HttpHeader.ACCEPT_LANGUAGE, "en-US,enq=0.5")) + .with(new HttpField(HttpHeader.ACCEPT_LANGUAGE, "en-GB,en-USq=0.8,enq=0.6")) + .with(new HttpField(HttpHeader.ACCEPT_LANGUAGE, "en-AU,enq=0.9,it-ITq=0.8,itq=0.7,en-GBq=0.6,en-USq=0.5")) + .with(new HttpField(HttpHeader.ACCEPT_CHARSET, "ISO-8859-1,utf-8q=0.7,*q=0.3")) + .with(new HttpField(HttpHeader.ACCEPT, "*/*")) + .with(new HttpField(HttpHeader.ACCEPT, "image/png,image/*q=0.8,*/*q=0.5")) + .with(new HttpField(HttpHeader.ACCEPT, "text/html,application/xhtml+xml,application/xmlq=0.9,*/*q=0.8")) + .with(new HttpField(HttpHeader.ACCEPT, "text/html,application/xhtml+xml,application/xmlq=0.9,image/webp,image/apng,*/*q=0.8")) + .with(new HttpField(HttpHeader.ACCEPT_RANGES, HttpHeaderValue.BYTES)) + .with(new HttpField(HttpHeader.PRAGMA, "no-cache")) + .with(new HttpField(HttpHeader.CACHE_CONTROL, "private, no-cache, no-cache=Set-Cookie, proxy-revalidate")) + .with(new HttpField(HttpHeader.CACHE_CONTROL, "no-cache")) + .with(new HttpField(HttpHeader.CACHE_CONTROL, "max-age=0")) + .with(new HttpField(HttpHeader.CONTENT_LENGTH, "0")) + .with(new HttpField(HttpHeader.CONTENT_ENCODING, "gzip")) + .with(new HttpField(HttpHeader.CONTENT_ENCODING, "deflate")) + .with(new HttpField(HttpHeader.TRANSFER_ENCODING, "chunked")) + .with(new HttpField(HttpHeader.EXPIRES, "Fri, 01 Jan 1990 00:00:00 GMT")) + .withAll(() -> + { + Map map = new LinkedHashMap<>(); + // Add common Content types as fields + for (String type : new String[]{ + "text/plain", "text/html", "text/xml", "text/json", "application/json", "application/x-www-form-urlencoded" + }) + { + HttpField field = new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type); + map.put(field.toString(), field); + + for (String charset : new String[]{"utf-8", "iso-8859-1"}) + { + PreEncodedHttpField field1 = new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type + ";charset=" + charset); + map.put(field1.toString(), field1); + PreEncodedHttpField field2 = new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type + "; charset=" + charset); + map.put(field2.toString(), field2); + PreEncodedHttpField field3 = new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type + ";charset=" + charset.toUpperCase(Locale.ENGLISH)); + map.put(field3.toString(), field3); + PreEncodedHttpField field4 = new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type + "; charset=" + charset.toUpperCase(Locale.ENGLISH)); + map.put(field4.toString(), field4); + } + } + return map; + }) + .withAll(() -> + { + Map map = new LinkedHashMap<>(); + for (HttpHeader h : HttpHeader.values()) + { + HttpField httpField = new HttpField(h, (String)null); + map.put(httpField.toString(), httpField); + } + return map; + }) + .build(); + private static final Index.Mutable NO_CACHE = new Index.Builder() + .caseSensitive(false) + .mutable() + .maxCapacity(0) + .build(); // States public enum FieldState @@ -181,65 +247,12 @@ public enum State private boolean _headResponse; private boolean _cr; private ByteBuffer _contentChunk; - private Trie _fieldCache; + private Index.Mutable _fieldCache; private int _length; private final StringBuilder _string = new StringBuilder(); private int _headerCacheSize = 1024; private boolean _headerCacheCaseSensitive; - static - { - CACHE.put(new HttpField(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE)); - CACHE.put(new HttpField(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE)); - CACHE.put(new HttpField(HttpHeader.CONNECTION, HttpHeaderValue.UPGRADE)); - CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING, "gzip")); - CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING, "gzip, deflate")); - CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING, "gzip, deflate, br")); - CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING, "gzip,deflate,sdch")); - CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE, "en-US,en;q=0.5")); - CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE, "en-GB,en-US;q=0.8,en;q=0.6")); - CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE, "en-AU,en;q=0.9,it-IT;q=0.8,it;q=0.7,en-GB;q=0.6,en-US;q=0.5")); - CACHE.put(new HttpField(HttpHeader.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.3")); - CACHE.put(new HttpField(HttpHeader.ACCEPT, "*/*")); - CACHE.put(new HttpField(HttpHeader.ACCEPT, "image/png,image/*;q=0.8,*/*;q=0.5")); - CACHE.put(new HttpField(HttpHeader.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")); - CACHE.put(new HttpField(HttpHeader.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")); - CACHE.put(new HttpField(HttpHeader.ACCEPT_RANGES, HttpHeaderValue.BYTES)); - CACHE.put(new HttpField(HttpHeader.PRAGMA, "no-cache")); - CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL, "private, no-cache, no-cache=Set-Cookie, proxy-revalidate")); - CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL, "no-cache")); - CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL, "max-age=0")); - CACHE.put(new HttpField(HttpHeader.CONTENT_LENGTH, "0")); - CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING, "gzip")); - CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING, "deflate")); - CACHE.put(new HttpField(HttpHeader.TRANSFER_ENCODING, "chunked")); - CACHE.put(new HttpField(HttpHeader.EXPIRES, "Fri, 01 Jan 1990 00:00:00 GMT")); - - // Add common Content types as fields - for (String type : new String[]{ - "text/plain", "text/html", "text/xml", "text/json", "application/json", "application/x-www-form-urlencoded" - }) - { - HttpField field = new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type); - CACHE.put(field); - - for (String charset : new String[]{"utf-8", "iso-8859-1"}) - { - CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type + ";charset=" + charset)); - CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type + "; charset=" + charset)); - CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type + ";charset=" + charset.toUpperCase(Locale.ENGLISH))); - CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE, type + "; charset=" + charset.toUpperCase(Locale.ENGLISH))); - } - } - - // Add headers with null values so HttpParser can avoid looking up name again for unknown values - for (HttpHeader h : HttpHeader.values()) - { - if (!h.isPseudo() && !CACHE.put(new HttpField(h, (String)null))) - throw new IllegalStateException("CACHE FULL"); - } - } - private static HttpCompliance compliance() { return RFC7230; @@ -1052,14 +1065,19 @@ else if (_endOfContent == EndOfContent.CHUNKED_CONTENT) if (_fieldCache == null) { _fieldCache = (getHeaderCacheSize() > 0 && (_version != null && _version == HttpVersion.HTTP_1_1)) - ? new ArrayTernaryTrie<>(getHeaderCacheSize()) + ? new Index.Builder() + .caseSensitive(false) + .mutable() + .maxCapacity(getHeaderCacheSize()) + .build() : NO_CACHE; } - if (!_fieldCache.isFull()) + if (_field == null) + _field = new HttpField(_header, caseInsensitiveHeader(_headerString, _header.asString()), _valueString); + if (!_fieldCache.put(_field)) { - if (_field == null) - _field = new HttpField(_header, caseInsensitiveHeader(_headerString, _header.asString()), _valueString); + _fieldCache.clear(); _fieldCache.put(_field); } } @@ -1898,7 +1916,7 @@ protected void setState(FieldState state) _fieldState = state; } - public Trie getFieldCache() + public Index getFieldCache() { return _fieldCache; } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpScheme.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpScheme.java index 01f85c1491a0..da8369988de9 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpScheme.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpScheme.java @@ -20,9 +20,8 @@ import java.nio.ByteBuffer; -import org.eclipse.jetty.util.ArrayTrie; import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.Trie; +import org.eclipse.jetty.util.Index; /** * HTTP and WebSocket Schemes @@ -34,15 +33,10 @@ public enum HttpScheme WS("ws", 80), WSS("wss", 443); - public static final Trie CACHE = new ArrayTrie(); - - static - { - for (HttpScheme version : HttpScheme.values()) - { - CACHE.put(version.asString(), version); - } - } + public static final Index CACHE = new Index.Builder() + .caseSensitive(false) + .withAll(HttpScheme.values(), HttpScheme::asString) + .build(); private final String _string; private final ByteBuffer _buffer; diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpVersion.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpVersion.java index a259f201f22d..ccd0eec564b3 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpVersion.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpVersion.java @@ -20,9 +20,8 @@ import java.nio.ByteBuffer; -import org.eclipse.jetty.util.ArrayTrie; +import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.Trie; public enum HttpVersion { @@ -31,15 +30,10 @@ public enum HttpVersion HTTP_1_1("HTTP/1.1", 11), HTTP_2("HTTP/2.0", 20); - public static final Trie CACHE = new ArrayTrie(); - - static - { - for (HttpVersion version : HttpVersion.values()) - { - CACHE.put(version.toString(), version); - } - } + public static final Index CACHE = new Index.Builder() + .caseSensitive(false) + .withAll(HttpVersion.values(), HttpVersion::toString) + .build(); /** * Optimised lookup to find an Http Version and whitespace in a byte array. diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java b/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java index 2eb61902cd19..2e34c947edb4 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java @@ -32,10 +32,9 @@ import java.util.Properties; import java.util.Set; -import org.eclipse.jetty.util.ArrayTrie; import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.Trie; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,7 +45,6 @@ public class MimeTypes { private static final Logger LOG = LoggerFactory.getLogger(MimeTypes.class); - private static final Trie TYPES = new ArrayTrie(512); private static final Map __dftMimeMap = new HashMap(); private static final Map __inferredEncodings = new HashMap(); private static final Map __assumedEncodings = new HashMap(); @@ -168,23 +166,30 @@ public Type getBaseType() } } - public static final Trie CACHE = new ArrayTrie<>(512); + public static final Index CACHE = new Index.Builder() + .caseSensitive(false) + .withAll(() -> + { + Map result = new HashMap<>(); + for (Type type : Type.values()) + { + String key1 = type.toString(); + result.put(key1, type); + + if (key1.indexOf(";charset=") > 0) + { + String key2 = StringUtil.replace(key1, ";charset=", "; charset="); + result.put(key2, type); + } + } + return result; + }) + .build(); static { for (MimeTypes.Type type : MimeTypes.Type.values()) { - CACHE.put(type.toString(), type); - TYPES.put(type.toString(), type.asBuffer()); - - int charset = type.toString().indexOf(";charset="); - if (charset > 0) - { - String alt = StringUtil.replace(type.toString(), ";charset=", "; charset="); - CACHE.put(alt, type); - TYPES.put(alt, type.asBuffer()); - } - if (type.isCharsetAssumed()) __assumedEncodings.put(type.asString(), type.getCharsetString()); } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathMappings.java b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathMappings.java index f70b5529c708..300477b6bb58 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathMappings.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/PathMappings.java @@ -28,8 +28,7 @@ import java.util.TreeSet; import java.util.function.Predicate; -import org.eclipse.jetty.util.ArrayTernaryTrie; -import org.eclipse.jetty.util.Trie; +import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.Dumpable; @@ -49,9 +48,18 @@ public class PathMappings implements Iterable>, Dumpable private static final Logger LOG = LoggerFactory.getLogger(PathMappings.class); private final Set> _mappings = new TreeSet<>(Comparator.comparing(MappedResource::getPathSpec)); - private Trie> _exactMap = new ArrayTernaryTrie<>(false); - private Trie> _prefixMap = new ArrayTernaryTrie<>(false); - private Trie> _suffixMap = new ArrayTernaryTrie<>(false); + private final Index.Mutable> _exactMap = new Index.Builder>() + .caseSensitive(true) + .mutable() + .build(); + private final Index.Mutable> _prefixMap = new Index.Builder>() + .caseSensitive(true) + .mutable() + .build(); + private final Index.Mutable> _suffixMap = new Index.Builder>() + .caseSensitive(true) + .mutable() + .build(); @Override public String dump() @@ -136,10 +144,9 @@ public MappedResource getMatch(String path) case EXACT: { int i = path.length(); - final Trie> exact_map = _exactMap; while (i >= 0) { - MappedResource candidate = exact_map.getBest(path, 0, i); + MappedResource candidate = _exactMap.getBest(path, 0, i); if (candidate == null) break; if (candidate.getPathSpec().matches(path)) @@ -152,10 +159,9 @@ public MappedResource getMatch(String path) case PREFIX_GLOB: { int i = path.length(); - final Trie> prefix_map = _prefixMap; while (i >= 0) { - MappedResource candidate = prefix_map.getBest(path, 0, i); + MappedResource candidate = _prefixMap.getBest(path, 0, i); if (candidate == null) break; if (candidate.getPathSpec().matches(path)) @@ -168,10 +174,9 @@ public MappedResource getMatch(String path) case SUFFIX_GLOB: { int i = 0; - final Trie> suffix_map = _suffixMap; while ((i = path.indexOf('.', i + 1)) > 0) { - MappedResource candidate = suffix_map.get(path, i + 1, path.length() - i - 1); + MappedResource candidate = _suffixMap.get(path, i + 1, path.length() - i - 1); if (candidate != null && candidate.getPathSpec().matches(path)) return candidate; } @@ -230,24 +235,18 @@ public boolean put(PathSpec pathSpec, E resource) { case EXACT: String exact = pathSpec.getPrefix(); - while (exact != null && !_exactMap.put(exact, entry)) - { - _exactMap = new ArrayTernaryTrie<>((ArrayTernaryTrie>)_exactMap, 1.5); - } + if (exact != null) + _exactMap.put(exact, entry); break; case PREFIX_GLOB: String prefix = pathSpec.getPrefix(); - while (prefix != null && !_prefixMap.put(prefix, entry)) - { - _prefixMap = new ArrayTernaryTrie<>((ArrayTernaryTrie>)_prefixMap, 1.5); - } + if (prefix != null) + _prefixMap.put(prefix, entry); break; case SUFFIX_GLOB: String suffix = pathSpec.getSuffix(); - while (suffix != null && !_suffixMap.put(suffix, entry)) - { - _suffixMap = new ArrayTernaryTrie<>((ArrayTernaryTrie>)_prefixMap, 1.5); - } + if (suffix != null) + _suffixMap.put(suffix, entry); break; default: } diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Cipher.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Cipher.java index d287934fee22..571ea4b62c12 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Cipher.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Cipher.java @@ -20,319 +20,310 @@ import java.util.Comparator; -import org.eclipse.jetty.util.ArrayTrie; -import org.eclipse.jetty.util.Trie; +import org.eclipse.jetty.util.Index; public class HTTP2Cipher { public static final Comparator COMPARATOR = new CipherComparator(); - private static final Trie __blackProtocols = new ArrayTrie<>(6 * 5); - private static final Trie __blackCiphers = new ArrayTrie<>(275 * 40); + private static final Index __blackProtocols = new Index.Builder() + .caseSensitive(false) + .with("TLSv1.2", Boolean.TRUE) + .with("TLSv1.1", Boolean.TRUE) + .with("TLSv1", Boolean.TRUE) + .with("SSL", Boolean.TRUE) + .with("SSLv2", Boolean.TRUE) + .with("SSLv3", Boolean.TRUE) + .build(); - static - { - String[] protocols = {"TLSv1.2", "TLSv1.1", "TLSv1", "SSL", "SSLv2", "SSLv3"}; - for (String p : protocols) - { - __blackProtocols.put(p, Boolean.TRUE); - } - - String[] ciphers = - { - "TLS_NULL_WITH_NULL_NULL", - "TLS_RSA_WITH_NULL_MD5", - "TLS_RSA_WITH_NULL_SHA", - "TLS_RSA_EXPORT_WITH_RC4_40_MD5", - "TLS_RSA_WITH_RC4_128_MD5", - "TLS_RSA_WITH_RC4_128_SHA", - "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5", - "TLS_RSA_WITH_IDEA_CBC_SHA", - "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA", - "TLS_RSA_WITH_DES_CBC_SHA", - "TLS_RSA_WITH_3DES_EDE_CBC_SHA", - "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", - "TLS_DH_DSS_WITH_DES_CBC_SHA", - "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA", - "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", - "TLS_DH_RSA_WITH_DES_CBC_SHA", - "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA", - "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", - "TLS_DHE_DSS_WITH_DES_CBC_SHA", - "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", - "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", - "TLS_DHE_RSA_WITH_DES_CBC_SHA", - "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", - "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5", - "TLS_DH_anon_WITH_RC4_128_MD5", - "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA", - "TLS_DH_anon_WITH_DES_CBC_SHA", - "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA", - "TLS_KRB5_WITH_DES_CBC_SHA", - "TLS_KRB5_WITH_3DES_EDE_CBC_SHA", - "TLS_KRB5_WITH_RC4_128_SHA", - "TLS_KRB5_WITH_IDEA_CBC_SHA", - "TLS_KRB5_WITH_DES_CBC_MD5", - "TLS_KRB5_WITH_3DES_EDE_CBC_MD5", - "TLS_KRB5_WITH_RC4_128_MD5", - "TLS_KRB5_WITH_IDEA_CBC_MD5", - "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", - "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA", - "TLS_KRB5_EXPORT_WITH_RC4_40_SHA", - "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", - "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5", - "TLS_KRB5_EXPORT_WITH_RC4_40_MD5", - "TLS_PSK_WITH_NULL_SHA", - "TLS_DHE_PSK_WITH_NULL_SHA", - "TLS_RSA_PSK_WITH_NULL_SHA", - "TLS_RSA_WITH_AES_128_CBC_SHA", - "TLS_DH_DSS_WITH_AES_128_CBC_SHA", - "TLS_DH_RSA_WITH_AES_128_CBC_SHA", - "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", - "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", - "TLS_DH_anon_WITH_AES_128_CBC_SHA", - "TLS_RSA_WITH_AES_256_CBC_SHA", - "TLS_DH_DSS_WITH_AES_256_CBC_SHA", - "TLS_DH_RSA_WITH_AES_256_CBC_SHA", - "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", - "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", - "TLS_DH_anon_WITH_AES_256_CBC_SHA", - "TLS_RSA_WITH_NULL_SHA256", - "TLS_RSA_WITH_AES_128_CBC_SHA256", - "TLS_RSA_WITH_AES_256_CBC_SHA256", - "TLS_DH_DSS_WITH_AES_128_CBC_SHA256", - "TLS_DH_RSA_WITH_AES_128_CBC_SHA256", - "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", - "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA", - "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA", - "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA", - "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA", - "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA", - "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA", - "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", - "TLS_DH_DSS_WITH_AES_256_CBC_SHA256", - "TLS_DH_RSA_WITH_AES_256_CBC_SHA256", - "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", - "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", - "TLS_DH_anon_WITH_AES_128_CBC_SHA256", - "TLS_DH_anon_WITH_AES_256_CBC_SHA256", - "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA", - "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA", - "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA", - "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA", - "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA", - "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA", - "TLS_PSK_WITH_RC4_128_SHA", - "TLS_PSK_WITH_3DES_EDE_CBC_SHA", - "TLS_PSK_WITH_AES_128_CBC_SHA", - "TLS_PSK_WITH_AES_256_CBC_SHA", - "TLS_DHE_PSK_WITH_RC4_128_SHA", - "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA", - "TLS_DHE_PSK_WITH_AES_128_CBC_SHA", - "TLS_DHE_PSK_WITH_AES_256_CBC_SHA", - "TLS_RSA_PSK_WITH_RC4_128_SHA", - "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA", - "TLS_RSA_PSK_WITH_AES_128_CBC_SHA", - "TLS_RSA_PSK_WITH_AES_256_CBC_SHA", - "TLS_RSA_WITH_SEED_CBC_SHA", - "TLS_DH_DSS_WITH_SEED_CBC_SHA", - "TLS_DH_RSA_WITH_SEED_CBC_SHA", - "TLS_DHE_DSS_WITH_SEED_CBC_SHA", - "TLS_DHE_RSA_WITH_SEED_CBC_SHA", - "TLS_DH_anon_WITH_SEED_CBC_SHA", - "TLS_RSA_WITH_AES_128_GCM_SHA256", - "TLS_RSA_WITH_AES_256_GCM_SHA384", - "TLS_DH_RSA_WITH_AES_128_GCM_SHA256", - "TLS_DH_RSA_WITH_AES_256_GCM_SHA384", - "TLS_DH_DSS_WITH_AES_128_GCM_SHA256", - "TLS_DH_DSS_WITH_AES_256_GCM_SHA384", - "TLS_DH_anon_WITH_AES_128_GCM_SHA256", - "TLS_DH_anon_WITH_AES_256_GCM_SHA384", - "TLS_PSK_WITH_AES_128_GCM_SHA256", - "TLS_PSK_WITH_AES_256_GCM_SHA384", - "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256", - "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384", - "TLS_PSK_WITH_AES_128_CBC_SHA256", - "TLS_PSK_WITH_AES_256_CBC_SHA384", - "TLS_PSK_WITH_NULL_SHA256", - "TLS_PSK_WITH_NULL_SHA384", - "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256", - "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384", - "TLS_DHE_PSK_WITH_NULL_SHA256", - "TLS_DHE_PSK_WITH_NULL_SHA384", - "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256", - "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384", - "TLS_RSA_PSK_WITH_NULL_SHA256", - "TLS_RSA_PSK_WITH_NULL_SHA384", - "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256", - "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256", - "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256", - "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256", - "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", - "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256", - "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256", - "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256", - "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256", - "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256", - "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256", - "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256", - "TLS_EMPTY_RENEGOTIATION_INFO_SCSV", - "TLS_ECDH_ECDSA_WITH_NULL_SHA", - "TLS_ECDH_ECDSA_WITH_RC4_128_SHA", - "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", - "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", - "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", - "TLS_ECDHE_ECDSA_WITH_NULL_SHA", - "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", - "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", - "TLS_ECDH_RSA_WITH_NULL_SHA", - "TLS_ECDH_RSA_WITH_RC4_128_SHA", - "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", - "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", - "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", - "TLS_ECDHE_RSA_WITH_NULL_SHA", - "TLS_ECDHE_RSA_WITH_RC4_128_SHA", - "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", - "TLS_ECDH_anon_WITH_NULL_SHA", - "TLS_ECDH_anon_WITH_RC4_128_SHA", - "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", - "TLS_ECDH_anon_WITH_AES_128_CBC_SHA", - "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", - "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA", - "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA", - "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA", - "TLS_SRP_SHA_WITH_AES_128_CBC_SHA", - "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA", - "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA", - "TLS_SRP_SHA_WITH_AES_256_CBC_SHA", - "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA", - "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA", - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", - "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", - "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", - "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", - "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", - "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDHE_PSK_WITH_RC4_128_SHA", - "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA", - "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA", - "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA", - "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256", - "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384", - "TLS_ECDHE_PSK_WITH_NULL_SHA", - "TLS_ECDHE_PSK_WITH_NULL_SHA256", - "TLS_ECDHE_PSK_WITH_NULL_SHA384", - "TLS_RSA_WITH_ARIA_128_CBC_SHA256", - "TLS_RSA_WITH_ARIA_256_CBC_SHA384", - "TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256", - "TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384", - "TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256", - "TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384", - "TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256", - "TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384", - "TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256", - "TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384", - "TLS_DH_anon_WITH_ARIA_128_CBC_SHA256", - "TLS_DH_anon_WITH_ARIA_256_CBC_SHA384", - "TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256", - "TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384", - "TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256", - "TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384", - "TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256", - "TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384", - "TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256", - "TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384", - "TLS_RSA_WITH_ARIA_128_GCM_SHA256", - "TLS_RSA_WITH_ARIA_256_GCM_SHA384", - "TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256", - "TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384", - "TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256", - "TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384", - "TLS_DH_anon_WITH_ARIA_128_GCM_SHA256", - "TLS_DH_anon_WITH_ARIA_256_GCM_SHA384", - "TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256", - "TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384", - "TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256", - "TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384", - "TLS_PSK_WITH_ARIA_128_CBC_SHA256", - "TLS_PSK_WITH_ARIA_256_CBC_SHA384", - "TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256", - "TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384", - "TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256", - "TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384", - "TLS_PSK_WITH_ARIA_128_GCM_SHA256", - "TLS_PSK_WITH_ARIA_256_GCM_SHA384", - "TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256", - "TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384", - "TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256", - "TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384", - "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", - "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", - "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", - "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", - "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", - "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384", - "TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256", - "TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384", - "TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256", - "TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384", - "TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256", - "TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384", - "TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256", - "TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384", - "TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256", - "TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384", - "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", - "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", - "TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256", - "TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384", - "TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256", - "TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384", - "TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256", - "TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384", - "TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256", - "TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384", - "TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", - "TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", - "TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256", - "TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384", - "TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", - "TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", - "TLS_RSA_WITH_AES_128_CCM", - "TLS_RSA_WITH_AES_256_CCM", - "TLS_RSA_WITH_AES_128_CCM_8", - "TLS_RSA_WITH_AES_256_CCM_8", - "TLS_PSK_WITH_AES_128_CCM", - "TLS_PSK_WITH_AES_256_CCM", - "TLS_PSK_WITH_AES_128_CCM_8", - "TLS_PSK_WITH_AES_256_CCM_8" - }; - for (String c : ciphers) - { - __blackCiphers.put(c, Boolean.TRUE); - } - } + private static final Index __blackCiphers = new Index.Builder() + .caseSensitive(false) + .with("TLS_NULL_WITH_NULL_NULL", Boolean.TRUE) + .with("TLS_RSA_WITH_NULL_MD5", Boolean.TRUE) + .with("TLS_RSA_WITH_NULL_SHA", Boolean.TRUE) + .with("TLS_RSA_EXPORT_WITH_RC4_40_MD5", Boolean.TRUE) + .with("TLS_RSA_WITH_RC4_128_MD5", Boolean.TRUE) + .with("TLS_RSA_WITH_RC4_128_SHA", Boolean.TRUE) + .with("TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5", Boolean.TRUE) + .with("TLS_RSA_WITH_IDEA_CBC_SHA", Boolean.TRUE) + .with("TLS_RSA_EXPORT_WITH_DES40_CBC_SHA", Boolean.TRUE) + .with("TLS_RSA_WITH_DES_CBC_SHA", Boolean.TRUE) + .with("TLS_RSA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_DES_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_DES_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_DSS_WITH_DES_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_RSA_WITH_DES_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_anon_EXPORT_WITH_RC4_40_MD5", Boolean.TRUE) + .with("TLS_DH_anon_WITH_RC4_128_MD5", Boolean.TRUE) + .with("TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_anon_WITH_DES_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_anon_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_KRB5_WITH_DES_CBC_SHA", Boolean.TRUE) + .with("TLS_KRB5_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_KRB5_WITH_RC4_128_SHA", Boolean.TRUE) + .with("TLS_KRB5_WITH_IDEA_CBC_SHA", Boolean.TRUE) + .with("TLS_KRB5_WITH_DES_CBC_MD5", Boolean.TRUE) + .with("TLS_KRB5_WITH_3DES_EDE_CBC_MD5", Boolean.TRUE) + .with("TLS_KRB5_WITH_RC4_128_MD5", Boolean.TRUE) + .with("TLS_KRB5_WITH_IDEA_CBC_MD5", Boolean.TRUE) + .with("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", Boolean.TRUE) + .with("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA", Boolean.TRUE) + .with("TLS_KRB5_EXPORT_WITH_RC4_40_SHA", Boolean.TRUE) + .with("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", Boolean.TRUE) + .with("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5", Boolean.TRUE) + .with("TLS_KRB5_EXPORT_WITH_RC4_40_MD5", Boolean.TRUE) + .with("TLS_PSK_WITH_NULL_SHA", Boolean.TRUE) + .with("TLS_DHE_PSK_WITH_NULL_SHA", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_NULL_SHA", Boolean.TRUE) + .with("TLS_RSA_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_DSS_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_RSA_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_anon_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_RSA_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_DSS_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_RSA_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_anon_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_RSA_WITH_NULL_SHA256", Boolean.TRUE) + .with("TLS_RSA_WITH_AES_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_RSA_WITH_AES_256_CBC_SHA256", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_AES_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_AES_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_RSA_WITH_CAMELLIA_128_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_AES_256_CBC_SHA256", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_AES_256_CBC_SHA256", Boolean.TRUE) + .with("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", Boolean.TRUE) + .with("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", Boolean.TRUE) + .with("TLS_DH_anon_WITH_AES_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DH_anon_WITH_AES_256_CBC_SHA256", Boolean.TRUE) + .with("TLS_RSA_WITH_CAMELLIA_256_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA", Boolean.TRUE) + .with("TLS_PSK_WITH_RC4_128_SHA", Boolean.TRUE) + .with("TLS_PSK_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_PSK_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_PSK_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_PSK_WITH_RC4_128_SHA", Boolean.TRUE) + .with("TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_PSK_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_PSK_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_RC4_128_SHA", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_RSA_WITH_SEED_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_SEED_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_SEED_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_DSS_WITH_SEED_CBC_SHA", Boolean.TRUE) + .with("TLS_DHE_RSA_WITH_SEED_CBC_SHA", Boolean.TRUE) + .with("TLS_DH_anon_WITH_SEED_CBC_SHA", Boolean.TRUE) + .with("TLS_RSA_WITH_AES_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_RSA_WITH_AES_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_AES_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_AES_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_AES_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_AES_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_DH_anon_WITH_AES_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_DH_anon_WITH_AES_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_PSK_WITH_AES_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_PSK_WITH_AES_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_AES_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_AES_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_PSK_WITH_AES_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_PSK_WITH_AES_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_PSK_WITH_NULL_SHA256", Boolean.TRUE) + .with("TLS_PSK_WITH_NULL_SHA384", Boolean.TRUE) + .with("TLS_DHE_PSK_WITH_AES_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DHE_PSK_WITH_AES_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_DHE_PSK_WITH_NULL_SHA256", Boolean.TRUE) + .with("TLS_DHE_PSK_WITH_NULL_SHA384", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_AES_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_AES_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_NULL_SHA256", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_NULL_SHA384", Boolean.TRUE) + .with("TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256", Boolean.TRUE) + .with("TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256", Boolean.TRUE) + .with("TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256", Boolean.TRUE) + .with("TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256", Boolean.TRUE) + .with("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_NULL_SHA", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_RC4_128_SHA", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDHE_ECDSA_WITH_NULL_SHA", Boolean.TRUE) + .with("TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", Boolean.TRUE) + .with("TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_NULL_SHA", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_RC4_128_SHA", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDHE_RSA_WITH_NULL_SHA", Boolean.TRUE) + .with("TLS_ECDHE_RSA_WITH_RC4_128_SHA", Boolean.TRUE) + .with("TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDH_anon_WITH_NULL_SHA", Boolean.TRUE) + .with("TLS_ECDH_anon_WITH_RC4_128_SHA", Boolean.TRUE) + .with("TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDH_anon_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDH_anon_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_SRP_SHA_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_SRP_SHA_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_ECDHE_PSK_WITH_RC4_128_SHA", Boolean.TRUE) + .with("TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA", Boolean.TRUE) + .with("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_ECDHE_PSK_WITH_NULL_SHA", Boolean.TRUE) + .with("TLS_ECDHE_PSK_WITH_NULL_SHA256", Boolean.TRUE) + .with("TLS_ECDHE_PSK_WITH_NULL_SHA384", Boolean.TRUE) + .with("TLS_RSA_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_RSA_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_DH_anon_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DH_anon_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_RSA_WITH_ARIA_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_RSA_WITH_ARIA_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_DH_anon_WITH_ARIA_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_DH_anon_WITH_ARIA_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_PSK_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_PSK_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_PSK_WITH_ARIA_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_PSK_WITH_ARIA_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384", Boolean.TRUE) + .with("TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE) + .with("TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", Boolean.TRUE) + .with("TLS_RSA_WITH_AES_128_CCM", Boolean.TRUE) + .with("TLS_RSA_WITH_AES_256_CCM", Boolean.TRUE) + .with("TLS_RSA_WITH_AES_128_CCM_8", Boolean.TRUE) + .with("TLS_RSA_WITH_AES_256_CCM_8", Boolean.TRUE) + .with("TLS_PSK_WITH_AES_128_CCM", Boolean.TRUE) + .with("TLS_PSK_WITH_AES_256_CCM", Boolean.TRUE) + .with("TLS_PSK_WITH_AES_128_CCM_8", Boolean.TRUE) + .with("TLS_PSK_WITH_AES_256_CCM_8", Boolean.TRUE) + .build(); public static boolean isBlackListProtocol(String tlsProtocol) { - Boolean b = __blackProtocols.get(tlsProtocol); - return b != null && b; + return __blackProtocols.get(tlsProtocol) != null; } public static boolean isBlackListCipher(String tlsCipher) { - Boolean b = __blackCiphers.get(tlsCipher); - return b != null && b; + return __blackCiphers.get(tlsCipher) != null; } /** diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java index 70be6754f25b..338e2821bbec 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java @@ -29,9 +29,8 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpScheme; -import org.eclipse.jetty.util.ArrayTernaryTrie; +import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.Trie; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -114,13 +113,14 @@ public class HpackContext }; private static final Map __staticFieldMap = new HashMap<>(); - private static final Trie __staticNameMap = new ArrayTernaryTrie<>(true, 512); - private static final StaticEntry[] __staticTableByHeader = new StaticEntry[HttpHeader.UNKNOWN.ordinal()]; + private static final Index __staticNameMap; + private static final StaticEntry[] __staticTableByHeader = new StaticEntry[HttpHeader.values().length]; private static final StaticEntry[] __staticTable = new StaticEntry[STATIC_TABLE.length]; public static final int STATIC_SIZE = STATIC_TABLE.length - 1; static { + Index.Builder staticNameMapBuilder = new Index.Builder().caseSensitive(false); Set added = new HashSet<>(); for (int i = 1; i < STATIC_TABLE.length; i++) { @@ -173,11 +173,10 @@ public class HpackContext if (!added.contains(entry._field.getName())) { added.add(entry._field.getName()); - __staticNameMap.put(entry._field.getName(), entry); - if (__staticNameMap.get(entry._field.getName()) == null) - throw new IllegalStateException("name trie too small"); + staticNameMapBuilder.with(entry._field.getName(), entry); } } + __staticNameMap = staticNameMapBuilder.build(); for (HttpHeader h : HttpHeader.values()) { diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieRule.java index 3b0e0ce02a98..5fdee3a84130 100644 --- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieRule.java +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieRule.java @@ -28,8 +28,7 @@ import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.util.ArrayTernaryTrie; -import org.eclipse.jetty.util.Trie; +import org.eclipse.jetty.util.Index; /** * Special handling for MSIE (Microsoft Internet Explorer). @@ -42,21 +41,19 @@ public class MsieRule extends Rule { private static final int IEv5 = '5'; private static final int IEv6 = '6'; - private static final Trie __IE6_BadOS = new ArrayTernaryTrie<>(); + private static final Index __IE6_BadOS = new Index.Builder() + .caseSensitive(false) + .with("NT 5.01", Boolean.TRUE) + .with("NT 5.0", Boolean.TRUE) + .with("NT 4.0", Boolean.TRUE) + .with("98", Boolean.TRUE) + .with("98; Win 9x 4.90", Boolean.TRUE) + .with("95", Boolean.TRUE) + .with("CE", Boolean.TRUE) + .build(); private static final HttpField CONNECTION_CLOSE = new HttpField(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE); private static final HttpField VARY_USER_AGENT = new PreEncodedHttpField(HttpHeader.VARY, HttpHeader.USER_AGENT.asString()); - static - { - __IE6_BadOS.put("NT 5.01", Boolean.TRUE); - __IE6_BadOS.put("NT 5.0", Boolean.TRUE); - __IE6_BadOS.put("NT 4.0", Boolean.TRUE); - __IE6_BadOS.put("98", Boolean.TRUE); - __IE6_BadOS.put("98; Win 9x 4.90", Boolean.TRUE); - __IE6_BadOS.put("95", Boolean.TRUE); - __IE6_BadOS.put("CE", Boolean.TRUE); - } - public MsieRule() { _handling = false; diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieSslRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieSslRule.java index fda3c1d3238d..ca725183bb1d 100644 --- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieSslRule.java +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieSslRule.java @@ -24,8 +24,7 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; -import org.eclipse.jetty.util.ArrayTernaryTrie; -import org.eclipse.jetty.util.Trie; +import org.eclipse.jetty.util.Index; /** * MSIE (Microsoft Internet Explorer) SSL Rule. @@ -37,17 +36,16 @@ public class MsieSslRule extends Rule { private static final int IEv5 = '5'; private static final int IEv6 = '6'; - private static Trie __IE6_BadOS = new ArrayTernaryTrie<>(); - - { - __IE6_BadOS.put("NT 5.01", Boolean.TRUE); - __IE6_BadOS.put("NT 5.0", Boolean.TRUE); - __IE6_BadOS.put("NT 4.0", Boolean.TRUE); - __IE6_BadOS.put("98", Boolean.TRUE); - __IE6_BadOS.put("98; Win 9x 4.90", Boolean.TRUE); - __IE6_BadOS.put("95", Boolean.TRUE); - __IE6_BadOS.put("CE", Boolean.TRUE); - } + private static final Index __IE6_BadOS = new Index.Builder() + .caseSensitive(false) + .with("NT 5.01", Boolean.TRUE) + .with("NT 5.0", Boolean.TRUE) + .with("NT 4.0", Boolean.TRUE) + .with("98", Boolean.TRUE) + .with("98; Win 9x 4.90", Boolean.TRUE) + .with("95", Boolean.TRUE) + .with("CE", Boolean.TRUE) + .build(); public MsieSslRule() { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java index 91d3e2668934..b608d601ce40 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java @@ -33,10 +33,9 @@ import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.http.QuotedCSVParser; import org.eclipse.jetty.server.HttpConfiguration.Customizer; -import org.eclipse.jetty.util.ArrayTrie; import org.eclipse.jetty.util.HostPort; +import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.Trie; import static java.lang.invoke.MethodType.methodType; @@ -159,7 +158,10 @@ public class ForwardedRequestCustomizer implements Customizer private String _forwardedCipherSuiteHeader = "Proxy-auth-cert"; private String _forwardedSslSessionIdHeader = "Proxy-ssl-id"; private boolean _sslIsSecure = true; - private Trie _handles; + private final Index.Mutable _handles = new Index.Builder() + .caseSensitive(false) + .mutable() + .build(); public ForwardedRequestCustomizer() { @@ -596,52 +598,33 @@ public void setHostHeader(String hostHeader) private void updateHandles() { - int size = 0; MethodHandles.Lookup lookup = MethodHandles.lookup(); - - // Loop to grow capacity of ArrayTrie for all headers - while (true) + try { - try - { - size += 128; // experimented good baseline size - _handles = new ArrayTrie<>(size); - - if (updateForwardedHandle(lookup, getForwardedHeader(), "handleRFC7239")) - continue; - if (updateForwardedHandle(lookup, getForwardedHostHeader(), "handleForwardedHost")) - continue; - if (updateForwardedHandle(lookup, getForwardedForHeader(), "handleForwardedFor")) - continue; - if (updateForwardedHandle(lookup, getForwardedPortHeader(), "handleForwardedPort")) - continue; - if (updateForwardedHandle(lookup, getForwardedProtoHeader(), "handleProto")) - continue; - if (updateForwardedHandle(lookup, getForwardedHttpsHeader(), "handleHttps")) - continue; - if (updateForwardedHandle(lookup, getForwardedServerHeader(), "handleForwardedServer")) - continue; - if (updateForwardedHandle(lookup, getForwardedCipherSuiteHeader(), "handleCipherSuite")) - continue; - if (updateForwardedHandle(lookup, getForwardedSslSessionIdHeader(), "handleSslSessionId")) - continue; - break; - } - catch (NoSuchMethodException | IllegalAccessException e) - { - throw new IllegalStateException(e); - } + updateForwardedHandle(lookup, getForwardedHeader(), "handleRFC7239"); + updateForwardedHandle(lookup, getForwardedHostHeader(), "handleForwardedHost"); + updateForwardedHandle(lookup, getForwardedForHeader(), "handleForwardedFor"); + updateForwardedHandle(lookup, getForwardedPortHeader(), "handleForwardedPort"); + updateForwardedHandle(lookup, getForwardedProtoHeader(), "handleProto"); + updateForwardedHandle(lookup, getForwardedHttpsHeader(), "handleHttps"); + updateForwardedHandle(lookup, getForwardedServerHeader(), "handleForwardedServer"); + updateForwardedHandle(lookup, getForwardedCipherSuiteHeader(), "handleCipherSuite"); + updateForwardedHandle(lookup, getForwardedSslSessionIdHeader(), "handleSslSessionId"); + } + catch (NoSuchMethodException | IllegalAccessException e) + { + throw new IllegalStateException(e); } } - private boolean updateForwardedHandle(MethodHandles.Lookup lookup, String headerName, String forwardedMethodName) throws NoSuchMethodException, IllegalAccessException + private void updateForwardedHandle(MethodHandles.Lookup lookup, String headerName, String forwardedMethodName) throws NoSuchMethodException, IllegalAccessException { final MethodType type = methodType(void.class, HttpField.class); if (StringUtil.isBlank(headerName)) - return false; + return; - return !_handles.put(headerName, lookup.findVirtual(Forwarded.class, forwardedMethodName, type)); + _handles.put(headerName, lookup.findVirtual(Forwarded.class, forwardedMethodName, type)); } private static class MutableHostPort diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java index 694aca4abb39..88b7c917ccac 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java @@ -508,38 +508,27 @@ public void parsedHeader(HttpField field) if (HttpVersion.HTTP_1_1.equals(_requestBuilder.version())) { HttpHeaderValue expect = HttpHeaderValue.CACHE.get(value); - switch (expect == null ? HttpHeaderValue.UNKNOWN : expect) + if (expect == HttpHeaderValue.CONTINUE) { - case CONTINUE: - _expect100Continue = true; - break; - - case PROCESSING: - _expect102Processing = true; - break; - - default: - String[] values = field.getValues(); - for (int i = 0; values != null && i < values.length; i++) - { - expect = HttpHeaderValue.CACHE.get(values[i].trim()); - if (expect == null) - _unknownExpectation = true; - else - { - switch (expect) - { - case CONTINUE: - _expect100Continue = true; - break; - case PROCESSING: - _expect102Processing = true; - break; - default: - _unknownExpectation = true; - } - } - } + _expect100Continue = true; + } + else if (expect == HttpHeaderValue.PROCESSING) + { + _expect102Processing = true; + } + else + { + String[] values = field.getValues(); + for (int i = 0; values != null && i < values.length; i++) + { + expect = HttpHeaderValue.CACHE.get(values[i].trim()); + if (expect == HttpHeaderValue.CONTINUE) + _expect100Continue = true; + else if (expect == HttpHeaderValue.PROCESSING) + _expect102Processing = true; + else + _unknownExpectation = true; + } } } break; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java index 5916ce182a19..991d7c57de36 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java @@ -27,9 +27,8 @@ import org.eclipse.jetty.http.HttpCompliance; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpScheme; +import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.Jetty; -import org.eclipse.jetty.util.TreeTrie; -import org.eclipse.jetty.util.Trie; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.Dumpable; @@ -51,7 +50,10 @@ public class HttpConfiguration implements Dumpable { public static final String SERVER_VERSION = "Jetty(" + Jetty.VERSION + ")"; private final List _customizers = new CopyOnWriteArrayList<>(); - private final Trie _formEncodedMethods = new TreeTrie<>(); + private final Index.Mutable _formEncodedMethods = new Index.Builder() + .caseSensitive(false) + .mutable() + .build(); private int _outputBufferSize = 32 * 1024; private int _outputAggregationSize = _outputBufferSize / 4; private int _requestHeaderSize = 8 * 1024; @@ -424,7 +426,7 @@ public void setResponseHeaderSize(int responseHeaderSize) /** * @param headerCacheSize The size of the header field cache, in terms of unique characters branches - * in the lookup {@link Trie} and associated data structures. + * in the lookup {@link Index.Mutable} and associated data structures. */ public void setHeaderCacheSize(int headerCacheSize) { @@ -491,7 +493,7 @@ public Set getFormEncodedMethods() */ public void addFormEncodedMethod(String method) { - _formEncodedMethods.put(method, Boolean.TRUE); + _formEncodedMethods.put(method,Boolean.TRUE); } /** @@ -504,7 +506,7 @@ public void addFormEncodedMethod(String method) */ public boolean isFormEncodedMethod(String method) { - return Boolean.TRUE.equals(_formEncodedMethods.get(method)); + return _formEncodedMethods.get(method) != null; } /** diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java index 7045b1aadbb9..123d7be34242 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandlerCollection.java @@ -20,8 +20,10 @@ import java.io.IOException; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import javax.servlet.ServletException; @@ -32,10 +34,9 @@ import org.eclipse.jetty.server.HandlerContainer; import org.eclipse.jetty.server.HttpChannelState; import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.util.ArrayTernaryTrie; import org.eclipse.jetty.util.ArrayUtil; import org.eclipse.jetty.util.Callback; -import org.eclipse.jetty.util.Trie; +import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; import org.eclipse.jetty.util.thread.SerializedExecutor; @@ -129,44 +130,9 @@ protected Handlers newHandlers(Handler[] handlers) entry.setValue(sorted); } - // Loop until we have a big enough trie to hold all the context paths - int capacity = 512; - Mapping mapping; - loop: - while (true) - { - mapping = new Mapping(handlers, capacity); - for (Map.Entry entry : path2Branches.entrySet()) - { - if (!mapping._pathBranches.put(entry.getKey().substring(1), entry)) - { - capacity += 512; - continue loop; - } - } - break; - } - + Mapping mapping = new Mapping(handlers, path2Branches); if (LOG.isDebugEnabled()) - { - for (String ctx : mapping._pathBranches.keySet()) - { - LOG.debug("{}->{}", ctx, Arrays.asList(mapping._pathBranches.get(ctx).getValue())); - } - } - - // add new context branches to concurrent map - for (Branch[] branches : path2Branches.values()) - { - for (Branch branch : branches) - { - for (ContextHandler context : branch.getContextHandlers()) - { - mapping._contextBranches.put(context, branch.getHandler()); - } - } - } - + LOG.debug("{}", mapping._pathBranches); return mapping; } @@ -209,7 +175,7 @@ public void handle(String target, Request baseRequest, HttpServletRequest reques // handle many contexts if (target.startsWith("/")) { - Trie> pathBranches = mapping._pathBranches; + Index> pathBranches = mapping._pathBranches; if (pathBranches == null) return; @@ -377,13 +343,38 @@ public String toString() private static class Mapping extends Handlers { - private final Map _contextBranches = new HashMap<>(); - private final Trie> _pathBranches; + private final Map _contextBranches; + private final Index> _pathBranches; - private Mapping(Handler[] handlers, int capacity) + private Mapping(Handler[] handlers, Map path2Branches) { super(handlers); - _pathBranches = new ArrayTernaryTrie<>(false, capacity); + _pathBranches = new Index.Builder>() + .caseSensitive(true) + .withAll(() -> + { + Map> result = new LinkedHashMap<>(); + for (Map.Entry entry : path2Branches.entrySet()) + { + result.put(entry.getKey().substring(1), entry); + } + return result; + }) + .build(); + + // add new context branches to map + Map contextBranches = new HashMap<>(); + for (Branch[] branches : path2Branches.values()) + { + for (Branch branch : branches) + { + for (ContextHandler context : branch.getContextHandlers()) + { + contextBranches.put(context, branch.getHandler()); + } + } + } + _contextBranches = Collections.unmodifiableMap(contextBranches); } } } diff --git a/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/AsyncJSON.java b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/AsyncJSON.java index a16d28bb0414..180b4cc8c6ab 100644 --- a/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/AsyncJSON.java +++ b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/AsyncJSON.java @@ -26,10 +26,9 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.eclipse.jetty.util.ArrayTernaryTrie; import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.Loader; -import org.eclipse.jetty.util.Trie; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.util.ajax.JSON.Convertible; @@ -79,7 +78,7 @@ public class AsyncJSON */ public static class Factory { - private Trie cache; + private Index.Mutable cache; private Map convertors; private boolean detailedParseException; @@ -106,7 +105,10 @@ public void setDetailedParseException(boolean detailedParseException) public boolean cache(String value) { if (cache == null) - cache = new ArrayTernaryTrie.Growing<>(false, 64, 64); + cache = new Index.Builder() + .caseSensitive(true) + .mutable() + .build(); CachedString cached = new CachedString(value); if (cached.isCacheable()) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/AbstractTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/AbstractTrie.java index 15e45a614f22..07110018ef83 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/AbstractTrie.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/AbstractTrie.java @@ -20,6 +20,11 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; /** * Abstract Trie implementation. @@ -29,7 +34,7 @@ * * @param the type of object that the Trie holds */ -public abstract class AbstractTrie implements Trie +abstract class AbstractTrie implements Index.Mutable { final boolean _caseInsensitive; @@ -38,13 +43,16 @@ protected AbstractTrie(boolean insensitive) _caseInsensitive = insensitive; } - @Override + public boolean isCaseInsensitive() + { + return _caseInsensitive; + } + public boolean put(V v) { return put(v.toString(), v); } - @Override public V remove(String s) { V o = get(s); @@ -52,33 +60,109 @@ public V remove(String s) return o; } - @Override public V get(String s) { return get(s, 0, s.length()); } - @Override public V get(ByteBuffer b) { return get(b, 0, b.remaining()); } - @Override public V getBest(String s) { return getBest(s, 0, s.length()); } - @Override public V getBest(byte[] b, int offset, int len) { return getBest(new String(b, offset, len, StandardCharsets.ISO_8859_1)); } - @Override - public boolean isCaseInsensitive() + /** + * Calculate required Trie capacity in nodes of a tree decomposition of the keys. + * For example given the keys: + *

    + *
  • utf_16
  • + *
  • utf_8
  • + *
  • utf16
  • + *
  • utf8
  • + *
+ * The tree has 10 nodes as follows: + *
+     *                     1 - 6
+     *                   /
+     *                 _ - 8
+     *               /
+     *     u - t - f - 1 - 6
+     *               \
+     *                 8
+     * 
+ * @param keys The keys to be put in a Trie + * @param caseSensitive true if the capacity should be calculated with case-sensitive keys + * @return The capacity in nodes of a tree decomposition + */ + protected static int requiredCapacity(Set keys, boolean caseSensitive) { - return _caseInsensitive; + List list = caseSensitive + ? new ArrayList<>(keys) + : keys.stream().map(String::toLowerCase).collect(Collectors.toList()); + Collections.sort(list); + return AbstractTrie.requiredCapacity(list, 0, list.size(), 0); + } + + /** + * Calculate required Trie capacity in nodes of a sub-tree decomposition of the keys. + * @param keys The keys to calculate the capacity for + * @param offset The offset of the first key to be considered + * @param length The number of keys to be considered + * @param index The character to be considered + * @return The capacity in tree nodes of the substree + */ + private static int requiredCapacity(List keys, int offset, int length, int index) + { + int required = 0; + + // Examine all the keys in the subtree + Character nodeChar = null; + for (int i = 0; i < length; i++) + { + String k = keys.get(offset + i); + + // If the key is shorter than our current index then ignore it + if (k.length() <= index) + continue; + + // Get the character at the index of the current key + char c = k.charAt(index); + + // If the character is the same as the current node, then we are + // still in the current node and need to continue searching for the + // next node or the end of the keys + if (nodeChar != null && c == nodeChar) + continue; + + // The character is a new node, so increase required by 1 + required++; + + // if we had a previous node, then add the required nodes for the subtree under it. + if (nodeChar != null) + required += AbstractTrie.requiredCapacity(keys, offset, i, index + 1); + + // set the char for the new node + nodeChar = c; + + // reset the offset, length and index to continue iteration from the start of the new node + offset += i; + length -= i; + i = 0; + } + + // If we finish the iteration with a nodeChar, then we must add the required nodes for the subtree under it. + if (nodeChar != null) + required += AbstractTrie.requiredCapacity(keys, offset, length, index + 1); + + return required; } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java index 4f2d0ae0d345..41838c013aaf 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -57,11 +58,11 @@ * * @param the Entry type */ -public class ArrayTernaryTrie extends AbstractTrie +class ArrayTernaryTrie extends AbstractTrie { - private static int LO = 1; - private static int EQ = 2; - private static int HI = 3; + private static final int LO = 1; + private static final int EQ = 2; + private static final int HI = 3; /** * The Size of a Trie row is the char, and the low, equal and high @@ -69,6 +70,13 @@ public class ArrayTernaryTrie extends AbstractTrie */ private static final int ROW_SIZE = 4; + /** + * The maximum capacity of the implementation. Over that, + * the 16 bit indexes can overflow and the trie + * cannot find existing entries anymore. + */ + private static final int MAX_CAPACITY = 21_000; + /** * The Trie rows in a single array which allows a lookup of row,character * to the next row in the Trie. This is actually a 2 dimensional @@ -93,39 +101,6 @@ public class ArrayTernaryTrie extends AbstractTrie */ private char _rows; - /** - * Create a case insensitive Trie of default capacity. - */ - public ArrayTernaryTrie() - { - this(128); - } - - /** - * Create a Trie of default capacity - * - * @param insensitive true if the Trie is insensitive to the case of the key. - */ - public ArrayTernaryTrie(boolean insensitive) - { - this(insensitive, 128); - } - - /** - * Create a case insensitive Trie - * - * @param capacity The capacity of the Trie, which is in the worst case - * is the total number of characters of all keys stored in the Trie. - * The capacity needed is dependent of the shared prefixes of the keys. - * For example, a capacity of 6 nodes is required to store keys "foo" - * and "bar", but a capacity of only 4 is required to - * store "bar" and "bat". - */ - public ArrayTernaryTrie(int capacity) - { - this(true, capacity); - } - /** * Create a Trie * @@ -137,28 +112,37 @@ public ArrayTernaryTrie(int capacity) * and "bar", but a capacity of only 4 is required to * store "bar" and "bat". */ - public ArrayTernaryTrie(boolean insensitive, int capacity) + @SuppressWarnings("unchecked") + ArrayTernaryTrie(boolean insensitive, int capacity) { super(insensitive); + if (capacity > MAX_CAPACITY) + throw new IllegalArgumentException("ArrayTernaryTrie maximum capacity overflow (" + capacity + " > " + MAX_CAPACITY + ")"); _value = (V[])new Object[capacity]; _tree = new char[capacity * ROW_SIZE]; _key = new String[capacity]; } - /** - * Copy Trie and change capacity by a factor - * - * @param trie the trie to copy from - * @param factor the factor to grow the capacity by - */ - public ArrayTernaryTrie(ArrayTernaryTrie trie, double factor) + @SuppressWarnings("unchecked") + ArrayTernaryTrie(boolean insensitive, Map initialValues) { - super(trie.isCaseInsensitive()); - int capacity = (int)(trie._value.length * factor); - _rows = trie._rows; - _value = Arrays.copyOf(trie._value, capacity); - _tree = Arrays.copyOf(trie._tree, capacity * ROW_SIZE); - _key = Arrays.copyOf(trie._key, capacity); + super(insensitive); + // The calculated requiredCapacity does not take into account the + // extra reserved slot for the empty string key, nor the slots + // required for 'terminating' the entry (1 slot per key) so we + // have to add those. + Set keys = initialValues.keySet(); + int capacity = AbstractTrie.requiredCapacity(keys, !insensitive) + keys.size() + 1; + if (capacity > MAX_CAPACITY) + throw new IllegalArgumentException("ArrayTernaryTrie maximum capacity overflow (" + capacity + " > " + MAX_CAPACITY + ")"); + _value = (V[])new Object[capacity]; + _tree = new char[capacity * ROW_SIZE]; + _key = new String[capacity]; + for (Map.Entry entry : initialValues.entrySet()) + { + if (!put(entry.getKey(), entry.getValue())) + throw new AssertionError("Invalid capacity calculated (" + capacity + ") at '" + entry + "' for " + initialValues); + } } @Override @@ -527,12 +511,6 @@ public Set> entrySet() return entries; } - @Override - public boolean isFull() - { - return _rows + 1 == _key.length; - } - public static int hilo(int diff) { // branchless equivalent to return ((diff<0)?LO:HI); @@ -556,24 +534,14 @@ public void dump() } } - public static class Growing implements Trie + static class Growing extends AbstractTrie { private final int _growby; private ArrayTernaryTrie _trie; - public Growing() - { - this(1024, 1024); - } - - public Growing(int capacity, int growby) - { - _growby = growby; - _trie = new ArrayTernaryTrie<>(capacity); - } - - public Growing(boolean insensitive, int capacity, int growby) + Growing(boolean insensitive, int capacity, int growby) { + super(insensitive); _growby = growby; _trie = new ArrayTernaryTrie<>(insensitive, capacity); } @@ -591,15 +559,14 @@ public V remove(String s) } @Override - public boolean isCaseInsensitive() - { - return _trie.isCaseInsensitive(); - } - - @Override - public boolean equals(Object obj) + public boolean equals(Object o) { - return _trie.equals(obj); + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Growing growing = (Growing)o; + return Objects.equals(_trie, growing._trie); } @Override @@ -692,12 +659,6 @@ public Set keySet() return _trie.keySet(); } - @Override - public boolean isFull() - { - return false; - } - public void dump() { _trie.dump(); diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTrie.java index 177dddde898c..b21502921952 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTrie.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTrie.java @@ -22,6 +22,7 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.HashSet; +import java.util.Map; import java.util.Set; /** @@ -47,7 +48,7 @@ * * @param the entry type */ -public class ArrayTrie extends AbstractTrie +class ArrayTrie extends AbstractTrie { /** * The Size of a Trie row is how many characters can be looked @@ -112,11 +113,6 @@ public class ArrayTrie extends AbstractTrie */ private char _rows; - public ArrayTrie() - { - this(128); - } - /** * @param capacity The capacity of the trie, which at the worst case * is the total number of characters of all keys stored in the Trie. @@ -126,12 +122,30 @@ public ArrayTrie() * store "bar" and "bat". */ @SuppressWarnings("unchecked") - public ArrayTrie(int capacity) + ArrayTrie(int capacity) + { + super(true); + capacity++; + _value = (V[])new Object[capacity]; + _rowIndex = new char[capacity * ROW_SIZE]; + _key = new String[capacity]; + } + + @SuppressWarnings("unchecked") + ArrayTrie(Map initialValues) { super(true); + // The calculated requiredCapacity does not take into account the + // extra reserved slot for the empty string key, so we have to add 1. + int capacity = requiredCapacity(initialValues.keySet(), false) + 1; _value = (V[])new Object[capacity]; - _rowIndex = new char[capacity * 32]; + _rowIndex = new char[capacity * ROW_SIZE]; _key = new String[capacity]; + for (Map.Entry entry : initialValues.entrySet()) + { + if (!put(entry.getKey(), entry.getValue())) + throw new AssertionError("Invalid capacity calculated (" + capacity + ") at '" + entry + "' for " + initialValues); + } } @Override @@ -446,6 +460,18 @@ public Set keySet() return keys; } + @Override + public int size() + { + return keySet().size(); + } + + @Override + public boolean isEmpty() + { + return keySet().isEmpty(); + } + private void keySet(Set set, int t) { if (t < _value.length && _value[t] != null) @@ -468,10 +494,4 @@ private void keySet(Set set, int t) } } } - - @Override - public boolean isFull() - { - return _rows + 1 >= _key.length; - } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/EmptyTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/EmptyTrie.java new file mode 100644 index 000000000000..f90480409de4 --- /dev/null +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/EmptyTrie.java @@ -0,0 +1,99 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under +// the terms of the Eclipse Public License 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0 +// +// This Source Code may also be made available under the following +// Secondary Licenses when the conditions for such availability set +// forth in the Eclipse Public License, v. 2.0 are satisfied: +// the Apache License v2.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.util; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.Set; + +/** + * An empty trie implementation that never contains anything and never accepts new entries. + * @param the entry type + */ +class EmptyTrie extends AbstractTrie +{ + @SuppressWarnings("rawtypes") + private static final EmptyTrie SENSITIVE = new EmptyTrie<>(false); + @SuppressWarnings("rawtypes") + private static final EmptyTrie INSENSITIVE = new EmptyTrie<>(true); + + @SuppressWarnings("unchecked") + public static EmptyTrie instance(boolean caseSensitive) + { + return caseSensitive ? SENSITIVE : INSENSITIVE; + } + + private EmptyTrie(boolean insensitive) + { + super(insensitive); + } + + @Override + public boolean put(String s, V v) + { + return false; + } + + @Override + public V get(String s, int offset, int len) + { + return null; + } + + @Override + public V get(ByteBuffer b, int offset, int len) + { + return null; + } + + @Override + public V getBest(String s, int offset, int len) + { + return null; + } + + @Override + public V getBest(ByteBuffer b, int offset, int len) + { + return null; + } + + @Override + public boolean isEmpty() + { + return true; + } + + @Override + public Set keySet() + { + return Collections.emptySet(); + } + + @Override + public int size() + { + return 0; + } + + @Override + public void clear() + { + } +} diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Index.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Index.java new file mode 100644 index 000000000000..b37345ace4ca --- /dev/null +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Index.java @@ -0,0 +1,364 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under +// the terms of the Eclipse Public License 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0 +// +// This Source Code may also be made available under the following +// Secondary Licenses when the conditions for such availability set +// forth in the Eclipse Public License, v. 2.0 are satisfied: +// the Apache License v2.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.util; + +import java.nio.ByteBuffer; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * An immutable String lookup data structure. + * @param the entry type + */ +public interface Index +{ + /** + * Get an exact match from a String key + * + * @param s The key + * @return the value for the string key + */ + V get(String s); + + /** + * Get an exact match from a segment of a ByteBuufer as key + * + * @param b The buffer + * @return The value or null if not found + */ + V get(ByteBuffer b); + + /** + * Get an exact match from a String key + * + * @param s The key + * @param offset The offset within the string of the key + * @param len the length of the key + * @return the value for the string / offset / length + */ + V get(String s, int offset, int len); + + /** + * Get an exact match from a segment of a ByteBuufer as key + * + * @param b The buffer + * @param offset The offset within the buffer of the key + * @param len the length of the key + * @return The value or null if not found + */ + V get(ByteBuffer b, int offset, int len); + + /** + * Get the best match from key in a String. + * + * @param s The string + * @param offset The offset within the string of the key + * @param len the length of the key + * @return The value or null if not found + */ + V getBest(String s, int offset, int len); + + /** + * Get the best match from key in a byte buffer. + * The key is assumed to by ISO_8859_1 characters. + * + * @param b The buffer + * @param offset The offset within the buffer of the key + * @param len the length of the key + * @return The value or null if not found + */ + V getBest(ByteBuffer b, int offset, int len); + + /** + * Get the best match from key in a String. + * + * @param s The string + * @return The value or null if not found + */ + V getBest(String s); + + /** + * Get the best match from key in a byte array. + * The key is assumed to by ISO_8859_1 characters. + * + * @param b The buffer + * @param offset The offset within the array of the key + * @param len the length of the key + * @return The value or null if not found + */ + V getBest(byte[] b, int offset, int len); + + /** + * Check if the index contains any entry. + * + * @return true if the index does not contain any entry. + */ + boolean isEmpty(); + + /** + * Get the number of entries in the index. + * + * @return the index' entries count. + */ + int size(); + + /** + * Get a {@link Set} of the keys contained in this index. + * + * @return a {@link Set} of the keys contained in this index. + */ + Set keySet(); + + /** + * A mutable String lookup data structure. + * Implementations are not thread-safe. + * @param the entry type + */ + interface Mutable extends Index + { + /** + * Put an entry into the index. + * + * @param s The key for the entry + * @param v The value of the entry + * @return True if the index had capacity to add the field. + */ + boolean put(String s, V v); + + /** + * Put a value as both a key and a value. + * + * @param v The value and key + * @return True if the Trie had capacity to add the field. + */ + boolean put(V v); + + /** + * Remove an entry from the index. + * + * @param s The key for the entry + * @return The removed value of the entry + */ + V remove(String s); + + /** + * Remove all entries from the index. + */ + void clear(); + + /** + * Builder of {@link Index.Mutable} instances. Such builder cannot be + * directly created, it is instead returned by calling {@link Index.Builder#mutable()}. + * @param the entry type + */ + class Builder extends Index.Builder + { + private int maxCapacity = -1; + + Builder(boolean caseSensitive, Map contents) + { + super(caseSensitive, contents); + } + + /** + * Configure a maximum capacity for the mutable index. + * A negative value means there is no capacity limit and + * the index can grow without limits. + * The default value is -1. + * @param capacity the maximum capacity of the index. + * @return this + */ + public Builder maxCapacity(int capacity) + { + this.maxCapacity = capacity; + return this; + } + + /** + * Build a {@link Mutable} instance. + * @return a {@link Mutable} instance. + */ + public Mutable build() + { + if (contents != null && maxCapacity == 0) + throw new IllegalStateException("Cannot create a mutable index with maxCapacity=0 and some contents"); + + // TODO we need to consider large size and alphabet when picking a trie impl + Mutable result; + if (maxCapacity > 0) + { + result = new ArrayTernaryTrie<>(!caseSensitive, maxCapacity); + } + else if (maxCapacity < 0) + { + if (caseSensitive) + result = new ArrayTernaryTrie.Growing<>(false, 512, 512); + else + result = new TreeTrie<>(); + } + else + { + result = EmptyTrie.instance(caseSensitive); + } + + if (contents != null) + { + for (Map.Entry entry : contents.entrySet()) + { + if (!result.put(entry.getKey(), entry.getValue())) + throw new AssertionError("Index capacity exceeded at " + entry.getKey()); + } + } + return result; + } + } + } + + /** + * Builder of {@link Index} instances. + * @param the entry type + */ + class Builder + { + Map contents; + boolean caseSensitive; + + /** + * Create a new index builder instance. + */ + public Builder() + { + this(false, null); + } + + Builder(boolean caseSensitive, Map contents) + { + this.caseSensitive = caseSensitive; + this.contents = contents; + } + + private Map contents() + { + if (contents == null) + contents = new LinkedHashMap<>(); + return contents; + } + + /** + * Configure the index to be either case-sensitive or not. + * Default value is false. + * + * @param caseSensitive true if the index has to be case-sensitive + * @return this + */ + public Builder caseSensitive(boolean caseSensitive) + { + this.caseSensitive = caseSensitive; + return this; + } + + /** + * Configure some pre-existing entries. + * + * @param values an array of values + * @param keyFunction a {@link Function} that generates the key of each + * entry of the values array + * @return this + */ + public Builder withAll(V[] values, Function keyFunction) + { + for (V value : values) + { + String key = keyFunction.apply(value); + contents().put(key, value); + } + return this; + } + + /** + * Configure some pre-existing entries. + * + * @param entriesSupplier a {@link Map} {@link Supplier} of entries + * @return this + */ + public Builder withAll(Supplier> entriesSupplier) + { + Map map = entriesSupplier.get(); + contents().putAll(map); + return this; + } + + /** + * Configure a pre-existing entry with a key + * that is the {@link #toString()} representation + * of the value. + * + * @param value The value + * @return this + */ + public Builder with(V value) + { + contents().put(value.toString(), value); + return this; + } + + /** + * Configure a pre-existing entry. + * + * @param key The key + * @param value The value for the key string + * @return this + */ + public Builder with(String key, V value) + { + contents().put(key, value); + return this; + } + + /** + * Configure the index to be mutable. + * + * @return a {@link Mutable.Builder} configured like this builder. + */ + public Mutable.Builder mutable() + { + return new Mutable.Builder<>(caseSensitive, contents); + } + + /** + * Build a {@link Index} instance. + * + * @return a {@link Index} instance. + */ + public Index build() + { + if (contents == null) + return EmptyTrie.instance(caseSensitive); + + // TODO we need to consider large size and alphabet when picking a trie impl + if (caseSensitive) + return new ArrayTernaryTrie<>(false, contents); + else + return new ArrayTrie<>(contents); + } + } +} diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java index 2b6c1fe9fd42..043a0eb4dfa2 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java @@ -33,8 +33,6 @@ */ public class StringUtil { - private static final Trie CHARSETS = new ArrayTrie<>(256); - public static final String ALL_INTERFACES = "0.0.0.0"; public static final String CRLF = "\r\n"; public static final String DEFAULT_DELIMS = ",;"; @@ -43,15 +41,15 @@ public class StringUtil public static final String __UTF8 = "utf-8"; public static final String __UTF16 = "utf-16"; - static - { - CHARSETS.put("utf-8", __UTF8); - CHARSETS.put("utf8", __UTF8); - CHARSETS.put("utf-16", __UTF16); - CHARSETS.put("utf16", __UTF16); - CHARSETS.put("iso-8859-1", __ISO_8859_1); - CHARSETS.put("iso_8859_1", __ISO_8859_1); - } + private static final Index CHARSETS = new Index.Builder() + .caseSensitive(false) + .with("utf-8", __UTF8) + .with("utf8", __UTF8) + .with("utf-16", __UTF16) + .with("utf16", __UTF16) + .with("iso-8859-1", __ISO_8859_1) + .with("iso_8859_1", __ISO_8859_1) + .build(); /** * Convert alternate charset names (eg utf8) to normalized diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/TreeTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/TreeTrie.java index 7cd1cf6b360d..9757477bbe1e 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/TreeTrie.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/TreeTrie.java @@ -42,7 +42,7 @@ * * @param the entry type */ -public class TreeTrie extends AbstractTrie +class TreeTrie extends AbstractTrie { private static final int[] LOOKUP = { @@ -63,13 +63,15 @@ public class TreeTrie extends AbstractTrie private String _key; private V _value; - public TreeTrie() + @SuppressWarnings("unchecked") + TreeTrie() { super(true); _nextIndex = new TreeTrie[INDEX]; _c = 0; } + @SuppressWarnings("unchecked") private TreeTrie(char c) { super(true); @@ -231,6 +233,18 @@ public V getBest(byte[] b, int offset, int len) return t._value; } + @Override + public boolean isEmpty() + { + return keySet().isEmpty(); + } + + @Override + public int size() + { + return keySet().size(); + } + @Override public V getBest(String s, int offset, int len) { @@ -394,10 +408,4 @@ private static void keySet(Set set, TreeTrie t) } } } - - @Override - public boolean isFull() - { - return false; - } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Trie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Trie.java deleted file mode 100644 index e6bf16780f18..000000000000 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/Trie.java +++ /dev/null @@ -1,230 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under -// the terms of the Eclipse Public License 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0 -// -// This Source Code may also be made available under the following -// Secondary Licenses when the conditions for such availability set -// forth in the Eclipse Public License, v. 2.0 are satisfied: -// the Apache License v2.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.util; - -import java.nio.ByteBuffer; -import java.util.Collections; -import java.util.Set; - -/** - * A Trie String lookup data structure. - * - * @param the Trie entry type - */ -public interface Trie -{ - - /** - * Put an entry into the Trie - * - * @param s The key for the entry - * @param v The value of the entry - * @return True if the Trie had capacity to add the field. - */ - public boolean put(String s, V v); - - /** - * Put a value as both a key and a value. - * - * @param v The value and key - * @return True if the Trie had capacity to add the field. - */ - public boolean put(V v); - - public V remove(String s); - - /** - * Get an exact match from a String key - * - * @param s The key - * @return the value for the string key - */ - public V get(String s); - - /** - * Get an exact match from a String key - * - * @param s The key - * @param offset The offset within the string of the key - * @param len the length of the key - * @return the value for the string / offset / length - */ - public V get(String s, int offset, int len); - - /** - * Get an exact match from a segment of a ByteBuufer as key - * - * @param b The buffer - * @return The value or null if not found - */ - public V get(ByteBuffer b); - - /** - * Get an exact match from a segment of a ByteBuufer as key - * - * @param b The buffer - * @param offset The offset within the buffer of the key - * @param len the length of the key - * @return The value or null if not found - */ - public V get(ByteBuffer b, int offset, int len); - - /** - * Get the best match from key in a String. - * - * @param s The string - * @return The value or null if not found - */ - public V getBest(String s); - - /** - * Get the best match from key in a String. - * - * @param s The string - * @param offset The offset within the string of the key - * @param len the length of the key - * @return The value or null if not found - */ - public V getBest(String s, int offset, int len); - - /** - * Get the best match from key in a byte array. - * The key is assumed to by ISO_8859_1 characters. - * - * @param b The buffer - * @param offset The offset within the array of the key - * @param len the length of the key - * @return The value or null if not found - */ - public V getBest(byte[] b, int offset, int len); - - /** - * Get the best match from key in a byte buffer. - * The key is assumed to by ISO_8859_1 characters. - * - * @param b The buffer - * @param offset The offset within the buffer of the key - * @param len the length of the key - * @return The value or null if not found - */ - public V getBest(ByteBuffer b, int offset, int len); - - public Set keySet(); - - public boolean isFull(); - - public boolean isCaseInsensitive(); - - public void clear(); - - static Trie empty(final boolean caseInsensitive) - { - return new Trie() - { - @Override - public boolean put(String s, Object o) - { - return false; - } - - @Override - public boolean put(Object o) - { - return false; - } - - @Override - public T remove(String s) - { - return null; - } - - @Override - public T get(String s) - { - return null; - } - - @Override - public T get(String s, int offset, int len) - { - return null; - } - - @Override - public T get(ByteBuffer b) - { - return null; - } - - @Override - public T get(ByteBuffer b, int offset, int len) - { - return null; - } - - @Override - public T getBest(String s) - { - return null; - } - - @Override - public T getBest(String s, int offset, int len) - { - return null; - } - - @Override - public T getBest(byte[] b, int offset, int len) - { - return null; - } - - @Override - public T getBest(ByteBuffer b, int offset, int len) - { - return null; - } - - @Override - public Set keySet() - { - return Collections.emptySet(); - } - - @Override - public boolean isFull() - { - return true; - } - - @Override - public boolean isCaseInsensitive() - { - return caseInsensitive; - } - - @Override - public void clear() - { - } - }; - } -} diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/IndexTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/IndexTest.java new file mode 100644 index 000000000000..8c3ba90c19b9 --- /dev/null +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/IndexTest.java @@ -0,0 +1,65 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under +// the terms of the Eclipse Public License 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0 +// +// This Source Code may also be made available under the following +// Secondary Licenses when the conditions for such availability set +// forth in the Eclipse Public License, v. 2.0 are satisfied: +// the Apache License v2.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.util; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + +public class IndexTest +{ + @Test + public void belowMaxCapacityTest() + { + int size = 10_450; + + Index.Builder builder = new Index.Builder<>(); + builder.caseSensitive(true); + for (int i = 0; i < size; i++) + { + builder.with("/test/group" + i, i); + } + Index index = builder.build(); + + for (int i = 0; i < size; i++) + { + Integer integer = index.get("/test/group" + i); + if (integer == null) + fail("missing entry for '/test/group" + i + "'"); + else if (integer != i) + fail("incorrect value for '/test/group" + i + "' (" + integer + ")"); + } + } + + @Test + public void overMaxCapacityTest() + { + int size = 11_000; + + Index.Builder builder = new Index.Builder<>(); + builder.caseSensitive(true); + for (int i = 0; i < size; i++) + { + builder.with("/test/group" + i, i); + } + + assertThrows(IllegalArgumentException.class, builder::build); + } +} diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/TrieTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/TrieTest.java index f2e55cfcddb7..41083c079799 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/TrieTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/TrieTest.java @@ -21,30 +21,32 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.stream.Stream; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import static org.eclipse.jetty.util.AbstractTrie.requiredCapacity; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.in; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; public class TrieTest { public static Stream implementations() { - List impls = new ArrayList<>(); + List> impls = new ArrayList<>(); impls.add(new ArrayTrie(128)); - impls.add(new TreeTrie()); - impls.add(new ArrayTernaryTrie(128)); + impls.add(new ArrayTernaryTrie(true, 128)); + impls.add(new ArrayTernaryTrie.Growing(true, 128, 128)); - for (Trie trie : impls) + for (AbstractTrie trie : impls) { trie.put("hello", 1); trie.put("He", 2); @@ -62,26 +64,7 @@ public static Stream implementations() @ParameterizedTest @MethodSource("implementations") - public void testOverflow(Trie trie) throws Exception - { - int i = 0; - while (true) - { - if (++i > 10000) - break; // must not be fixed size - if (!trie.put("prefix" + i, i)) - { - assertTrue(trie.isFull()); - break; - } - } - - assertTrue(!trie.isFull() || !trie.put("overflow", 0)); - } - - @ParameterizedTest - @MethodSource("implementations") - public void testKeySet(Trie trie) throws Exception + public void testKeySet(AbstractTrie trie) throws Exception { String[] values = new String[]{ "hello", @@ -103,7 +86,7 @@ public void testKeySet(Trie trie) throws Exception @ParameterizedTest @MethodSource("implementations") - public void testGetString(Trie trie) throws Exception + public void testGetString(AbstractTrie trie) throws Exception { assertEquals(1, trie.get("hello").intValue()); assertEquals(2, trie.get("He").intValue()); @@ -130,7 +113,7 @@ public void testGetString(Trie trie) throws Exception @ParameterizedTest @MethodSource("implementations") - public void testGetBuffer(Trie trie) throws Exception + public void testGetBuffer(AbstractTrie trie) throws Exception { assertEquals(1, trie.get(BufferUtil.toBuffer("xhellox"), 1, 5).intValue()); assertEquals(2, trie.get(BufferUtil.toBuffer("xhellox"), 1, 2).intValue()); @@ -155,7 +138,7 @@ public void testGetBuffer(Trie trie) throws Exception @ParameterizedTest @MethodSource("implementations") - public void testGetDirectBuffer(Trie trie) throws Exception + public void testGetDirectBuffer(AbstractTrie trie) throws Exception { assertEquals(1, trie.get(BufferUtil.toDirectBuffer("xhellox"), 1, 5).intValue()); assertEquals(2, trie.get(BufferUtil.toDirectBuffer("xhellox"), 1, 2).intValue()); @@ -180,7 +163,7 @@ public void testGetDirectBuffer(Trie trie) throws Exception @ParameterizedTest @MethodSource("implementations") - public void testGetBestArray(Trie trie) throws Exception + public void testGetBestArray(AbstractTrie trie) throws Exception { assertEquals(1, trie.getBest(StringUtil.getUtf8Bytes("xhelloxxxx"), 1, 8).intValue()); assertEquals(2, trie.getBest(StringUtil.getUtf8Bytes("xhelxoxxxx"), 1, 8).intValue()); @@ -198,7 +181,7 @@ public void testGetBestArray(Trie trie) throws Exception @ParameterizedTest @MethodSource("implementations") - public void testGetBestBuffer(Trie trie) throws Exception + public void testGetBestBuffer(AbstractTrie trie) throws Exception { assertEquals(1, trie.getBest(BufferUtil.toBuffer("xhelloxxxx"), 1, 8).intValue()); assertEquals(2, trie.getBest(BufferUtil.toBuffer("xhelxoxxxx"), 1, 8).intValue()); @@ -219,7 +202,7 @@ public void testGetBestBuffer(Trie trie) throws Exception @ParameterizedTest @MethodSource("implementations") - public void testGetBestDirectBuffer(Trie trie) throws Exception + public void testGetBestDirectBuffer(AbstractTrie trie) throws Exception { assertEquals(1, trie.getBest(BufferUtil.toDirectBuffer("xhelloxxxx"), 1, 8).intValue()); assertEquals(2, trie.getBest(BufferUtil.toDirectBuffer("xhelxoxxxx"), 1, 8).intValue()); @@ -240,7 +223,7 @@ public void testGetBestDirectBuffer(Trie trie) throws Exception @ParameterizedTest @MethodSource("implementations") - public void testFull(Trie trie) throws Exception + public void testFull(AbstractTrie trie) throws Exception { if (!(trie instanceof ArrayTrie || trie instanceof ArrayTernaryTrie)) return; @@ -250,4 +233,33 @@ public void testFull(Trie trie) throws Exception testGetBestArray(trie); testGetBestBuffer(trie); } + + @Test + public void testRequiredCapacity() + { + assertThat(requiredCapacity(Set.of("ABC", "abc"), true), is(6)); + assertThat(requiredCapacity(Set.of("ABC", "abc"), false), is(3)); + assertThat(requiredCapacity(Set.of(""), false), is(0)); + assertThat(requiredCapacity(Set.of("ABC", ""), false), is(3)); + assertThat(requiredCapacity(Set.of("ABC"), false), is(3)); + assertThat(requiredCapacity(Set.of("ABC", "XYZ"), false), is(6)); + assertThat(requiredCapacity(Set.of("A00", "A11"), false), is(5)); + assertThat(requiredCapacity(Set.of("A00", "A01", "A10", "A11"), false), is(7)); + assertThat(requiredCapacity(Set.of("A", "AB"), false), is(2)); + assertThat(requiredCapacity(Set.of("A", "ABC"), false), is(3)); + assertThat(requiredCapacity(Set.of("A", "ABCD"), false), is(4)); + assertThat(requiredCapacity(Set.of("AB", "ABC"), false), is(3)); + assertThat(requiredCapacity(Set.of("ABC", "ABCD"), false), is(4)); + assertThat(requiredCapacity(Set.of("ABC", "ABCDEF"), false), is(6)); + assertThat(requiredCapacity(Set.of("AB", "A"), false), is(2)); + assertThat(requiredCapacity(Set.of("ABC", "ABCDEF"), false), is(6)); + assertThat(requiredCapacity(Set.of("ABCDEF", "ABC"), false), is(6)); + assertThat(requiredCapacity(Set.of("ABC", "ABCDEF", "ABX"), false), is(7)); + assertThat(requiredCapacity(Set.of("ABCDEF", "ABC", "ABX"), false), is(7)); + assertThat(requiredCapacity(Set.of("ADEF", "AQPR4", "AQZ"), false), is(9)); + assertThat(requiredCapacity(Set.of("111", "ADEF", "AQPR4", "AQZ", "999"), false), is(15)); + assertThat(requiredCapacity(Set.of("utf-16", "utf-8"), false), is(7)); + assertThat(requiredCapacity(Set.of("utf-16", "utf-8", "utf16", "utf8"), false), is(10)); + assertThat(requiredCapacity(Set.of("utf-8", "utf8", "utf-16", "utf16", "iso-8859-1", "iso_8859_1"), false), is(27)); + } } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClassMatcher.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClassMatcher.java index 63ce3599f653..2d3f7005c9fb 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClassMatcher.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClassMatcher.java @@ -38,8 +38,8 @@ import java.util.function.Predicate; import java.util.function.Supplier; -import org.eclipse.jetty.util.ArrayTernaryTrie; import org.eclipse.jetty.util.IncludeExcludeSet; +import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.URIUtil; @@ -180,7 +180,10 @@ public String getModule() public static class ByPackage extends AbstractSet implements Predicate { - private final ArrayTernaryTrie.Growing _entries = new ArrayTernaryTrie.Growing<>(false, 512, 512); + private final Index.Mutable _entries = new Index.Builder() + .caseSensitive(true) + .mutable() + .build(); @Override public boolean test(String name) @@ -383,7 +386,10 @@ public boolean test(URI uri) @SuppressWarnings("serial") public static class ByModule extends HashSet implements Predicate { - private final ArrayTernaryTrie.Growing _entries = new ArrayTernaryTrie.Growing<>(false, 512, 512); + private final Index.Mutable _entries = new Index.Builder() + .caseSensitive(true) + .mutable() + .build(); @Override public boolean test(URI uri) diff --git a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/ExtensionConfig.java b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/ExtensionConfig.java index 10a6fbb0eb66..876e94618497 100644 --- a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/ExtensionConfig.java +++ b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/ExtensionConfig.java @@ -29,23 +29,20 @@ import java.util.stream.Collectors; import org.eclipse.jetty.http.QuotedCSV; -import org.eclipse.jetty.util.ArrayTrie; +import org.eclipse.jetty.util.Index; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.Trie; /** * Represents an Extension Configuration, as seen during the connection Handshake process. */ public class ExtensionConfig { - private static final Trie CACHE = new ArrayTrie<>(512); - - static - { - CACHE.put("identity", new ExtensionConfig("identity")); - CACHE.put("permessage-deflate", new ExtensionConfig("permessage-deflate")); - CACHE.put("permessage-deflate; client_max_window_bits", new ExtensionConfig("permessage-deflate; client_max_window_bits")); - } + private static final Index CACHE = new Index.Builder() + .caseSensitive(false) + .with("identity", new ExtensionConfig("identity")) + .with("permessage-deflate", new ExtensionConfig("permessage-deflate")) + .with("permessage-deflate; client_max_window_bits", new ExtensionConfig("permessage-deflate; client_max_window_bits")) + .build(); /** * Parse a single parameterized name.