Merged branch 'jetty-10.0.x' into 'jetty-11.0.x'.
Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
commit
fa232a4684
|
@ -26,10 +26,9 @@ import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpTokens.EndOfContent;
|
import org.eclipse.jetty.http.HttpTokens.EndOfContent;
|
||||||
import org.eclipse.jetty.util.ArrayTrie;
|
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
|
import org.eclipse.jetty.util.Index;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.Trie;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -92,13 +91,11 @@ public class HttpGenerator
|
||||||
private final int _send;
|
private final int _send;
|
||||||
private static final int SEND_SERVER = 0x01;
|
private static final int SEND_SERVER = 0x01;
|
||||||
private static final int SEND_XPOWEREDBY = 0x02;
|
private static final int SEND_XPOWEREDBY = 0x02;
|
||||||
private static final Trie<Boolean> ASSUMED_CONTENT_METHODS = new ArrayTrie<>(8);
|
private static final Index<Boolean> ASSUMED_CONTENT_METHODS = new Index.Builder<Boolean>()
|
||||||
|
.caseSensitive(false)
|
||||||
static
|
.with(HttpMethod.POST.asString(), Boolean.TRUE)
|
||||||
{
|
.with(HttpMethod.PUT.asString(), Boolean.TRUE)
|
||||||
ASSUMED_CONTENT_METHODS.put(HttpMethod.POST.asString(), Boolean.TRUE);
|
.build();
|
||||||
ASSUMED_CONTENT_METHODS.put(HttpMethod.PUT.asString(), Boolean.TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setJettyVersion(String serverVersion)
|
public static void setJettyVersion(String serverVersion)
|
||||||
{
|
{
|
||||||
|
@ -679,7 +676,7 @@ public class HttpGenerator
|
||||||
// Calculate how to end _content and connection, _content length and transfer encoding
|
// Calculate how to end _content and connection, _content length and transfer encoding
|
||||||
// settings from http://tools.ietf.org/html/rfc7230#section-3.3.3
|
// 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 assumedContent = assumedContentRequest || contentType || chunkedHint;
|
||||||
boolean noContentRequest = request != null && contentLength <= 0 && !assumedContent;
|
boolean noContentRequest = request != null && contentLength <= 0 && !assumedContent;
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,8 @@ package org.eclipse.jetty.http;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
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.StringUtil;
|
||||||
import org.eclipse.jetty.util.Trie;
|
|
||||||
|
|
||||||
public enum HttpHeader
|
public enum HttpHeader
|
||||||
{
|
{
|
||||||
|
@ -133,21 +132,12 @@ public enum HttpHeader
|
||||||
C_AUTHORITY(":authority", true),
|
C_AUTHORITY(":authority", true),
|
||||||
C_PATH(":path", true),
|
C_PATH(":path", true),
|
||||||
C_STATUS(":status", true),
|
C_STATUS(":status", true),
|
||||||
C_PROTOCOL(":protocol"),
|
C_PROTOCOL(":protocol");
|
||||||
|
|
||||||
UNKNOWN("::UNKNOWN::", true);
|
public static final Index<HttpHeader> CACHE = new Index.Builder<HttpHeader>()
|
||||||
|
.caseSensitive(false)
|
||||||
public static final Trie<HttpHeader> CACHE = new ArrayTrie<>(630);
|
.withAll(HttpHeader.values(), HttpHeader::toString)
|
||||||
|
.build();
|
||||||
static
|
|
||||||
{
|
|
||||||
for (HttpHeader header : HttpHeader.values())
|
|
||||||
{
|
|
||||||
if (header != UNKNOWN)
|
|
||||||
if (!CACHE.put(header.toString(), header))
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String _string;
|
private final String _string;
|
||||||
private final String _lowerCase;
|
private final String _lowerCase;
|
||||||
|
|
|
@ -21,9 +21,8 @@ package org.eclipse.jetty.http;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.ArrayTrie;
|
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
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"),
|
TE("TE"),
|
||||||
BYTES("bytes"),
|
BYTES("bytes"),
|
||||||
NO_CACHE("no-cache"),
|
NO_CACHE("no-cache"),
|
||||||
UPGRADE("Upgrade"),
|
UPGRADE("Upgrade");
|
||||||
UNKNOWN("::UNKNOWN::");
|
|
||||||
|
|
||||||
public static final Trie<HttpHeaderValue> CACHE = new ArrayTrie<HttpHeaderValue>();
|
public static final Index<HttpHeaderValue> CACHE = new Index.Builder<HttpHeaderValue>()
|
||||||
|
.caseSensitive(false)
|
||||||
static
|
.withAll(HttpHeaderValue.values(), HttpHeaderValue::toString)
|
||||||
{
|
.build();
|
||||||
for (HttpHeaderValue value : HttpHeaderValue.values())
|
|
||||||
{
|
|
||||||
if (value != UNKNOWN)
|
|
||||||
CACHE.put(value.toString(), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String _string;
|
private final String _string;
|
||||||
private final ByteBuffer _buffer;
|
private final ByteBuffer _buffer;
|
||||||
|
|
|
@ -20,10 +20,8 @@ package org.eclipse.jetty.http;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
import org.eclipse.jetty.util.Index;
|
||||||
import org.eclipse.jetty.util.ArrayTrie;
|
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.Trie;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Known HTTP Methods
|
* Known HTTP Methods
|
||||||
|
@ -142,29 +140,24 @@ public enum HttpMethod
|
||||||
return _method;
|
return _method;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Trie<HttpMethod> INSENSITIVE_CACHE = new ArrayTrie<>(252);
|
public static final Index<HttpMethod> INSENSITIVE_CACHE = new Index.Builder<HttpMethod>()
|
||||||
public static final Trie<HttpMethod> CACHE = new ArrayTernaryTrie<>(false, 300);
|
.caseSensitive(false)
|
||||||
public static final Trie<HttpMethod> LOOK_AHEAD = new ArrayTernaryTrie<>(false, 330);
|
.withAll(HttpMethod.values(), HttpMethod::asString)
|
||||||
|
.build();
|
||||||
|
public static final Index<HttpMethod> CACHE = new Index.Builder<HttpMethod>()
|
||||||
|
.caseSensitive(true)
|
||||||
|
.withAll(HttpMethod.values(), HttpMethod::asString)
|
||||||
|
.build();
|
||||||
|
public static final Index<HttpMethod> LOOK_AHEAD = new Index.Builder<HttpMethod>()
|
||||||
|
.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 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 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 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 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 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);
|
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.
|
* Optimized lookup to find a method name and trailing space in a byte array.
|
||||||
|
|
|
@ -21,15 +21,15 @@ package org.eclipse.jetty.http;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpTokens.EndOfContent;
|
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.BufferUtil;
|
||||||
|
import org.eclipse.jetty.util.Index;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.Trie;
|
|
||||||
import org.eclipse.jetty.util.Utf8StringBuilder;
|
import org.eclipse.jetty.util.Utf8StringBuilder;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -67,7 +67,7 @@ import static org.eclipse.jetty.http.HttpCompliance.Violation.WHITESPACE_AFTER_F
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* For performance, the parse is heavily dependent on the
|
* 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
|
* single pass for both the structure ( : and CRLF ) and semantic (which
|
||||||
* header and value) of a header. Specifically the static {@link HttpHeader#CACHE}
|
* header and value) of a header. Specifically the static {@link HttpHeader#CACHE}
|
||||||
* is used to lookup common combinations of headers and values
|
* 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
|
* determine the header name even if the name:value combination is not cached
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public static final Trie<HttpField> CACHE = new ArrayTrie<>(2048);
|
public static final Index<HttpField> CACHE = new Index.Builder<HttpField>()
|
||||||
private static final Trie<HttpField> NO_CACHE = Trie.empty(true);
|
.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<String, HttpField> 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<String, HttpField> 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<HttpField> NO_CACHE = new Index.Builder<HttpField>()
|
||||||
|
.caseSensitive(false)
|
||||||
|
.mutable()
|
||||||
|
.maxCapacity(0)
|
||||||
|
.build();
|
||||||
|
|
||||||
// States
|
// States
|
||||||
public enum FieldState
|
public enum FieldState
|
||||||
|
@ -181,65 +247,12 @@ public class HttpParser
|
||||||
private boolean _headResponse;
|
private boolean _headResponse;
|
||||||
private boolean _cr;
|
private boolean _cr;
|
||||||
private ByteBuffer _contentChunk;
|
private ByteBuffer _contentChunk;
|
||||||
private Trie<HttpField> _fieldCache;
|
private Index.Mutable<HttpField> _fieldCache;
|
||||||
private int _length;
|
private int _length;
|
||||||
private final StringBuilder _string = new StringBuilder();
|
private final StringBuilder _string = new StringBuilder();
|
||||||
private int _headerCacheSize = 1024;
|
private int _headerCacheSize = 1024;
|
||||||
private boolean _headerCacheCaseSensitive;
|
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()
|
private static HttpCompliance compliance()
|
||||||
{
|
{
|
||||||
return RFC7230;
|
return RFC7230;
|
||||||
|
@ -1052,14 +1065,19 @@ public class HttpParser
|
||||||
if (_fieldCache == null)
|
if (_fieldCache == null)
|
||||||
{
|
{
|
||||||
_fieldCache = (getHeaderCacheSize() > 0 && (_version != null && _version == HttpVersion.HTTP_1_1))
|
_fieldCache = (getHeaderCacheSize() > 0 && (_version != null && _version == HttpVersion.HTTP_1_1))
|
||||||
? new ArrayTernaryTrie<>(getHeaderCacheSize())
|
? new Index.Builder<HttpField>()
|
||||||
|
.caseSensitive(false)
|
||||||
|
.mutable()
|
||||||
|
.maxCapacity(getHeaderCacheSize())
|
||||||
|
.build()
|
||||||
: NO_CACHE;
|
: NO_CACHE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_fieldCache.isFull())
|
if (_field == null)
|
||||||
|
_field = new HttpField(_header, caseInsensitiveHeader(_headerString, _header.asString()), _valueString);
|
||||||
|
if (!_fieldCache.put(_field))
|
||||||
{
|
{
|
||||||
if (_field == null)
|
_fieldCache.clear();
|
||||||
_field = new HttpField(_header, caseInsensitiveHeader(_headerString, _header.asString()), _valueString);
|
|
||||||
_fieldCache.put(_field);
|
_fieldCache.put(_field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1898,7 +1916,7 @@ public class HttpParser
|
||||||
_fieldState = state;
|
_fieldState = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Trie<HttpField> getFieldCache()
|
public Index<HttpField> getFieldCache()
|
||||||
{
|
{
|
||||||
return _fieldCache;
|
return _fieldCache;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,8 @@ package org.eclipse.jetty.http;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.ArrayTrie;
|
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.Trie;
|
import org.eclipse.jetty.util.Index;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP and WebSocket Schemes
|
* HTTP and WebSocket Schemes
|
||||||
|
@ -34,15 +33,10 @@ public enum HttpScheme
|
||||||
WS("ws", 80),
|
WS("ws", 80),
|
||||||
WSS("wss", 443);
|
WSS("wss", 443);
|
||||||
|
|
||||||
public static final Trie<HttpScheme> CACHE = new ArrayTrie<HttpScheme>();
|
public static final Index<HttpScheme> CACHE = new Index.Builder<HttpScheme>()
|
||||||
|
.caseSensitive(false)
|
||||||
static
|
.withAll(HttpScheme.values(), HttpScheme::asString)
|
||||||
{
|
.build();
|
||||||
for (HttpScheme version : HttpScheme.values())
|
|
||||||
{
|
|
||||||
CACHE.put(version.asString(), version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String _string;
|
private final String _string;
|
||||||
private final ByteBuffer _buffer;
|
private final ByteBuffer _buffer;
|
||||||
|
|
|
@ -20,9 +20,8 @@ package org.eclipse.jetty.http;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
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.StringUtil;
|
||||||
import org.eclipse.jetty.util.Trie;
|
|
||||||
|
|
||||||
public enum HttpVersion
|
public enum HttpVersion
|
||||||
{
|
{
|
||||||
|
@ -31,15 +30,10 @@ public enum HttpVersion
|
||||||
HTTP_1_1("HTTP/1.1", 11),
|
HTTP_1_1("HTTP/1.1", 11),
|
||||||
HTTP_2("HTTP/2.0", 20);
|
HTTP_2("HTTP/2.0", 20);
|
||||||
|
|
||||||
public static final Trie<HttpVersion> CACHE = new ArrayTrie<HttpVersion>();
|
public static final Index<HttpVersion> CACHE = new Index.Builder<HttpVersion>()
|
||||||
|
.caseSensitive(false)
|
||||||
static
|
.withAll(HttpVersion.values(), HttpVersion::toString)
|
||||||
{
|
.build();
|
||||||
for (HttpVersion version : HttpVersion.values())
|
|
||||||
{
|
|
||||||
CACHE.put(version.toString(), version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optimised lookup to find an Http Version and whitespace in a byte array.
|
* Optimised lookup to find an Http Version and whitespace in a byte array.
|
||||||
|
|
|
@ -32,10 +32,9 @@ import java.util.Map.Entry;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.ArrayTrie;
|
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
|
import org.eclipse.jetty.util.Index;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.Trie;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -46,7 +45,6 @@ public class MimeTypes
|
||||||
{
|
{
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(MimeTypes.class);
|
private static final Logger LOG = LoggerFactory.getLogger(MimeTypes.class);
|
||||||
private static final Trie<ByteBuffer> TYPES = new ArrayTrie<ByteBuffer>(512);
|
|
||||||
private static final Map<String, String> __dftMimeMap = new HashMap<String, String>();
|
private static final Map<String, String> __dftMimeMap = new HashMap<String, String>();
|
||||||
private static final Map<String, String> __inferredEncodings = new HashMap<String, String>();
|
private static final Map<String, String> __inferredEncodings = new HashMap<String, String>();
|
||||||
private static final Map<String, String> __assumedEncodings = new HashMap<String, String>();
|
private static final Map<String, String> __assumedEncodings = new HashMap<String, String>();
|
||||||
|
@ -168,23 +166,30 @@ public class MimeTypes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Trie<MimeTypes.Type> CACHE = new ArrayTrie<>(512);
|
public static final Index<Type> CACHE = new Index.Builder<Type>()
|
||||||
|
.caseSensitive(false)
|
||||||
|
.withAll(() ->
|
||||||
|
{
|
||||||
|
Map<String, Type> 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
|
static
|
||||||
{
|
{
|
||||||
for (MimeTypes.Type type : MimeTypes.Type.values())
|
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())
|
if (type.isCharsetAssumed())
|
||||||
__assumedEncodings.put(type.asString(), type.getCharsetString());
|
__assumedEncodings.put(type.asString(), type.getCharsetString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,7 @@ import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
import org.eclipse.jetty.util.Index;
|
||||||
import org.eclipse.jetty.util.Trie;
|
|
||||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
import org.eclipse.jetty.util.component.Dumpable;
|
import org.eclipse.jetty.util.component.Dumpable;
|
||||||
|
@ -49,9 +48,18 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(PathMappings.class);
|
private static final Logger LOG = LoggerFactory.getLogger(PathMappings.class);
|
||||||
private final Set<MappedResource<E>> _mappings = new TreeSet<>(Comparator.comparing(MappedResource::getPathSpec));
|
private final Set<MappedResource<E>> _mappings = new TreeSet<>(Comparator.comparing(MappedResource::getPathSpec));
|
||||||
|
|
||||||
private Trie<MappedResource<E>> _exactMap = new ArrayTernaryTrie<>(false);
|
private final Index.Mutable<MappedResource<E>> _exactMap = new Index.Builder<MappedResource<E>>()
|
||||||
private Trie<MappedResource<E>> _prefixMap = new ArrayTernaryTrie<>(false);
|
.caseSensitive(true)
|
||||||
private Trie<MappedResource<E>> _suffixMap = new ArrayTernaryTrie<>(false);
|
.mutable()
|
||||||
|
.build();
|
||||||
|
private final Index.Mutable<MappedResource<E>> _prefixMap = new Index.Builder<MappedResource<E>>()
|
||||||
|
.caseSensitive(true)
|
||||||
|
.mutable()
|
||||||
|
.build();
|
||||||
|
private final Index.Mutable<MappedResource<E>> _suffixMap = new Index.Builder<MappedResource<E>>()
|
||||||
|
.caseSensitive(true)
|
||||||
|
.mutable()
|
||||||
|
.build();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String dump()
|
public String dump()
|
||||||
|
@ -136,10 +144,9 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
||||||
case EXACT:
|
case EXACT:
|
||||||
{
|
{
|
||||||
int i = path.length();
|
int i = path.length();
|
||||||
final Trie<MappedResource<E>> exact_map = _exactMap;
|
|
||||||
while (i >= 0)
|
while (i >= 0)
|
||||||
{
|
{
|
||||||
MappedResource<E> candidate = exact_map.getBest(path, 0, i);
|
MappedResource<E> candidate = _exactMap.getBest(path, 0, i);
|
||||||
if (candidate == null)
|
if (candidate == null)
|
||||||
break;
|
break;
|
||||||
if (candidate.getPathSpec().matches(path))
|
if (candidate.getPathSpec().matches(path))
|
||||||
|
@ -152,10 +159,9 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
||||||
case PREFIX_GLOB:
|
case PREFIX_GLOB:
|
||||||
{
|
{
|
||||||
int i = path.length();
|
int i = path.length();
|
||||||
final Trie<MappedResource<E>> prefix_map = _prefixMap;
|
|
||||||
while (i >= 0)
|
while (i >= 0)
|
||||||
{
|
{
|
||||||
MappedResource<E> candidate = prefix_map.getBest(path, 0, i);
|
MappedResource<E> candidate = _prefixMap.getBest(path, 0, i);
|
||||||
if (candidate == null)
|
if (candidate == null)
|
||||||
break;
|
break;
|
||||||
if (candidate.getPathSpec().matches(path))
|
if (candidate.getPathSpec().matches(path))
|
||||||
|
@ -168,10 +174,9 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
||||||
case SUFFIX_GLOB:
|
case SUFFIX_GLOB:
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
final Trie<MappedResource<E>> suffix_map = _suffixMap;
|
|
||||||
while ((i = path.indexOf('.', i + 1)) > 0)
|
while ((i = path.indexOf('.', i + 1)) > 0)
|
||||||
{
|
{
|
||||||
MappedResource<E> candidate = suffix_map.get(path, i + 1, path.length() - i - 1);
|
MappedResource<E> candidate = _suffixMap.get(path, i + 1, path.length() - i - 1);
|
||||||
if (candidate != null && candidate.getPathSpec().matches(path))
|
if (candidate != null && candidate.getPathSpec().matches(path))
|
||||||
return candidate;
|
return candidate;
|
||||||
}
|
}
|
||||||
|
@ -230,24 +235,18 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
||||||
{
|
{
|
||||||
case EXACT:
|
case EXACT:
|
||||||
String exact = pathSpec.getPrefix();
|
String exact = pathSpec.getPrefix();
|
||||||
while (exact != null && !_exactMap.put(exact, entry))
|
if (exact != null)
|
||||||
{
|
_exactMap.put(exact, entry);
|
||||||
_exactMap = new ArrayTernaryTrie<>((ArrayTernaryTrie<MappedResource<E>>)_exactMap, 1.5);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case PREFIX_GLOB:
|
case PREFIX_GLOB:
|
||||||
String prefix = pathSpec.getPrefix();
|
String prefix = pathSpec.getPrefix();
|
||||||
while (prefix != null && !_prefixMap.put(prefix, entry))
|
if (prefix != null)
|
||||||
{
|
_prefixMap.put(prefix, entry);
|
||||||
_prefixMap = new ArrayTernaryTrie<>((ArrayTernaryTrie<MappedResource<E>>)_prefixMap, 1.5);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case SUFFIX_GLOB:
|
case SUFFIX_GLOB:
|
||||||
String suffix = pathSpec.getSuffix();
|
String suffix = pathSpec.getSuffix();
|
||||||
while (suffix != null && !_suffixMap.put(suffix, entry))
|
if (suffix != null)
|
||||||
{
|
_suffixMap.put(suffix, entry);
|
||||||
_suffixMap = new ArrayTernaryTrie<>((ArrayTernaryTrie<MappedResource<E>>)_prefixMap, 1.5);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,20 +94,6 @@ public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getMessagesIn()
|
|
||||||
{
|
|
||||||
HTTP2ClientSession session = (HTTP2ClientSession)getSession();
|
|
||||||
return session.getStreamsOpened();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getMessagesOut()
|
|
||||||
{
|
|
||||||
HTTP2ClientSession session = (HTTP2ClientSession)getSession();
|
|
||||||
return session.getStreamsClosed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOpen()
|
public void onOpen()
|
||||||
{
|
{
|
||||||
|
@ -127,15 +113,11 @@ public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
|
||||||
ISession session = getSession();
|
ISession session = getSession();
|
||||||
|
|
||||||
int windowDelta = client.getInitialSessionRecvWindow() - FlowControlStrategy.DEFAULT_WINDOW_SIZE;
|
int windowDelta = client.getInitialSessionRecvWindow() - FlowControlStrategy.DEFAULT_WINDOW_SIZE;
|
||||||
|
session.updateRecvWindow(windowDelta);
|
||||||
if (windowDelta > 0)
|
if (windowDelta > 0)
|
||||||
{
|
|
||||||
session.updateRecvWindow(windowDelta);
|
|
||||||
session.frames(null, List.of(prefaceFrame, settingsFrame, new WindowUpdateFrame(0, windowDelta)), this);
|
session.frames(null, List.of(prefaceFrame, settingsFrame, new WindowUpdateFrame(0, windowDelta)), this);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
session.frames(null, List.of(prefaceFrame, settingsFrame), this);
|
session.frames(null, List.of(prefaceFrame, settingsFrame), this);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package org.eclipse.jetty.http2.client;
|
package org.eclipse.jetty.http2.client;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.MetaData;
|
import org.eclipse.jetty.http.MetaData;
|
||||||
|
import org.eclipse.jetty.http2.CloseState;
|
||||||
import org.eclipse.jetty.http2.ErrorCode;
|
import org.eclipse.jetty.http2.ErrorCode;
|
||||||
import org.eclipse.jetty.http2.FlowControlStrategy;
|
import org.eclipse.jetty.http2.FlowControlStrategy;
|
||||||
import org.eclipse.jetty.http2.HTTP2Session;
|
import org.eclipse.jetty.http2.HTTP2Session;
|
||||||
|
@ -62,7 +63,10 @@ public class HTTP2ClientSession extends HTTP2Session
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
stream.process(frame, Callback.NOOP);
|
stream.process(frame, Callback.NOOP);
|
||||||
|
boolean closed = stream.updateClose(frame.isEndStream(), CloseState.Event.RECEIVED);
|
||||||
notifyHeaders(stream, frame);
|
notifyHeaders(stream, frame);
|
||||||
|
if (closed)
|
||||||
|
removeStream(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -134,7 +134,7 @@ public class AsyncServletTest extends AbstractTest
|
||||||
HeadersFrame frame = new HeadersFrame(metaData, null, true);
|
HeadersFrame frame = new HeadersFrame(metaData, null, true);
|
||||||
FuturePromise<Stream> promise = new FuturePromise<>();
|
FuturePromise<Stream> promise = new FuturePromise<>();
|
||||||
CountDownLatch responseLatch = new CountDownLatch(1);
|
CountDownLatch responseLatch = new CountDownLatch(1);
|
||||||
CountDownLatch resetLatch = new CountDownLatch(1);
|
CountDownLatch failLatch = new CountDownLatch(1);
|
||||||
session.newStream(frame, promise, new Stream.Listener.Adapter()
|
session.newStream(frame, promise, new Stream.Listener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -144,9 +144,10 @@ public class AsyncServletTest extends AbstractTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReset(Stream stream, ResetFrame frame)
|
public void onFailure(Stream stream, int error, String reason, Throwable failure, Callback callback)
|
||||||
{
|
{
|
||||||
resetLatch.countDown();
|
failLatch.countDown();
|
||||||
|
callback.succeeded();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Stream stream = promise.get(5, TimeUnit.SECONDS);
|
Stream stream = promise.get(5, TimeUnit.SECONDS);
|
||||||
|
@ -154,7 +155,7 @@ public class AsyncServletTest extends AbstractTest
|
||||||
|
|
||||||
assertTrue(serverLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
|
assertTrue(serverLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
|
||||||
assertFalse(responseLatch.await(idleTimeout + 1000, TimeUnit.MILLISECONDS));
|
assertFalse(responseLatch.await(idleTimeout + 1000, TimeUnit.MILLISECONDS));
|
||||||
assertTrue(resetLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
|
assertTrue(failLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -137,10 +137,10 @@ public abstract class FlowControlStrategyTest
|
||||||
@Test
|
@Test
|
||||||
public void testWindowSizeUpdates() throws Exception
|
public void testWindowSizeUpdates() throws Exception
|
||||||
{
|
{
|
||||||
final CountDownLatch prefaceLatch = new CountDownLatch(1);
|
CountDownLatch prefaceLatch = new CountDownLatch(1);
|
||||||
final CountDownLatch stream1Latch = new CountDownLatch(1);
|
CountDownLatch stream1Latch = new CountDownLatch(1);
|
||||||
final CountDownLatch stream2Latch = new CountDownLatch(1);
|
CountDownLatch stream2Latch = new CountDownLatch(1);
|
||||||
final CountDownLatch settingsLatch = new CountDownLatch(1);
|
CountDownLatch settingsLatch = new CountDownLatch(1);
|
||||||
start(new ServerSessionListener.Adapter()
|
start(new ServerSessionListener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -233,11 +233,11 @@ public abstract class FlowControlStrategyTest
|
||||||
// then we change the window to 512 B. At this point, the client
|
// then we change the window to 512 B. At this point, the client
|
||||||
// must stop sending data (although the initial window allows it).
|
// must stop sending data (although the initial window allows it).
|
||||||
|
|
||||||
final int size = 512;
|
int size = 512;
|
||||||
// We get 3 data frames: the first of 1024 and 2 of 512 each
|
// We get 3 data frames: the first of 1024 and 2 of 512 each
|
||||||
// after the flow control window has been reduced.
|
// after the flow control window has been reduced.
|
||||||
final CountDownLatch dataLatch = new CountDownLatch(3);
|
CountDownLatch dataLatch = new CountDownLatch(3);
|
||||||
final AtomicReference<Callback> callbackRef = new AtomicReference<>();
|
AtomicReference<Callback> callbackRef = new AtomicReference<>();
|
||||||
start(new ServerSessionListener.Adapter()
|
start(new ServerSessionListener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -275,7 +275,7 @@ public abstract class FlowControlStrategyTest
|
||||||
});
|
});
|
||||||
|
|
||||||
// Two SETTINGS frames, the initial one and the one we send from the server.
|
// Two SETTINGS frames, the initial one and the one we send from the server.
|
||||||
final CountDownLatch settingsLatch = new CountDownLatch(2);
|
CountDownLatch settingsLatch = new CountDownLatch(2);
|
||||||
Session session = newClient(new Session.Listener.Adapter()
|
Session session = newClient(new Session.Listener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -312,9 +312,9 @@ public abstract class FlowControlStrategyTest
|
||||||
@Test
|
@Test
|
||||||
public void testServerFlowControlOneBigWrite() throws Exception
|
public void testServerFlowControlOneBigWrite() throws Exception
|
||||||
{
|
{
|
||||||
final int windowSize = 1536;
|
int windowSize = 1536;
|
||||||
final int length = 5 * windowSize;
|
int length = 5 * windowSize;
|
||||||
final CountDownLatch settingsLatch = new CountDownLatch(2);
|
CountDownLatch settingsLatch = new CountDownLatch(2);
|
||||||
start(new ServerSessionListener.Adapter()
|
start(new ServerSessionListener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -349,13 +349,13 @@ public abstract class FlowControlStrategyTest
|
||||||
|
|
||||||
assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
|
assertTrue(settingsLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
|
||||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
CountDownLatch dataLatch = new CountDownLatch(1);
|
||||||
final Exchanger<Callback> exchanger = new Exchanger<>();
|
Exchanger<Callback> exchanger = new Exchanger<>();
|
||||||
MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
|
MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
|
||||||
HeadersFrame requestFrame = new HeadersFrame(metaData, null, true);
|
HeadersFrame requestFrame = new HeadersFrame(metaData, null, true);
|
||||||
session.newStream(requestFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
|
session.newStream(requestFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
|
||||||
{
|
{
|
||||||
private AtomicInteger dataFrames = new AtomicInteger();
|
private final AtomicInteger dataFrames = new AtomicInteger();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||||
|
@ -406,10 +406,10 @@ public abstract class FlowControlStrategyTest
|
||||||
@Test
|
@Test
|
||||||
public void testClientFlowControlOneBigWrite() throws Exception
|
public void testClientFlowControlOneBigWrite() throws Exception
|
||||||
{
|
{
|
||||||
final int windowSize = 1536;
|
int windowSize = 1536;
|
||||||
final Exchanger<Callback> exchanger = new Exchanger<>();
|
Exchanger<Callback> exchanger = new Exchanger<>();
|
||||||
final CountDownLatch settingsLatch = new CountDownLatch(1);
|
CountDownLatch settingsLatch = new CountDownLatch(1);
|
||||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
CountDownLatch dataLatch = new CountDownLatch(1);
|
||||||
start(new ServerSessionListener.Adapter()
|
start(new ServerSessionListener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -428,7 +428,7 @@ public abstract class FlowControlStrategyTest
|
||||||
stream.headers(responseFrame, Callback.NOOP);
|
stream.headers(responseFrame, Callback.NOOP);
|
||||||
return new Stream.Listener.Adapter()
|
return new Stream.Listener.Adapter()
|
||||||
{
|
{
|
||||||
private AtomicInteger dataFrames = new AtomicInteger();
|
private final AtomicInteger dataFrames = new AtomicInteger();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||||
|
@ -480,7 +480,7 @@ public abstract class FlowControlStrategyTest
|
||||||
session.newStream(requestFrame, streamPromise, null);
|
session.newStream(requestFrame, streamPromise, null);
|
||||||
Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
|
Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
|
||||||
|
|
||||||
final int length = 5 * windowSize;
|
int length = 5 * windowSize;
|
||||||
DataFrame dataFrame = new DataFrame(stream.getId(), ByteBuffer.allocate(length), true);
|
DataFrame dataFrame = new DataFrame(stream.getId(), ByteBuffer.allocate(length), true);
|
||||||
stream.data(dataFrame, Callback.NOOP);
|
stream.data(dataFrame, Callback.NOOP);
|
||||||
|
|
||||||
|
@ -499,7 +499,7 @@ public abstract class FlowControlStrategyTest
|
||||||
assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
|
assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkThatWeAreFlowControlStalled(Exchanger<Callback> exchanger) throws Exception
|
private void checkThatWeAreFlowControlStalled(Exchanger<Callback> exchanger)
|
||||||
{
|
{
|
||||||
assertThrows(TimeoutException.class,
|
assertThrows(TimeoutException.class,
|
||||||
() -> exchanger.exchange(null, 1, TimeUnit.SECONDS));
|
() -> exchanger.exchange(null, 1, TimeUnit.SECONDS));
|
||||||
|
@ -508,7 +508,7 @@ public abstract class FlowControlStrategyTest
|
||||||
@Test
|
@Test
|
||||||
public void testSessionStalledStallsNewStreams() throws Exception
|
public void testSessionStalledStallsNewStreams() throws Exception
|
||||||
{
|
{
|
||||||
final int windowSize = 1024;
|
int windowSize = 1024;
|
||||||
start(new ServerSessionListener.Adapter()
|
start(new ServerSessionListener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -543,8 +543,8 @@ public abstract class FlowControlStrategyTest
|
||||||
Session session = newClient(new Session.Listener.Adapter());
|
Session session = newClient(new Session.Listener.Adapter());
|
||||||
|
|
||||||
// First request is just to consume most of the session window.
|
// First request is just to consume most of the session window.
|
||||||
final List<Callback> callbacks1 = new ArrayList<>();
|
List<Callback> callbacks1 = new ArrayList<>();
|
||||||
final CountDownLatch prepareLatch = new CountDownLatch(1);
|
CountDownLatch prepareLatch = new CountDownLatch(1);
|
||||||
MetaData.Request request1 = newRequest("POST", HttpFields.EMPTY);
|
MetaData.Request request1 = newRequest("POST", HttpFields.EMPTY);
|
||||||
session.newStream(new HeadersFrame(request1, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
|
session.newStream(new HeadersFrame(request1, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
|
||||||
{
|
{
|
||||||
|
@ -583,7 +583,7 @@ public abstract class FlowControlStrategyTest
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fourth request is now stalled.
|
// Fourth request is now stalled.
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
MetaData.Request request4 = newRequest("GET", HttpFields.EMPTY);
|
MetaData.Request request4 = newRequest("GET", HttpFields.EMPTY);
|
||||||
session.newStream(new HeadersFrame(request4, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
|
session.newStream(new HeadersFrame(request4, null, true), new Promise.Adapter<>(), new Stream.Listener.Adapter()
|
||||||
{
|
{
|
||||||
|
@ -612,7 +612,7 @@ public abstract class FlowControlStrategyTest
|
||||||
@Test
|
@Test
|
||||||
public void testServerSendsBigContent() throws Exception
|
public void testServerSendsBigContent() throws Exception
|
||||||
{
|
{
|
||||||
final byte[] data = new byte[1024 * 1024];
|
byte[] data = new byte[1024 * 1024];
|
||||||
new Random().nextBytes(data);
|
new Random().nextBytes(data);
|
||||||
|
|
||||||
start(new ServerSessionListener.Adapter()
|
start(new ServerSessionListener.Adapter()
|
||||||
|
@ -636,8 +636,8 @@ public abstract class FlowControlStrategyTest
|
||||||
Session session = newClient(new Session.Listener.Adapter());
|
Session session = newClient(new Session.Listener.Adapter());
|
||||||
MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
|
MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
|
||||||
HeadersFrame requestFrame = new HeadersFrame(metaData, null, true);
|
HeadersFrame requestFrame = new HeadersFrame(metaData, null, true);
|
||||||
final byte[] bytes = new byte[data.length];
|
byte[] bytes = new byte[data.length];
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
session.newStream(requestFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
|
session.newStream(requestFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
|
||||||
{
|
{
|
||||||
private int received;
|
private int received;
|
||||||
|
@ -681,7 +681,7 @@ public abstract class FlowControlStrategyTest
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final int initialWindow = 16;
|
int initialWindow = 16;
|
||||||
Session session = newClient(new Session.Listener.Adapter()
|
Session session = newClient(new Session.Listener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -697,11 +697,11 @@ public abstract class FlowControlStrategyTest
|
||||||
new Random().nextBytes(requestData);
|
new Random().nextBytes(requestData);
|
||||||
|
|
||||||
byte[] responseData = new byte[requestData.length];
|
byte[] responseData = new byte[requestData.length];
|
||||||
final ByteBuffer responseContent = ByteBuffer.wrap(responseData);
|
ByteBuffer responseContent = ByteBuffer.wrap(responseData);
|
||||||
MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
|
MetaData.Request metaData = newRequest("GET", HttpFields.EMPTY);
|
||||||
HeadersFrame requestFrame = new HeadersFrame(metaData, null, false);
|
HeadersFrame requestFrame = new HeadersFrame(metaData, null, false);
|
||||||
Promise.Completable<Stream> completable = new Promise.Completable<>();
|
Promise.Completable<Stream> completable = new Promise.Completable<>();
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
session.newStream(requestFrame, completable, new Stream.Listener.Adapter()
|
session.newStream(requestFrame, completable, new Stream.Listener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -730,6 +730,7 @@ public abstract class FlowControlStrategyTest
|
||||||
public void testClientExceedingSessionWindow() throws Exception
|
public void testClientExceedingSessionWindow() throws Exception
|
||||||
{
|
{
|
||||||
// On server, we don't consume the data.
|
// On server, we don't consume the data.
|
||||||
|
CountDownLatch serverCloseLatch = new CountDownLatch(1);
|
||||||
start(new ServerSessionListener.Adapter()
|
start(new ServerSessionListener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -744,16 +745,29 @@ public abstract class FlowControlStrategyTest
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
final CountDownLatch closeLatch = new CountDownLatch(1);
|
|
||||||
Session session = newClient(new Session.Listener.Adapter()
|
|
||||||
{
|
|
||||||
@Override
|
@Override
|
||||||
public void onClose(Session session, GoAwayFrame frame)
|
public void onClose(Session session, GoAwayFrame frame)
|
||||||
|
{
|
||||||
|
serverCloseLatch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
CountDownLatch clientGoAwayLatch = new CountDownLatch(1);
|
||||||
|
CountDownLatch clientCloseLatch = new CountDownLatch(1);
|
||||||
|
Session session = newClient(new Session.Listener.Adapter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onGoAway(Session session, GoAwayFrame frame)
|
||||||
{
|
{
|
||||||
if (frame.getError() == ErrorCode.FLOW_CONTROL_ERROR.code)
|
if (frame.getError() == ErrorCode.FLOW_CONTROL_ERROR.code)
|
||||||
closeLatch.countDown();
|
clientGoAwayLatch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClose(Session session, GoAwayFrame frame)
|
||||||
|
{
|
||||||
|
clientCloseLatch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -764,7 +778,7 @@ public abstract class FlowControlStrategyTest
|
||||||
session.newStream(requestFrame, Promise.from(completable), new Stream.Listener.Adapter());
|
session.newStream(requestFrame, Promise.from(completable), new Stream.Listener.Adapter());
|
||||||
Stream stream = completable.get(5, TimeUnit.SECONDS);
|
Stream stream = completable.get(5, TimeUnit.SECONDS);
|
||||||
ByteBuffer data = ByteBuffer.allocate(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
|
ByteBuffer data = ByteBuffer.allocate(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
|
||||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
CountDownLatch dataLatch = new CountDownLatch(1);
|
||||||
stream.data(new DataFrame(stream.getId(), data, false), new Callback()
|
stream.data(new DataFrame(stream.getId(), data, false), new Callback()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -796,16 +810,19 @@ public abstract class FlowControlStrategyTest
|
||||||
ByteBuffer extraData = ByteBuffer.allocate(1024);
|
ByteBuffer extraData = ByteBuffer.allocate(1024);
|
||||||
http2Session.getGenerator().data(lease, new DataFrame(stream.getId(), extraData, true), extraData.remaining());
|
http2Session.getGenerator().data(lease, new DataFrame(stream.getId(), extraData, true), extraData.remaining());
|
||||||
List<ByteBuffer> buffers = lease.getByteBuffers();
|
List<ByteBuffer> buffers = lease.getByteBuffers();
|
||||||
http2Session.getEndPoint().write(Callback.NOOP, buffers.toArray(new ByteBuffer[buffers.size()]));
|
http2Session.getEndPoint().write(Callback.NOOP, buffers.toArray(new ByteBuffer[0]));
|
||||||
|
|
||||||
// Expect the connection to be closed.
|
// Expect the connection to be closed.
|
||||||
assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
|
assertTrue(clientGoAwayLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
assertTrue(clientCloseLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
assertTrue(serverCloseLatch.await(5, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClientExceedingStreamWindow() throws Exception
|
public void testClientExceedingStreamWindow() throws Exception
|
||||||
{
|
{
|
||||||
// On server, we don't consume the data.
|
// On server, we don't consume the data.
|
||||||
|
CountDownLatch serverCloseLatch = new CountDownLatch(1);
|
||||||
start(new ServerSessionListener.Adapter()
|
start(new ServerSessionListener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -828,16 +845,29 @@ public abstract class FlowControlStrategyTest
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
final CountDownLatch closeLatch = new CountDownLatch(1);
|
|
||||||
Session session = newClient(new Session.Listener.Adapter()
|
|
||||||
{
|
|
||||||
@Override
|
@Override
|
||||||
public void onClose(Session session, GoAwayFrame frame)
|
public void onClose(Session session, GoAwayFrame frame)
|
||||||
|
{
|
||||||
|
serverCloseLatch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
CountDownLatch clientGoAwayLatch = new CountDownLatch(1);
|
||||||
|
CountDownLatch clientCloseLatch = new CountDownLatch(1);
|
||||||
|
Session session = newClient(new Session.Listener.Adapter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onGoAway(Session session, GoAwayFrame frame)
|
||||||
{
|
{
|
||||||
if (frame.getError() == ErrorCode.FLOW_CONTROL_ERROR.code)
|
if (frame.getError() == ErrorCode.FLOW_CONTROL_ERROR.code)
|
||||||
closeLatch.countDown();
|
clientGoAwayLatch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClose(Session session, GoAwayFrame frame)
|
||||||
|
{
|
||||||
|
clientCloseLatch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -848,7 +878,7 @@ public abstract class FlowControlStrategyTest
|
||||||
session.newStream(requestFrame, streamPromise, new Stream.Listener.Adapter());
|
session.newStream(requestFrame, streamPromise, new Stream.Listener.Adapter());
|
||||||
Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
|
Stream stream = streamPromise.get(5, TimeUnit.SECONDS);
|
||||||
ByteBuffer data = ByteBuffer.allocate(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
|
ByteBuffer data = ByteBuffer.allocate(FlowControlStrategy.DEFAULT_WINDOW_SIZE);
|
||||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
CountDownLatch dataLatch = new CountDownLatch(1);
|
||||||
stream.data(new DataFrame(stream.getId(), data, false), new Callback()
|
stream.data(new DataFrame(stream.getId(), data, false), new Callback()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -876,10 +906,12 @@ public abstract class FlowControlStrategyTest
|
||||||
ByteBuffer extraData = ByteBuffer.allocate(1024);
|
ByteBuffer extraData = ByteBuffer.allocate(1024);
|
||||||
http2Session.getGenerator().data(lease, new DataFrame(stream.getId(), extraData, true), extraData.remaining());
|
http2Session.getGenerator().data(lease, new DataFrame(stream.getId(), extraData, true), extraData.remaining());
|
||||||
List<ByteBuffer> buffers = lease.getByteBuffers();
|
List<ByteBuffer> buffers = lease.getByteBuffers();
|
||||||
http2Session.getEndPoint().write(Callback.NOOP, buffers.toArray(new ByteBuffer[buffers.size()]));
|
http2Session.getEndPoint().write(Callback.NOOP, buffers.toArray(new ByteBuffer[0]));
|
||||||
|
|
||||||
// Expect the connection to be closed.
|
// Expect the connection to be closed.
|
||||||
assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
|
assertTrue(clientGoAwayLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
assertTrue(clientCloseLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
assertTrue(serverCloseLatch.await(5, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -916,7 +948,7 @@ public abstract class FlowControlStrategyTest
|
||||||
MetaData.Request metaData = newRequest("POST", HttpFields.EMPTY);
|
MetaData.Request metaData = newRequest("POST", HttpFields.EMPTY);
|
||||||
HeadersFrame frame = new HeadersFrame(metaData, null, false);
|
HeadersFrame frame = new HeadersFrame(metaData, null, false);
|
||||||
FuturePromise<Stream> streamPromise = new FuturePromise<>();
|
FuturePromise<Stream> streamPromise = new FuturePromise<>();
|
||||||
final CountDownLatch resetLatch = new CountDownLatch(1);
|
CountDownLatch resetLatch = new CountDownLatch(1);
|
||||||
session.newStream(frame, streamPromise, new Stream.Listener.Adapter()
|
session.newStream(frame, streamPromise, new Stream.Listener.Adapter()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -929,7 +961,7 @@ public abstract class FlowControlStrategyTest
|
||||||
|
|
||||||
// Perform a big upload that will stall the flow control windows.
|
// Perform a big upload that will stall the flow control windows.
|
||||||
ByteBuffer data = ByteBuffer.allocate(5 * FlowControlStrategy.DEFAULT_WINDOW_SIZE);
|
ByteBuffer data = ByteBuffer.allocate(5 * FlowControlStrategy.DEFAULT_WINDOW_SIZE);
|
||||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
CountDownLatch dataLatch = new CountDownLatch(1);
|
||||||
stream.data(new DataFrame(stream.getId(), data, true), new Callback()
|
stream.data(new DataFrame(stream.getId(), data, true), new Callback()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -930,9 +930,20 @@ public class HTTP2Test extends AbstractTest
|
||||||
// Avoid aggressive idle timeout to allow the test verifications.
|
// Avoid aggressive idle timeout to allow the test verifications.
|
||||||
connector.setShutdownIdleTimeout(connector.getIdleTimeout());
|
connector.setShutdownIdleTimeout(connector.getIdleTimeout());
|
||||||
|
|
||||||
|
CountDownLatch clientGracefulGoAwayLatch = new CountDownLatch(1);
|
||||||
|
CountDownLatch clientGoAwayLatch = new CountDownLatch(1);
|
||||||
CountDownLatch clientCloseLatch = new CountDownLatch(1);
|
CountDownLatch clientCloseLatch = new CountDownLatch(1);
|
||||||
Session clientSession = newClient(new Session.Listener.Adapter()
|
Session clientSession = newClient(new Session.Listener.Adapter()
|
||||||
{
|
{
|
||||||
|
@Override
|
||||||
|
public void onGoAway(Session session, GoAwayFrame frame)
|
||||||
|
{
|
||||||
|
if (frame.isGraceful())
|
||||||
|
clientGracefulGoAwayLatch.countDown();
|
||||||
|
else
|
||||||
|
clientGoAwayLatch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClose(Session session, GoAwayFrame frame)
|
public void onClose(Session session, GoAwayFrame frame)
|
||||||
{
|
{
|
||||||
|
@ -977,26 +988,20 @@ public class HTTP2Test extends AbstractTest
|
||||||
int port = connector.getLocalPort();
|
int port = connector.getLocalPort();
|
||||||
CompletableFuture<Void> shutdown = Graceful.shutdown(server);
|
CompletableFuture<Void> shutdown = Graceful.shutdown(server);
|
||||||
|
|
||||||
// GOAWAY should not arrive to the client yet.
|
// Client should receive the graceful GOAWAY.
|
||||||
assertFalse(clientCloseLatch.await(1, TimeUnit.SECONDS));
|
assertTrue(clientGracefulGoAwayLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
// Client should not receive the non-graceful GOAWAY.
|
||||||
|
assertFalse(clientGoAwayLatch.await(500, TimeUnit.MILLISECONDS));
|
||||||
|
// Client should not be closed yet.
|
||||||
|
assertFalse(clientCloseLatch.await(500, TimeUnit.MILLISECONDS));
|
||||||
|
|
||||||
// New requests should be immediately rejected.
|
// Client cannot create new requests after receiving a GOAWAY.
|
||||||
HostPortHttpField authority3 = new HostPortHttpField("localhost" + ":" + port);
|
HostPortHttpField authority3 = new HostPortHttpField("localhost" + ":" + port);
|
||||||
MetaData.Request metaData3 = new MetaData.Request("GET", HttpScheme.HTTP.asString(), authority3, servletPath, HttpVersion.HTTP_2, HttpFields.EMPTY, -1);
|
MetaData.Request metaData3 = new MetaData.Request("GET", HttpScheme.HTTP.asString(), authority3, servletPath, HttpVersion.HTTP_2, HttpFields.EMPTY, -1);
|
||||||
HeadersFrame request3 = new HeadersFrame(metaData3, null, false);
|
HeadersFrame request3 = new HeadersFrame(metaData3, null, true);
|
||||||
FuturePromise<Stream> promise3 = new FuturePromise<>();
|
FuturePromise<Stream> promise3 = new FuturePromise<>();
|
||||||
CountDownLatch resetLatch = new CountDownLatch(1);
|
clientSession.newStream(request3, promise3, new Stream.Listener.Adapter());
|
||||||
clientSession.newStream(request3, promise3, new Stream.Listener.Adapter()
|
assertThrows(ExecutionException.class, () -> promise3.get(5, TimeUnit.SECONDS));
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onReset(Stream stream, ResetFrame frame)
|
|
||||||
{
|
|
||||||
resetLatch.countDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Stream stream3 = promise3.get(5, TimeUnit.SECONDS);
|
|
||||||
stream3.data(new DataFrame(stream3.getId(), ByteBuffer.allocate(1), true), Callback.NOOP);
|
|
||||||
assertTrue(resetLatch.await(5, TimeUnit.SECONDS));
|
|
||||||
|
|
||||||
// Finish the previous requests and expect the responses.
|
// Finish the previous requests and expect the responses.
|
||||||
stream1.data(new DataFrame(stream1.getId(), BufferUtil.EMPTY_BUFFER, true), Callback.NOOP);
|
stream1.data(new DataFrame(stream1.getId(), BufferUtil.EMPTY_BUFFER, true), Callback.NOOP);
|
||||||
|
@ -1005,9 +1010,9 @@ public class HTTP2Test extends AbstractTest
|
||||||
assertNull(shutdown.get(5, TimeUnit.SECONDS));
|
assertNull(shutdown.get(5, TimeUnit.SECONDS));
|
||||||
|
|
||||||
// Now GOAWAY should arrive to the client.
|
// Now GOAWAY should arrive to the client.
|
||||||
|
assertTrue(clientGoAwayLatch.await(5, TimeUnit.SECONDS));
|
||||||
assertTrue(clientCloseLatch.await(5, TimeUnit.SECONDS));
|
assertTrue(clientCloseLatch.await(5, TimeUnit.SECONDS));
|
||||||
// Wait to process the GOAWAY frames and close the EndPoints.
|
|
||||||
Thread.sleep(1000);
|
|
||||||
assertFalse(((HTTP2Session)clientSession).getEndPoint().isOpen());
|
assertFalse(((HTTP2Session)clientSession).getEndPoint().isOpen());
|
||||||
assertFalse(((HTTP2Session)serverSession).getEndPoint().isOpen());
|
assertFalse(((HTTP2Session)serverSession).getEndPoint().isOpen());
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,8 +84,8 @@ public class SessionFailureTest extends AbstractTest
|
||||||
@Override
|
@Override
|
||||||
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
|
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
|
||||||
{
|
{
|
||||||
// Forcibly close the connection.
|
// Forcibly shutdown the output to fail the write below.
|
||||||
((HTTP2Session)stream.getSession()).getEndPoint().close();
|
((HTTP2Session)stream.getSession()).getEndPoint().shutdownOutput();
|
||||||
// Now try to write something: it should fail.
|
// Now try to write something: it should fail.
|
||||||
stream.headers(frame, new Callback()
|
stream.headers(frame, new Callback()
|
||||||
{
|
{
|
||||||
|
|
|
@ -321,7 +321,9 @@ public class StreamCloseTest extends AbstractTest
|
||||||
MetaData.Request request = (MetaData.Request)frame.getMetaData();
|
MetaData.Request request = (MetaData.Request)frame.getMetaData();
|
||||||
if ("GET".equals(request.getMethod()))
|
if ("GET".equals(request.getMethod()))
|
||||||
{
|
{
|
||||||
((HTTP2Session)stream.getSession()).getEndPoint().close();
|
// Only shutdown the output, since closing the EndPoint causes a call to
|
||||||
|
// stop() on different thread which tries to concurrently fail the stream.
|
||||||
|
((HTTP2Session)stream.getSession()).getEndPoint().shutdownOutput();
|
||||||
// Try to write something to force an error.
|
// Try to write something to force an error.
|
||||||
stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(1024), true), Callback.NOOP);
|
stream.data(new DataFrame(stream.getId(), ByteBuffer.allocate(1024), true), Callback.NOOP);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,319 +20,310 @@ package org.eclipse.jetty.http2;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.ArrayTrie;
|
import org.eclipse.jetty.util.Index;
|
||||||
import org.eclipse.jetty.util.Trie;
|
|
||||||
|
|
||||||
public class HTTP2Cipher
|
public class HTTP2Cipher
|
||||||
{
|
{
|
||||||
public static final Comparator<String> COMPARATOR = new CipherComparator();
|
public static final Comparator<String> COMPARATOR = new CipherComparator();
|
||||||
|
|
||||||
private static final Trie<Boolean> __blackProtocols = new ArrayTrie<>(6 * 5);
|
private static final Index<Boolean> __blackProtocols = new Index.Builder<Boolean>()
|
||||||
private static final Trie<Boolean> __blackCiphers = new ArrayTrie<>(275 * 40);
|
.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
|
private static final Index<Boolean> __blackCiphers = new Index.Builder<Boolean>()
|
||||||
{
|
.caseSensitive(false)
|
||||||
String[] protocols = {"TLSv1.2", "TLSv1.1", "TLSv1", "SSL", "SSLv2", "SSLv3"};
|
.with("TLS_NULL_WITH_NULL_NULL", Boolean.TRUE)
|
||||||
for (String p : protocols)
|
.with("TLS_RSA_WITH_NULL_MD5", Boolean.TRUE)
|
||||||
{
|
.with("TLS_RSA_WITH_NULL_SHA", Boolean.TRUE)
|
||||||
__blackProtocols.put(p, 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)
|
||||||
String[] ciphers =
|
.with("TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5", Boolean.TRUE)
|
||||||
{
|
.with("TLS_RSA_WITH_IDEA_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_NULL_WITH_NULL_NULL",
|
.with("TLS_RSA_EXPORT_WITH_DES40_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_NULL_MD5",
|
.with("TLS_RSA_WITH_DES_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_NULL_SHA",
|
.with("TLS_RSA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_EXPORT_WITH_RC4_40_MD5",
|
.with("TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_RC4_128_MD5",
|
.with("TLS_DH_DSS_WITH_DES_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_RC4_128_SHA",
|
.with("TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
|
.with("TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_IDEA_CBC_SHA",
|
.with("TLS_DH_RSA_WITH_DES_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_EXPORT_WITH_DES40_CBC_SHA",
|
.with("TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_DES_CBC_SHA",
|
.with("TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_DHE_DSS_WITH_DES_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA",
|
.with("TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_DES_CBC_SHA",
|
.with("TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_DHE_RSA_WITH_DES_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA",
|
.with("TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_DES_CBC_SHA",
|
.with("TLS_DH_anon_EXPORT_WITH_RC4_40_MD5", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_DH_anon_WITH_RC4_128_MD5", Boolean.TRUE)
|
||||||
"TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
|
.with("TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_DSS_WITH_DES_CBC_SHA",
|
.with("TLS_DH_anon_WITH_DES_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_DH_anon_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
|
.with("TLS_KRB5_WITH_DES_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_RSA_WITH_DES_CBC_SHA",
|
.with("TLS_KRB5_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_KRB5_WITH_RC4_128_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_anon_EXPORT_WITH_RC4_40_MD5",
|
.with("TLS_KRB5_WITH_IDEA_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_RC4_128_MD5",
|
.with("TLS_KRB5_WITH_DES_CBC_MD5", Boolean.TRUE)
|
||||||
"TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
|
.with("TLS_KRB5_WITH_3DES_EDE_CBC_MD5", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_DES_CBC_SHA",
|
.with("TLS_KRB5_WITH_RC4_128_MD5", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_KRB5_WITH_IDEA_CBC_MD5", Boolean.TRUE)
|
||||||
"TLS_KRB5_WITH_DES_CBC_SHA",
|
.with("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", Boolean.TRUE)
|
||||||
"TLS_KRB5_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA", Boolean.TRUE)
|
||||||
"TLS_KRB5_WITH_RC4_128_SHA",
|
.with("TLS_KRB5_EXPORT_WITH_RC4_40_SHA", Boolean.TRUE)
|
||||||
"TLS_KRB5_WITH_IDEA_CBC_SHA",
|
.with("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", Boolean.TRUE)
|
||||||
"TLS_KRB5_WITH_DES_CBC_MD5",
|
.with("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5", Boolean.TRUE)
|
||||||
"TLS_KRB5_WITH_3DES_EDE_CBC_MD5",
|
.with("TLS_KRB5_EXPORT_WITH_RC4_40_MD5", Boolean.TRUE)
|
||||||
"TLS_KRB5_WITH_RC4_128_MD5",
|
.with("TLS_PSK_WITH_NULL_SHA", Boolean.TRUE)
|
||||||
"TLS_KRB5_WITH_IDEA_CBC_MD5",
|
.with("TLS_DHE_PSK_WITH_NULL_SHA", Boolean.TRUE)
|
||||||
"TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA",
|
.with("TLS_RSA_PSK_WITH_NULL_SHA", Boolean.TRUE)
|
||||||
"TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA",
|
.with("TLS_RSA_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
|
.with("TLS_DH_DSS_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5",
|
.with("TLS_DH_RSA_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5",
|
.with("TLS_DHE_DSS_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_KRB5_EXPORT_WITH_RC4_40_MD5",
|
.with("TLS_DHE_RSA_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_NULL_SHA",
|
.with("TLS_DH_anon_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_PSK_WITH_NULL_SHA",
|
.with("TLS_RSA_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_NULL_SHA",
|
.with("TLS_DH_DSS_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_AES_128_CBC_SHA",
|
.with("TLS_DH_RSA_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_AES_128_CBC_SHA",
|
.with("TLS_DHE_DSS_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_AES_128_CBC_SHA",
|
.with("TLS_DHE_RSA_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
|
.with("TLS_DH_anon_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
|
.with("TLS_RSA_WITH_NULL_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_AES_128_CBC_SHA",
|
.with("TLS_RSA_WITH_AES_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_AES_256_CBC_SHA",
|
.with("TLS_RSA_WITH_AES_256_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_AES_256_CBC_SHA",
|
.with("TLS_DH_DSS_WITH_AES_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_AES_256_CBC_SHA",
|
.with("TLS_DH_RSA_WITH_AES_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
|
.with("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
|
.with("TLS_RSA_WITH_CAMELLIA_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_AES_256_CBC_SHA",
|
.with("TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_NULL_SHA256",
|
.with("TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_AES_128_CBC_SHA256",
|
.with("TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_AES_256_CBC_SHA256",
|
.with("TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_AES_128_CBC_SHA256",
|
.with("TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_AES_128_CBC_SHA256",
|
.with("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
|
.with("TLS_DH_DSS_WITH_AES_256_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA",
|
.with("TLS_DH_RSA_WITH_AES_256_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA",
|
.with("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA",
|
.with("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA",
|
.with("TLS_DH_anon_WITH_AES_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA",
|
.with("TLS_DH_anon_WITH_AES_256_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA",
|
.with("TLS_RSA_WITH_CAMELLIA_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
|
.with("TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_AES_256_CBC_SHA256",
|
.with("TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_AES_256_CBC_SHA256",
|
.with("TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
|
.with("TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
|
.with("TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_AES_128_CBC_SHA256",
|
.with("TLS_PSK_WITH_RC4_128_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_AES_256_CBC_SHA256",
|
.with("TLS_PSK_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA",
|
.with("TLS_PSK_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA",
|
.with("TLS_PSK_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA",
|
.with("TLS_DHE_PSK_WITH_RC4_128_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA",
|
.with("TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA",
|
.with("TLS_DHE_PSK_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA",
|
.with("TLS_DHE_PSK_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_RC4_128_SHA",
|
.with("TLS_RSA_PSK_WITH_RC4_128_SHA", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_AES_128_CBC_SHA",
|
.with("TLS_RSA_PSK_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_AES_256_CBC_SHA",
|
.with("TLS_RSA_PSK_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_PSK_WITH_RC4_128_SHA",
|
.with("TLS_RSA_WITH_SEED_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_DH_DSS_WITH_SEED_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_PSK_WITH_AES_128_CBC_SHA",
|
.with("TLS_DH_RSA_WITH_SEED_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_PSK_WITH_AES_256_CBC_SHA",
|
.with("TLS_DHE_DSS_WITH_SEED_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_RC4_128_SHA",
|
.with("TLS_DHE_RSA_WITH_SEED_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_DH_anon_WITH_SEED_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_AES_128_CBC_SHA",
|
.with("TLS_RSA_WITH_AES_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_AES_256_CBC_SHA",
|
.with("TLS_RSA_WITH_AES_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_SEED_CBC_SHA",
|
.with("TLS_DH_RSA_WITH_AES_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_SEED_CBC_SHA",
|
.with("TLS_DH_RSA_WITH_AES_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_SEED_CBC_SHA",
|
.with("TLS_DH_DSS_WITH_AES_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_DHE_DSS_WITH_SEED_CBC_SHA",
|
.with("TLS_DH_DSS_WITH_AES_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_DHE_RSA_WITH_SEED_CBC_SHA",
|
.with("TLS_DH_anon_WITH_AES_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_SEED_CBC_SHA",
|
.with("TLS_DH_anon_WITH_AES_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
.with("TLS_PSK_WITH_AES_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_AES_256_GCM_SHA384",
|
.with("TLS_PSK_WITH_AES_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_AES_128_GCM_SHA256",
|
.with("TLS_RSA_PSK_WITH_AES_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_AES_256_GCM_SHA384",
|
.with("TLS_RSA_PSK_WITH_AES_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_AES_128_GCM_SHA256",
|
.with("TLS_PSK_WITH_AES_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_AES_256_GCM_SHA384",
|
.with("TLS_PSK_WITH_AES_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_AES_128_GCM_SHA256",
|
.with("TLS_PSK_WITH_NULL_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_AES_256_GCM_SHA384",
|
.with("TLS_PSK_WITH_NULL_SHA384", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_AES_128_GCM_SHA256",
|
.with("TLS_DHE_PSK_WITH_AES_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_AES_256_GCM_SHA384",
|
.with("TLS_DHE_PSK_WITH_AES_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_AES_128_GCM_SHA256",
|
.with("TLS_DHE_PSK_WITH_NULL_SHA256", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_AES_256_GCM_SHA384",
|
.with("TLS_DHE_PSK_WITH_NULL_SHA384", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_AES_128_CBC_SHA256",
|
.with("TLS_RSA_PSK_WITH_AES_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_AES_256_CBC_SHA384",
|
.with("TLS_RSA_PSK_WITH_AES_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_NULL_SHA256",
|
.with("TLS_RSA_PSK_WITH_NULL_SHA256", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_NULL_SHA384",
|
.with("TLS_RSA_PSK_WITH_NULL_SHA384", Boolean.TRUE)
|
||||||
"TLS_DHE_PSK_WITH_AES_128_CBC_SHA256",
|
.with("TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DHE_PSK_WITH_AES_256_CBC_SHA384",
|
.with("TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DHE_PSK_WITH_NULL_SHA256",
|
.with("TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DHE_PSK_WITH_NULL_SHA384",
|
.with("TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_AES_128_CBC_SHA256",
|
.with("TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_AES_256_CBC_SHA384",
|
.with("TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_NULL_SHA256",
|
.with("TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_NULL_SHA384",
|
.with("TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
.with("TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256",
|
.with("TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
.with("TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256",
|
.with("TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
.with("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256",
|
.with("TLS_ECDH_ECDSA_WITH_NULL_SHA", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256",
|
.with("TLS_ECDH_ECDSA_WITH_RC4_128_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256",
|
.with("TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256",
|
.with("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256",
|
.with("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",
|
.with("TLS_ECDHE_ECDSA_WITH_NULL_SHA", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256",
|
.with("TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", Boolean.TRUE)
|
||||||
"TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
|
.with("TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_NULL_SHA",
|
.with("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
|
.with("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_ECDH_RSA_WITH_NULL_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
|
.with("TLS_ECDH_RSA_WITH_RC4_128_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
|
.with("TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDHE_ECDSA_WITH_NULL_SHA",
|
.with("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
|
.with("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_ECDHE_RSA_WITH_NULL_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
.with("TLS_ECDHE_RSA_WITH_RC4_128_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
.with("TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_NULL_SHA",
|
.with("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_RC4_128_SHA",
|
.with("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_ECDH_anon_WITH_NULL_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
|
.with("TLS_ECDH_anon_WITH_RC4_128_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",
|
.with("TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDHE_RSA_WITH_NULL_SHA",
|
.with("TLS_ECDH_anon_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
|
.with("TLS_ECDH_anon_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
.with("TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
.with("TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_anon_WITH_NULL_SHA",
|
.with("TLS_SRP_SHA_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_anon_WITH_RC4_128_SHA",
|
.with("TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
|
.with("TLS_SRP_SHA_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
|
.with("TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_SRP_SHA_WITH_AES_128_CBC_SHA",
|
.with("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA",
|
.with("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA",
|
.with("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_SRP_SHA_WITH_AES_256_CBC_SHA",
|
.with("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA",
|
.with("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA",
|
.with("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
.with("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
|
.with("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
|
.with("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
|
.with("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
.with("TLS_ECDHE_PSK_WITH_RC4_128_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
|
.with("TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",
|
.with("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",
|
.with("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
|
.with("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",
|
.with("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",
|
.with("TLS_ECDHE_PSK_WITH_NULL_SHA", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384",
|
.with("TLS_ECDHE_PSK_WITH_NULL_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDHE_PSK_WITH_RC4_128_SHA",
|
.with("TLS_ECDHE_PSK_WITH_NULL_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA",
|
.with("TLS_RSA_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
|
.with("TLS_RSA_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
|
.with("TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256",
|
.with("TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384",
|
.with("TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDHE_PSK_WITH_NULL_SHA",
|
.with("TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDHE_PSK_WITH_NULL_SHA256",
|
.with("TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDHE_PSK_WITH_NULL_SHA384",
|
.with("TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_ARIA_128_CBC_SHA256",
|
.with("TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_ARIA_256_CBC_SHA384",
|
.with("TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256",
|
.with("TLS_DH_anon_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384",
|
.with("TLS_DH_anon_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256",
|
.with("TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384",
|
.with("TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256",
|
.with("TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384",
|
.with("TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256",
|
.with("TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384",
|
.with("TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_ARIA_128_CBC_SHA256",
|
.with("TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_ARIA_256_CBC_SHA384",
|
.with("TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256",
|
.with("TLS_RSA_WITH_ARIA_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384",
|
.with("TLS_RSA_WITH_ARIA_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256",
|
.with("TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384",
|
.with("TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256",
|
.with("TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384",
|
.with("TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256",
|
.with("TLS_DH_anon_WITH_ARIA_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384",
|
.with("TLS_DH_anon_WITH_ARIA_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_ARIA_128_GCM_SHA256",
|
.with("TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_ARIA_256_GCM_SHA384",
|
.with("TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256",
|
.with("TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384",
|
.with("TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256",
|
.with("TLS_PSK_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384",
|
.with("TLS_PSK_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_ARIA_128_GCM_SHA256",
|
.with("TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_ARIA_256_GCM_SHA384",
|
.with("TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256",
|
.with("TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384",
|
.with("TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256",
|
.with("TLS_PSK_WITH_ARIA_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384",
|
.with("TLS_PSK_WITH_ARIA_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_ARIA_128_CBC_SHA256",
|
.with("TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_ARIA_256_CBC_SHA384",
|
.with("TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256",
|
.with("TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384",
|
.with("TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256",
|
.with("TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384",
|
.with("TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_ARIA_128_GCM_SHA256",
|
.with("TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_ARIA_256_GCM_SHA384",
|
.with("TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256",
|
.with("TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384",
|
.with("TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256",
|
.with("TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384",
|
.with("TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256",
|
.with("TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384",
|
.with("TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256",
|
.with("TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384",
|
.with("TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
.with("TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384",
|
.with("TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
.with("TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384",
|
.with("TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256",
|
.with("TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384",
|
.with("TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256",
|
.with("TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384",
|
.with("TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256",
|
.with("TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384",
|
.with("TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256",
|
.with("TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256", Boolean.TRUE)
|
||||||
"TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384",
|
.with("TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256",
|
.with("TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384",
|
.with("TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256",
|
.with("TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384",
|
.with("TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256",
|
.with("TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384",
|
.with("TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256",
|
.with("TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384",
|
.with("TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256",
|
.with("TLS_RSA_WITH_AES_128_CCM", Boolean.TRUE)
|
||||||
"TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384",
|
.with("TLS_RSA_WITH_AES_256_CCM", Boolean.TRUE)
|
||||||
"TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256",
|
.with("TLS_RSA_WITH_AES_128_CCM_8", Boolean.TRUE)
|
||||||
"TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384",
|
.with("TLS_RSA_WITH_AES_256_CCM_8", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256",
|
.with("TLS_PSK_WITH_AES_128_CCM", Boolean.TRUE)
|
||||||
"TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384",
|
.with("TLS_PSK_WITH_AES_256_CCM", Boolean.TRUE)
|
||||||
"TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256",
|
.with("TLS_PSK_WITH_AES_128_CCM_8", Boolean.TRUE)
|
||||||
"TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384",
|
.with("TLS_PSK_WITH_AES_256_CCM_8", Boolean.TRUE)
|
||||||
"TLS_RSA_WITH_AES_128_CCM",
|
.build();
|
||||||
"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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isBlackListProtocol(String tlsProtocol)
|
public static boolean isBlackListProtocol(String tlsProtocol)
|
||||||
{
|
{
|
||||||
Boolean b = __blackProtocols.get(tlsProtocol);
|
return __blackProtocols.get(tlsProtocol) != null;
|
||||||
return b != null && b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isBlackListCipher(String tlsCipher)
|
public static boolean isBlackListCipher(String tlsCipher)
|
||||||
{
|
{
|
||||||
Boolean b = __blackCiphers.get(tlsCipher);
|
return __blackCiphers.get(tlsCipher) != null;
|
||||||
return b != null && b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -267,7 +267,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher.
|
||||||
{
|
{
|
||||||
Runnable task = pollTask();
|
Runnable task = pollTask();
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("Dequeued task {}", task);
|
LOG.debug("Dequeued task {}", String.valueOf(task));
|
||||||
if (task != null)
|
if (task != null)
|
||||||
return task;
|
return task;
|
||||||
|
|
||||||
|
|
|
@ -367,7 +367,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
|
||||||
// If the failure came from within the
|
// If the failure came from within the
|
||||||
// flusher, we need to close the connection.
|
// flusher, we need to close the connection.
|
||||||
if (closed == null)
|
if (closed == null)
|
||||||
session.abort(x);
|
session.onWriteFailure(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
void terminate(Throwable cause)
|
void terminate(Throwable cause)
|
||||||
|
@ -378,7 +378,7 @@ public class HTTP2Flusher extends IteratingCallback implements Dumpable
|
||||||
closed = terminated;
|
closed = terminated;
|
||||||
terminated = cause;
|
terminated = cause;
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("{}", closed != null ? "Terminated" : "Terminating");
|
LOG.debug("{} {}", closed != null ? "Terminated" : "Terminating", this);
|
||||||
}
|
}
|
||||||
if (closed == null)
|
if (closed == null)
|
||||||
iterate();
|
iterate();
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -158,7 +158,7 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
||||||
localReset = true;
|
localReset = true;
|
||||||
failure = new EOFException("reset");
|
failure = new EOFException("reset");
|
||||||
}
|
}
|
||||||
session.frames(this, List.of(frame), callback);
|
((HTTP2Session)session).reset(this, frame, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean startWrite(Callback callback)
|
private boolean startWrite(Callback callback)
|
||||||
|
@ -367,24 +367,11 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
||||||
length = fields.getLongField(HttpHeader.CONTENT_LENGTH);
|
length = fields.getLongField(HttpHeader.CONTENT_LENGTH);
|
||||||
dataLength = length >= 0 ? length : Long.MIN_VALUE;
|
dataLength = length >= 0 ? length : Long.MIN_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateClose(frame.isEndStream(), CloseState.Event.RECEIVED))
|
|
||||||
session.removeStream(this);
|
|
||||||
|
|
||||||
callback.succeeded();
|
callback.succeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onData(DataFrame frame, Callback callback)
|
private void onData(DataFrame frame, Callback callback)
|
||||||
{
|
{
|
||||||
if (getRecvWindow() < 0)
|
|
||||||
{
|
|
||||||
// It's a bad client, it does not deserve to be
|
|
||||||
// treated gently by just resetting the stream.
|
|
||||||
((HTTP2Session)session).onConnectionFailure(ErrorCode.FLOW_CONTROL_ERROR.code, "stream_window_exceeded");
|
|
||||||
callback.failed(new IOException("stream_window_exceeded"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SPEC: remotely closed streams must be replied with a reset.
|
// SPEC: remotely closed streams must be replied with a reset.
|
||||||
if (isRemotelyClosed())
|
if (isRemotelyClosed())
|
||||||
{
|
{
|
||||||
|
@ -483,9 +470,10 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
||||||
dataEntry = dataQueue.poll();
|
dataEntry = dataQueue.poll();
|
||||||
}
|
}
|
||||||
DataFrame frame = dataEntry.frame;
|
DataFrame frame = dataEntry.frame;
|
||||||
if (updateClose(frame.isEndStream(), CloseState.Event.RECEIVED))
|
boolean closed = updateClose(frame.isEndStream(), CloseState.Event.RECEIVED);
|
||||||
session.removeStream(this);
|
|
||||||
notifyDataDemanded(this, frame, dataEntry.callback);
|
notifyDataDemanded(this, frame, dataEntry.callback);
|
||||||
|
if (closed)
|
||||||
|
session.removeStream(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -505,8 +493,8 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
||||||
failure = new EofException("reset");
|
failure = new EofException("reset");
|
||||||
}
|
}
|
||||||
close();
|
close();
|
||||||
session.removeStream(this);
|
if (session.removeStream(this))
|
||||||
notifyReset(this, frame, callback);
|
notifyReset(this, frame, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPush(PushPromiseFrame frame, Callback callback)
|
private void onPush(PushPromiseFrame frame, Callback callback)
|
||||||
|
@ -529,8 +517,8 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
||||||
failure = frame.getFailure();
|
failure = frame.getFailure();
|
||||||
}
|
}
|
||||||
close();
|
close();
|
||||||
session.removeStream(this);
|
if (session.removeStream(this))
|
||||||
notifyFailure(this, frame, callback);
|
notifyFailure(this, frame, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -45,8 +45,9 @@ public interface ISession extends Session
|
||||||
* <p>Removes the given {@code stream}.</p>
|
* <p>Removes the given {@code stream}.</p>
|
||||||
*
|
*
|
||||||
* @param stream the stream to remove
|
* @param stream the stream to remove
|
||||||
|
* @return whether the stream was removed
|
||||||
*/
|
*/
|
||||||
public void removeStream(IStream stream);
|
public boolean removeStream(IStream stream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Sends the given list of frames to create a new {@link Stream}.</p>
|
* <p>Sends the given list of frames to create a new {@link Stream}.</p>
|
||||||
|
|
|
@ -113,8 +113,6 @@ public interface Session
|
||||||
/**
|
/**
|
||||||
* <p>Closes the session by sending a GOAWAY frame with the given error code
|
* <p>Closes the session by sending a GOAWAY frame with the given error code
|
||||||
* and payload.</p>
|
* and payload.</p>
|
||||||
* <p>The GOAWAY frame is sent only once; subsequent or concurrent attempts to
|
|
||||||
* close the session will have no effect.</p>
|
|
||||||
*
|
*
|
||||||
* @param error the error code
|
* @param error the error code
|
||||||
* @param payload an optional payload (may be null)
|
* @param payload an optional payload (may be null)
|
||||||
|
@ -225,6 +223,16 @@ public interface Session
|
||||||
*
|
*
|
||||||
* @param session the session
|
* @param session the session
|
||||||
* @param frame the GOAWAY frame received
|
* @param frame the GOAWAY frame received
|
||||||
|
*/
|
||||||
|
default void onGoAway(Session session, GoAwayFrame frame)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Callback method invoked when a GOAWAY frame caused the session to be closed.</p>
|
||||||
|
*
|
||||||
|
* @param session the session
|
||||||
|
* @param frame the GOAWAY frame that caused the session to be closed
|
||||||
* @param callback the callback to notify of the GOAWAY processing
|
* @param callback the callback to notify of the GOAWAY processing
|
||||||
*/
|
*/
|
||||||
public default void onClose(Session session, GoAwayFrame frame, Callback callback)
|
public default void onClose(Session session, GoAwayFrame frame, Callback callback)
|
||||||
|
|
|
@ -20,30 +20,33 @@ package org.eclipse.jetty.http2.frames;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
import org.eclipse.jetty.http2.CloseState;
|
|
||||||
import org.eclipse.jetty.http2.ErrorCode;
|
import org.eclipse.jetty.http2.ErrorCode;
|
||||||
|
|
||||||
public class GoAwayFrame extends Frame
|
public class GoAwayFrame extends Frame
|
||||||
{
|
{
|
||||||
private final CloseState closeState;
|
public static final GoAwayFrame GRACEFUL = new GoAwayFrame(Integer.MAX_VALUE, ErrorCode.NO_ERROR.code, new byte[]{'g', 'r', 'a', 'c', 'e', 'f', 'u', 'l'});
|
||||||
|
|
||||||
private final int lastStreamId;
|
private final int lastStreamId;
|
||||||
private final int error;
|
private final int error;
|
||||||
private final byte[] payload;
|
private final byte[] payload;
|
||||||
|
|
||||||
public GoAwayFrame(int lastStreamId, int error, byte[] payload)
|
public GoAwayFrame(int lastStreamId, int error, byte[] payload)
|
||||||
{
|
|
||||||
this(CloseState.REMOTELY_CLOSED, lastStreamId, error, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
public GoAwayFrame(CloseState closeState, int lastStreamId, int error, byte[] payload)
|
|
||||||
{
|
{
|
||||||
super(FrameType.GO_AWAY);
|
super(FrameType.GO_AWAY);
|
||||||
this.closeState = closeState;
|
|
||||||
this.lastStreamId = lastStreamId;
|
this.lastStreamId = lastStreamId;
|
||||||
this.error = error;
|
this.error = error;
|
||||||
this.payload = payload;
|
this.payload = payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether this GOAWAY frame is graceful, i.e. its {@code lastStreamId == Integer.MAX_VALUE}
|
||||||
|
*/
|
||||||
|
public boolean isGraceful()
|
||||||
|
{
|
||||||
|
// SPEC: section 6.8.
|
||||||
|
return lastStreamId == Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
public int getLastStreamId()
|
public int getLastStreamId()
|
||||||
{
|
{
|
||||||
return lastStreamId;
|
return lastStreamId;
|
||||||
|
@ -76,11 +79,10 @@ public class GoAwayFrame extends Frame
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return String.format("%s,%d/%s/%s/%s",
|
return String.format("%s{%d/%s/%s}",
|
||||||
super.toString(),
|
super.toString(),
|
||||||
lastStreamId,
|
lastStreamId,
|
||||||
ErrorCode.toString(error, null),
|
ErrorCode.toString(error, null),
|
||||||
tryConvertPayload(),
|
tryConvertPayload());
|
||||||
closeState);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,9 +29,8 @@ import org.eclipse.jetty.http.HttpField;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpMethod;
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
import org.eclipse.jetty.http.HttpScheme;
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
import org.eclipse.jetty.util.Index;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.Trie;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -114,13 +113,14 @@ public class HpackContext
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final Map<HttpField, Entry> __staticFieldMap = new HashMap<>();
|
private static final Map<HttpField, Entry> __staticFieldMap = new HashMap<>();
|
||||||
private static final Trie<StaticEntry> __staticNameMap = new ArrayTernaryTrie<>(true, 512);
|
private static final Index<StaticEntry> __staticNameMap;
|
||||||
private static final StaticEntry[] __staticTableByHeader = new StaticEntry[HttpHeader.UNKNOWN.ordinal()];
|
private static final StaticEntry[] __staticTableByHeader = new StaticEntry[HttpHeader.values().length];
|
||||||
private static final StaticEntry[] __staticTable = new StaticEntry[STATIC_TABLE.length];
|
private static final StaticEntry[] __staticTable = new StaticEntry[STATIC_TABLE.length];
|
||||||
public static final int STATIC_SIZE = STATIC_TABLE.length - 1;
|
public static final int STATIC_SIZE = STATIC_TABLE.length - 1;
|
||||||
|
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
|
Index.Builder<StaticEntry> staticNameMapBuilder = new Index.Builder<StaticEntry>().caseSensitive(false);
|
||||||
Set<String> added = new HashSet<>();
|
Set<String> added = new HashSet<>();
|
||||||
for (int i = 1; i < STATIC_TABLE.length; i++)
|
for (int i = 1; i < STATIC_TABLE.length; i++)
|
||||||
{
|
{
|
||||||
|
@ -173,11 +173,10 @@ public class HpackContext
|
||||||
if (!added.contains(entry._field.getName()))
|
if (!added.contains(entry._field.getName()))
|
||||||
{
|
{
|
||||||
added.add(entry._field.getName());
|
added.add(entry._field.getName());
|
||||||
__staticNameMap.put(entry._field.getName(), entry);
|
staticNameMapBuilder.with(entry._field.getName(), entry);
|
||||||
if (__staticNameMap.get(entry._field.getName()) == null)
|
|
||||||
throw new IllegalStateException("name trie too small");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
__staticNameMap = staticNameMapBuilder.build();
|
||||||
|
|
||||||
for (HttpHeader h : HttpHeader.values())
|
for (HttpHeader h : HttpHeader.values())
|
||||||
{
|
{
|
||||||
|
|
|
@ -40,13 +40,13 @@ import org.eclipse.jetty.client.SendFailure;
|
||||||
import org.eclipse.jetty.http.HttpURI;
|
import org.eclipse.jetty.http.HttpURI;
|
||||||
import org.eclipse.jetty.http.HttpVersion;
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
import org.eclipse.jetty.http.MetaData;
|
import org.eclipse.jetty.http.MetaData;
|
||||||
import org.eclipse.jetty.http2.CloseState;
|
|
||||||
import org.eclipse.jetty.http2.ErrorCode;
|
import org.eclipse.jetty.http2.ErrorCode;
|
||||||
import org.eclipse.jetty.http2.HTTP2Session;
|
import org.eclipse.jetty.http2.HTTP2Session;
|
||||||
import org.eclipse.jetty.http2.IStream;
|
|
||||||
import org.eclipse.jetty.http2.api.Session;
|
import org.eclipse.jetty.http2.api.Session;
|
||||||
|
import org.eclipse.jetty.http2.api.Stream;
|
||||||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
|
import org.eclipse.jetty.util.Promise;
|
||||||
import org.eclipse.jetty.util.thread.Sweeper;
|
import org.eclipse.jetty.util.thread.Sweeper;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -99,29 +99,43 @@ public class HttpConnectionOverHTTP2 extends HttpConnection implements Sweeper.S
|
||||||
|
|
||||||
public void upgrade(Map<String, Object> context)
|
public void upgrade(Map<String, Object> context)
|
||||||
{
|
{
|
||||||
HttpResponse response = (HttpResponse)context.get(HttpResponse.class.getName());
|
|
||||||
HttpRequest request = (HttpRequest)response.getRequest();
|
|
||||||
// In case of HTTP/1.1 upgrade to HTTP/2, the request is HTTP/1.1
|
// In case of HTTP/1.1 upgrade to HTTP/2, the request is HTTP/1.1
|
||||||
// (with upgrade) for a resource, and the response is HTTP/2.
|
// (with upgrade) for a resource, and the response is HTTP/2.
|
||||||
// Create the implicit stream#1 so that it can receive the HTTP/2 response.
|
|
||||||
MetaData.Request metaData = new MetaData.Request(request.getMethod(), HttpURI.from(request.getURI()), HttpVersion.HTTP_2, request.getHeaders());
|
HttpResponse response = (HttpResponse)context.get(HttpResponse.class.getName());
|
||||||
// We do not support upgrade requests with content, so endStream=true.
|
HttpRequest request = (HttpRequest)response.getRequest();
|
||||||
HeadersFrame frame = new HeadersFrame(metaData, null, true);
|
|
||||||
IStream stream = ((HTTP2Session)session).newLocalStream(frame, null);
|
|
||||||
stream.updateClose(frame.isEndStream(), CloseState.Event.AFTER_SEND);
|
|
||||||
|
|
||||||
HttpExchange exchange = request.getConversation().getExchanges().peekLast();
|
HttpExchange exchange = request.getConversation().getExchanges().peekLast();
|
||||||
HttpChannelOverHTTP2 http2Channel = acquireHttpChannel();
|
HttpChannelOverHTTP2 http2Channel = acquireHttpChannel();
|
||||||
activeChannels.add(http2Channel);
|
activeChannels.add(http2Channel);
|
||||||
HttpExchange newExchange = new HttpExchange(exchange.getHttpDestination(), exchange.getRequest(), List.of());
|
HttpExchange newExchange = new HttpExchange(exchange.getHttpDestination(), exchange.getRequest(), List.of());
|
||||||
http2Channel.associate(newExchange);
|
http2Channel.associate(newExchange);
|
||||||
stream.setListener(http2Channel.getStreamListener());
|
|
||||||
http2Channel.setStream(stream);
|
|
||||||
newExchange.requestComplete(null);
|
|
||||||
newExchange.terminateRequest();
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
// Create the implicit stream#1 so that it can receive the HTTP/2 response.
|
||||||
LOG.debug("Upgrade completed for {}", this);
|
MetaData.Request metaData = new MetaData.Request(request.getMethod(), HttpURI.from(request.getURI()), HttpVersion.HTTP_2, request.getHeaders());
|
||||||
|
// We do not support upgrade requests with content, so endStream=true.
|
||||||
|
HeadersFrame frame = new HeadersFrame(metaData, null, true);
|
||||||
|
((HTTP2Session)session).newUpgradeStream(frame, http2Channel.getStreamListener(), new Promise<>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void succeeded(Stream stream)
|
||||||
|
{
|
||||||
|
http2Channel.setStream(stream);
|
||||||
|
newExchange.requestComplete(null);
|
||||||
|
newExchange.terminateRequest();
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Upgrade succeeded for {}", HttpConnectionOverHTTP2.this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failed(Throwable failure)
|
||||||
|
{
|
||||||
|
newExchange.requestComplete(failure);
|
||||||
|
newExchange.terminateRequest();
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Upgrade failed for {}", HttpConnectionOverHTTP2.this);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -35,6 +35,7 @@ public class AbstractTest
|
||||||
{
|
{
|
||||||
protected Server server;
|
protected Server server;
|
||||||
protected ServerConnector connector;
|
protected ServerConnector connector;
|
||||||
|
protected HTTP2Client http2Client;
|
||||||
protected HttpClient client;
|
protected HttpClient client;
|
||||||
|
|
||||||
protected void start(ServerSessionListener listener) throws Exception
|
protected void start(ServerSessionListener listener) throws Exception
|
||||||
|
@ -63,12 +64,13 @@ public class AbstractTest
|
||||||
server.addConnector(connector);
|
server.addConnector(connector);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void prepareClient() throws Exception
|
protected void prepareClient()
|
||||||
{
|
{
|
||||||
client = new HttpClient(new HttpClientTransportOverHTTP2(new HTTP2Client()));
|
http2Client = new HTTP2Client();
|
||||||
|
client = new HttpClient(new HttpClientTransportOverHTTP2(http2Client));
|
||||||
QueuedThreadPool clientExecutor = new QueuedThreadPool();
|
QueuedThreadPool clientExecutor = new QueuedThreadPool();
|
||||||
clientExecutor.setName("client");
|
clientExecutor.setName("client");
|
||||||
client.setExecutor(clientExecutor);
|
this.client.setExecutor(clientExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
|
|
|
@ -226,7 +226,9 @@ public class HttpClientTransportOverHTTP2Test extends AbstractTest
|
||||||
MetaData.Request request = (MetaData.Request)frame.getMetaData();
|
MetaData.Request request = (MetaData.Request)frame.getMetaData();
|
||||||
if (HttpMethod.HEAD.is(request.getMethod()))
|
if (HttpMethod.HEAD.is(request.getMethod()))
|
||||||
{
|
{
|
||||||
stream.getSession().close(ErrorCode.REFUSED_STREAM_ERROR.code, null, Callback.NOOP);
|
int error = ErrorCode.REFUSED_STREAM_ERROR.code;
|
||||||
|
stream.reset(new ResetFrame(stream.getId(), error), Callback.NOOP);
|
||||||
|
stream.getSession().close(error, null, Callback.NOOP);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Jetty Logging using jetty-slf4j-impl
|
# Jetty Logging using jetty-slf4j-impl
|
||||||
#org.eclipse.jetty.LEVEL=DEBUG
|
#org.eclipse.jetty.LEVEL=DEBUG
|
||||||
#org.eclipse.jetty.client.LEVEL=DEBUG
|
#org.eclipse.jetty.client.LEVEL=DEBUG
|
||||||
org.eclipse.jetty.http2.hpack.LEVEL=INFO
|
|
||||||
#org.eclipse.jetty.http2.LEVEL=DEBUG
|
#org.eclipse.jetty.http2.LEVEL=DEBUG
|
||||||
|
org.eclipse.jetty.http2.hpack.LEVEL=INFO
|
||||||
#org.eclipse.jetty.io.ssl.LEVEL=DEBUG
|
#org.eclipse.jetty.io.ssl.LEVEL=DEBUG
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.MetaData;
|
import org.eclipse.jetty.http.MetaData;
|
||||||
|
import org.eclipse.jetty.http2.CloseState;
|
||||||
import org.eclipse.jetty.http2.ErrorCode;
|
import org.eclipse.jetty.http2.ErrorCode;
|
||||||
import org.eclipse.jetty.http2.FlowControlStrategy;
|
import org.eclipse.jetty.http2.FlowControlStrategy;
|
||||||
import org.eclipse.jetty.http2.HTTP2Session;
|
import org.eclipse.jetty.http2.HTTP2Session;
|
||||||
|
@ -65,18 +66,12 @@ public class HTTP2ServerSession extends HTTP2Session implements ServerParser.Lis
|
||||||
settings = Collections.emptyMap();
|
settings = Collections.emptyMap();
|
||||||
SettingsFrame settingsFrame = new SettingsFrame(settings, false);
|
SettingsFrame settingsFrame = new SettingsFrame(settings, false);
|
||||||
|
|
||||||
WindowUpdateFrame windowFrame = null;
|
|
||||||
int sessionWindow = getInitialSessionRecvWindow() - FlowControlStrategy.DEFAULT_WINDOW_SIZE;
|
int sessionWindow = getInitialSessionRecvWindow() - FlowControlStrategy.DEFAULT_WINDOW_SIZE;
|
||||||
|
updateRecvWindow(sessionWindow);
|
||||||
if (sessionWindow > 0)
|
if (sessionWindow > 0)
|
||||||
{
|
frames(null, List.of(settingsFrame, new WindowUpdateFrame(0, sessionWindow)), Callback.NOOP);
|
||||||
updateRecvWindow(sessionWindow);
|
|
||||||
windowFrame = new WindowUpdateFrame(0, sessionWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (windowFrame == null)
|
|
||||||
frames(null, List.of(settingsFrame), Callback.NOOP);
|
|
||||||
else
|
else
|
||||||
frames(null, List.of(settingsFrame, windowFrame), Callback.NOOP);
|
frames(null, List.of(settingsFrame), Callback.NOOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -105,31 +100,26 @@ public class HTTP2ServerSession extends HTTP2Session implements ServerParser.Lis
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (isClosed())
|
stream = createRemoteStream(streamId, (MetaData.Request)metaData);
|
||||||
|
if (stream != null)
|
||||||
{
|
{
|
||||||
updateLastRemoteStreamId(streamId);
|
onStreamOpened(stream);
|
||||||
reset(new ResetFrame(streamId, ErrorCode.REFUSED_STREAM_ERROR.code), Callback.NOOP);
|
|
||||||
}
|
if (metaData instanceof MetaData.ConnectRequest)
|
||||||
else
|
|
||||||
{
|
|
||||||
stream = createRemoteStream(streamId, (MetaData.Request)metaData);
|
|
||||||
if (stream != null)
|
|
||||||
{
|
{
|
||||||
onStreamOpened(stream);
|
if (!isConnectProtocolEnabled() && ((MetaData.ConnectRequest)metaData).getProtocol() != null)
|
||||||
|
|
||||||
if (metaData instanceof MetaData.ConnectRequest)
|
|
||||||
{
|
{
|
||||||
if (!isConnectProtocolEnabled() && ((MetaData.ConnectRequest)metaData).getProtocol() != null)
|
stream.reset(new ResetFrame(streamId, ErrorCode.PROTOCOL_ERROR.code), Callback.NOOP);
|
||||||
{
|
return;
|
||||||
stream.reset(new ResetFrame(streamId, ErrorCode.PROTOCOL_ERROR.code), Callback.NOOP);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.process(frame, Callback.NOOP);
|
|
||||||
Stream.Listener listener = notifyNewStream(stream, frame);
|
|
||||||
stream.setListener(listener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stream.process(frame, Callback.NOOP);
|
||||||
|
boolean closed = stream.updateClose(frame.isEndStream(), CloseState.Event.RECEIVED);
|
||||||
|
Stream.Listener listener = notifyNewStream(stream, frame);
|
||||||
|
stream.setListener(listener);
|
||||||
|
if (closed)
|
||||||
|
removeStream(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,7 +138,10 @@ public class HTTP2ServerSession extends HTTP2Session implements ServerParser.Lis
|
||||||
if (stream != null)
|
if (stream != null)
|
||||||
{
|
{
|
||||||
stream.process(frame, Callback.NOOP);
|
stream.process(frame, Callback.NOOP);
|
||||||
|
boolean closed = stream.updateClose(frame.isEndStream(), CloseState.Event.RECEIVED);
|
||||||
notifyHeaders(stream, frame);
|
notifyHeaders(stream, frame);
|
||||||
|
if (closed)
|
||||||
|
removeStream(stream);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -109,6 +109,12 @@ public class RawHTTP2ServerConnectionFactory extends AbstractHTTP2ServerConnecti
|
||||||
delegate.onReset(session, frame);
|
delegate.onReset(session, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGoAway(Session session, GoAwayFrame frame)
|
||||||
|
{
|
||||||
|
delegate.onGoAway(session, frame);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClose(Session session, GoAwayFrame frame)
|
public void onClose(Session session, GoAwayFrame frame)
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,8 +28,7 @@ import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
import org.eclipse.jetty.util.Index;
|
||||||
import org.eclipse.jetty.util.Trie;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Special handling for MSIE (Microsoft Internet Explorer).
|
* 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 IEv5 = '5';
|
||||||
private static final int IEv6 = '6';
|
private static final int IEv6 = '6';
|
||||||
private static final Trie<Boolean> __IE6_BadOS = new ArrayTernaryTrie<>();
|
private static final Index<Boolean> __IE6_BadOS = new Index.Builder<Boolean>()
|
||||||
|
.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 CONNECTION_CLOSE = new HttpField(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE);
|
||||||
private static final HttpField VARY_USER_AGENT = new PreEncodedHttpField(HttpHeader.VARY, HttpHeader.USER_AGENT.asString());
|
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()
|
public MsieRule()
|
||||||
{
|
{
|
||||||
_handling = false;
|
_handling = false;
|
||||||
|
|
|
@ -24,8 +24,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
import org.eclipse.jetty.util.Index;
|
||||||
import org.eclipse.jetty.util.Trie;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MSIE (Microsoft Internet Explorer) SSL Rule.
|
* 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 IEv5 = '5';
|
||||||
private static final int IEv6 = '6';
|
private static final int IEv6 = '6';
|
||||||
private static Trie<Boolean> __IE6_BadOS = new ArrayTernaryTrie<>();
|
private static final Index<Boolean> __IE6_BadOS = new Index.Builder<Boolean>()
|
||||||
|
.caseSensitive(false)
|
||||||
{
|
.with("NT 5.01", Boolean.TRUE)
|
||||||
__IE6_BadOS.put("NT 5.01", Boolean.TRUE);
|
.with("NT 5.0", Boolean.TRUE)
|
||||||
__IE6_BadOS.put("NT 5.0", Boolean.TRUE);
|
.with("NT 4.0", Boolean.TRUE)
|
||||||
__IE6_BadOS.put("NT 4.0", Boolean.TRUE);
|
.with("98", Boolean.TRUE)
|
||||||
__IE6_BadOS.put("98", Boolean.TRUE);
|
.with("98; Win 9x 4.90", Boolean.TRUE)
|
||||||
__IE6_BadOS.put("98; Win 9x 4.90", Boolean.TRUE);
|
.with("95", Boolean.TRUE)
|
||||||
__IE6_BadOS.put("95", Boolean.TRUE);
|
.with("CE", Boolean.TRUE)
|
||||||
__IE6_BadOS.put("CE", Boolean.TRUE);
|
.build();
|
||||||
}
|
|
||||||
|
|
||||||
public MsieSslRule()
|
public MsieSslRule()
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,10 +33,9 @@ import org.eclipse.jetty.http.HttpScheme;
|
||||||
import org.eclipse.jetty.http.HttpURI;
|
import org.eclipse.jetty.http.HttpURI;
|
||||||
import org.eclipse.jetty.http.QuotedCSVParser;
|
import org.eclipse.jetty.http.QuotedCSVParser;
|
||||||
import org.eclipse.jetty.server.HttpConfiguration.Customizer;
|
import org.eclipse.jetty.server.HttpConfiguration.Customizer;
|
||||||
import org.eclipse.jetty.util.ArrayTrie;
|
|
||||||
import org.eclipse.jetty.util.HostPort;
|
import org.eclipse.jetty.util.HostPort;
|
||||||
|
import org.eclipse.jetty.util.Index;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.Trie;
|
|
||||||
|
|
||||||
import static java.lang.invoke.MethodType.methodType;
|
import static java.lang.invoke.MethodType.methodType;
|
||||||
|
|
||||||
|
@ -159,7 +158,10 @@ public class ForwardedRequestCustomizer implements Customizer
|
||||||
private String _forwardedCipherSuiteHeader = "Proxy-auth-cert";
|
private String _forwardedCipherSuiteHeader = "Proxy-auth-cert";
|
||||||
private String _forwardedSslSessionIdHeader = "Proxy-ssl-id";
|
private String _forwardedSslSessionIdHeader = "Proxy-ssl-id";
|
||||||
private boolean _sslIsSecure = true;
|
private boolean _sslIsSecure = true;
|
||||||
private Trie<MethodHandle> _handles;
|
private final Index.Mutable<MethodHandle> _handles = new Index.Builder<MethodHandle>()
|
||||||
|
.caseSensitive(false)
|
||||||
|
.mutable()
|
||||||
|
.build();
|
||||||
|
|
||||||
public ForwardedRequestCustomizer()
|
public ForwardedRequestCustomizer()
|
||||||
{
|
{
|
||||||
|
@ -596,52 +598,33 @@ public class ForwardedRequestCustomizer implements Customizer
|
||||||
|
|
||||||
private void updateHandles()
|
private void updateHandles()
|
||||||
{
|
{
|
||||||
int size = 0;
|
|
||||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||||
|
try
|
||||||
// Loop to grow capacity of ArrayTrie for all headers
|
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
try
|
updateForwardedHandle(lookup, getForwardedHeader(), "handleRFC7239");
|
||||||
{
|
updateForwardedHandle(lookup, getForwardedHostHeader(), "handleForwardedHost");
|
||||||
size += 128; // experimented good baseline size
|
updateForwardedHandle(lookup, getForwardedForHeader(), "handleForwardedFor");
|
||||||
_handles = new ArrayTrie<>(size);
|
updateForwardedHandle(lookup, getForwardedPortHeader(), "handleForwardedPort");
|
||||||
|
updateForwardedHandle(lookup, getForwardedProtoHeader(), "handleProto");
|
||||||
if (updateForwardedHandle(lookup, getForwardedHeader(), "handleRFC7239"))
|
updateForwardedHandle(lookup, getForwardedHttpsHeader(), "handleHttps");
|
||||||
continue;
|
updateForwardedHandle(lookup, getForwardedServerHeader(), "handleForwardedServer");
|
||||||
if (updateForwardedHandle(lookup, getForwardedHostHeader(), "handleForwardedHost"))
|
updateForwardedHandle(lookup, getForwardedCipherSuiteHeader(), "handleCipherSuite");
|
||||||
continue;
|
updateForwardedHandle(lookup, getForwardedSslSessionIdHeader(), "handleSslSessionId");
|
||||||
if (updateForwardedHandle(lookup, getForwardedForHeader(), "handleForwardedFor"))
|
}
|
||||||
continue;
|
catch (NoSuchMethodException | IllegalAccessException e)
|
||||||
if (updateForwardedHandle(lookup, getForwardedPortHeader(), "handleForwardedPort"))
|
{
|
||||||
continue;
|
throw new IllegalStateException(e);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
final MethodType type = methodType(void.class, HttpField.class);
|
||||||
|
|
||||||
if (StringUtil.isBlank(headerName))
|
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
|
private static class MutableHostPort
|
||||||
|
|
|
@ -508,38 +508,27 @@ public class HttpChannelOverHttp extends HttpChannel implements HttpParser.Reque
|
||||||
if (HttpVersion.HTTP_1_1.equals(_requestBuilder.version()))
|
if (HttpVersion.HTTP_1_1.equals(_requestBuilder.version()))
|
||||||
{
|
{
|
||||||
HttpHeaderValue expect = HttpHeaderValue.CACHE.get(value);
|
HttpHeaderValue expect = HttpHeaderValue.CACHE.get(value);
|
||||||
switch (expect == null ? HttpHeaderValue.UNKNOWN : expect)
|
if (expect == HttpHeaderValue.CONTINUE)
|
||||||
{
|
{
|
||||||
case CONTINUE:
|
_expect100Continue = true;
|
||||||
_expect100Continue = true;
|
}
|
||||||
break;
|
else if (expect == HttpHeaderValue.PROCESSING)
|
||||||
|
{
|
||||||
case PROCESSING:
|
_expect102Processing = true;
|
||||||
_expect102Processing = true;
|
}
|
||||||
break;
|
else
|
||||||
|
{
|
||||||
default:
|
String[] values = field.getValues();
|
||||||
String[] values = field.getValues();
|
for (int i = 0; values != null && i < values.length; i++)
|
||||||
for (int i = 0; values != null && i < values.length; i++)
|
{
|
||||||
{
|
expect = HttpHeaderValue.CACHE.get(values[i].trim());
|
||||||
expect = HttpHeaderValue.CACHE.get(values[i].trim());
|
if (expect == HttpHeaderValue.CONTINUE)
|
||||||
if (expect == null)
|
_expect100Continue = true;
|
||||||
_unknownExpectation = true;
|
else if (expect == HttpHeaderValue.PROCESSING)
|
||||||
else
|
_expect102Processing = true;
|
||||||
{
|
else
|
||||||
switch (expect)
|
_unknownExpectation = true;
|
||||||
{
|
}
|
||||||
case CONTINUE:
|
|
||||||
_expect100Continue = true;
|
|
||||||
break;
|
|
||||||
case PROCESSING:
|
|
||||||
_expect102Processing = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
_unknownExpectation = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -27,9 +27,8 @@ import org.eclipse.jetty.http.CookieCompliance;
|
||||||
import org.eclipse.jetty.http.HttpCompliance;
|
import org.eclipse.jetty.http.HttpCompliance;
|
||||||
import org.eclipse.jetty.http.HttpMethod;
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
import org.eclipse.jetty.http.HttpScheme;
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
|
import org.eclipse.jetty.util.Index;
|
||||||
import org.eclipse.jetty.util.Jetty;
|
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.ManagedAttribute;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
import org.eclipse.jetty.util.component.Dumpable;
|
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 + ")";
|
public static final String SERVER_VERSION = "Jetty(" + Jetty.VERSION + ")";
|
||||||
private final List<Customizer> _customizers = new CopyOnWriteArrayList<>();
|
private final List<Customizer> _customizers = new CopyOnWriteArrayList<>();
|
||||||
private final Trie<Boolean> _formEncodedMethods = new TreeTrie<>();
|
private final Index.Mutable<Boolean> _formEncodedMethods = new Index.Builder<Boolean>()
|
||||||
|
.caseSensitive(false)
|
||||||
|
.mutable()
|
||||||
|
.build();
|
||||||
private int _outputBufferSize = 32 * 1024;
|
private int _outputBufferSize = 32 * 1024;
|
||||||
private int _outputAggregationSize = _outputBufferSize / 4;
|
private int _outputAggregationSize = _outputBufferSize / 4;
|
||||||
private int _requestHeaderSize = 8 * 1024;
|
private int _requestHeaderSize = 8 * 1024;
|
||||||
|
@ -424,7 +426,7 @@ public class HttpConfiguration implements Dumpable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param headerCacheSize The size of the header field cache, in terms of unique characters branches
|
* @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)
|
public void setHeaderCacheSize(int headerCacheSize)
|
||||||
{
|
{
|
||||||
|
@ -491,7 +493,7 @@ public class HttpConfiguration implements Dumpable
|
||||||
*/
|
*/
|
||||||
public void addFormEncodedMethod(String method)
|
public void addFormEncodedMethod(String method)
|
||||||
{
|
{
|
||||||
_formEncodedMethods.put(method, Boolean.TRUE);
|
_formEncodedMethods.put(method,Boolean.TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -504,7 +506,7 @@ public class HttpConfiguration implements Dumpable
|
||||||
*/
|
*/
|
||||||
public boolean isFormEncodedMethod(String method)
|
public boolean isFormEncodedMethod(String method)
|
||||||
{
|
{
|
||||||
return Boolean.TRUE.equals(_formEncodedMethods.get(method));
|
return _formEncodedMethods.get(method) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,8 +20,10 @@ package org.eclipse.jetty.server.handler;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -32,10 +34,9 @@ import org.eclipse.jetty.server.Handler;
|
||||||
import org.eclipse.jetty.server.HandlerContainer;
|
import org.eclipse.jetty.server.HandlerContainer;
|
||||||
import org.eclipse.jetty.server.HttpChannelState;
|
import org.eclipse.jetty.server.HttpChannelState;
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
|
||||||
import org.eclipse.jetty.util.ArrayUtil;
|
import org.eclipse.jetty.util.ArrayUtil;
|
||||||
import org.eclipse.jetty.util.Callback;
|
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.ManagedObject;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
||||||
import org.eclipse.jetty.util.thread.SerializedExecutor;
|
import org.eclipse.jetty.util.thread.SerializedExecutor;
|
||||||
|
@ -129,44 +130,9 @@ public class ContextHandlerCollection extends HandlerCollection
|
||||||
entry.setValue(sorted);
|
entry.setValue(sorted);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop until we have a big enough trie to hold all the context paths
|
Mapping mapping = new Mapping(handlers, path2Branches);
|
||||||
int capacity = 512;
|
|
||||||
Mapping mapping;
|
|
||||||
loop:
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
mapping = new Mapping(handlers, capacity);
|
|
||||||
for (Map.Entry<String, Branch[]> entry : path2Branches.entrySet())
|
|
||||||
{
|
|
||||||
if (!mapping._pathBranches.put(entry.getKey().substring(1), entry))
|
|
||||||
{
|
|
||||||
capacity += 512;
|
|
||||||
continue loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
{
|
LOG.debug("{}", mapping._pathBranches);
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mapping;
|
return mapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,7 +175,7 @@ public class ContextHandlerCollection extends HandlerCollection
|
||||||
// handle many contexts
|
// handle many contexts
|
||||||
if (target.startsWith("/"))
|
if (target.startsWith("/"))
|
||||||
{
|
{
|
||||||
Trie<Map.Entry<String, Branch[]>> pathBranches = mapping._pathBranches;
|
Index<Map.Entry<String, Branch[]>> pathBranches = mapping._pathBranches;
|
||||||
if (pathBranches == null)
|
if (pathBranches == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -377,13 +343,38 @@ public class ContextHandlerCollection extends HandlerCollection
|
||||||
|
|
||||||
private static class Mapping extends Handlers
|
private static class Mapping extends Handlers
|
||||||
{
|
{
|
||||||
private final Map<ContextHandler, Handler> _contextBranches = new HashMap<>();
|
private final Map<ContextHandler, Handler> _contextBranches;
|
||||||
private final Trie<Map.Entry<String, Branch[]>> _pathBranches;
|
private final Index<Map.Entry<String, Branch[]>> _pathBranches;
|
||||||
|
|
||||||
private Mapping(Handler[] handlers, int capacity)
|
private Mapping(Handler[] handlers, Map<String, Branch[]> path2Branches)
|
||||||
{
|
{
|
||||||
super(handlers);
|
super(handlers);
|
||||||
_pathBranches = new ArrayTernaryTrie<>(false, capacity);
|
_pathBranches = new Index.Builder<Map.Entry<String, Branch[]>>()
|
||||||
|
.caseSensitive(true)
|
||||||
|
.withAll(() ->
|
||||||
|
{
|
||||||
|
Map<String, Map.Entry<String, Branch[]>> result = new LinkedHashMap<>();
|
||||||
|
for (Map.Entry<String, Branch[]> entry : path2Branches.entrySet())
|
||||||
|
{
|
||||||
|
result.put(entry.getKey().substring(1), entry);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// add new context branches to map
|
||||||
|
Map<ContextHandler, Handler> 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,10 +26,9 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
|
import org.eclipse.jetty.util.Index;
|
||||||
import org.eclipse.jetty.util.Loader;
|
import org.eclipse.jetty.util.Loader;
|
||||||
import org.eclipse.jetty.util.Trie;
|
|
||||||
import org.eclipse.jetty.util.TypeUtil;
|
import org.eclipse.jetty.util.TypeUtil;
|
||||||
import org.eclipse.jetty.util.Utf8StringBuilder;
|
import org.eclipse.jetty.util.Utf8StringBuilder;
|
||||||
import org.eclipse.jetty.util.ajax.JSON.Convertible;
|
import org.eclipse.jetty.util.ajax.JSON.Convertible;
|
||||||
|
@ -79,7 +78,7 @@ public class AsyncJSON
|
||||||
*/
|
*/
|
||||||
public static class Factory
|
public static class Factory
|
||||||
{
|
{
|
||||||
private Trie<CachedString> cache;
|
private Index.Mutable<CachedString> cache;
|
||||||
private Map<String, Convertor> convertors;
|
private Map<String, Convertor> convertors;
|
||||||
private boolean detailedParseException;
|
private boolean detailedParseException;
|
||||||
|
|
||||||
|
@ -106,7 +105,10 @@ public class AsyncJSON
|
||||||
public boolean cache(String value)
|
public boolean cache(String value)
|
||||||
{
|
{
|
||||||
if (cache == null)
|
if (cache == null)
|
||||||
cache = new ArrayTernaryTrie.Growing<>(false, 64, 64);
|
cache = new Index.Builder<CachedString>()
|
||||||
|
.caseSensitive(true)
|
||||||
|
.mutable()
|
||||||
|
.build();
|
||||||
|
|
||||||
CachedString cached = new CachedString(value);
|
CachedString cached = new CachedString(value);
|
||||||
if (cached.isCacheable())
|
if (cached.isCacheable())
|
||||||
|
|
|
@ -20,6 +20,11 @@ package org.eclipse.jetty.util;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
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.
|
* Abstract Trie implementation.
|
||||||
|
@ -29,7 +34,7 @@ import java.nio.charset.StandardCharsets;
|
||||||
*
|
*
|
||||||
* @param <V> the type of object that the Trie holds
|
* @param <V> the type of object that the Trie holds
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractTrie<V> implements Trie<V>
|
abstract class AbstractTrie<V> implements Index.Mutable<V>
|
||||||
{
|
{
|
||||||
final boolean _caseInsensitive;
|
final boolean _caseInsensitive;
|
||||||
|
|
||||||
|
@ -38,13 +43,16 @@ public abstract class AbstractTrie<V> implements Trie<V>
|
||||||
_caseInsensitive = insensitive;
|
_caseInsensitive = insensitive;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean isCaseInsensitive()
|
||||||
|
{
|
||||||
|
return _caseInsensitive;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean put(V v)
|
public boolean put(V v)
|
||||||
{
|
{
|
||||||
return put(v.toString(), v);
|
return put(v.toString(), v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public V remove(String s)
|
public V remove(String s)
|
||||||
{
|
{
|
||||||
V o = get(s);
|
V o = get(s);
|
||||||
|
@ -52,33 +60,109 @@ public abstract class AbstractTrie<V> implements Trie<V>
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public V get(String s)
|
public V get(String s)
|
||||||
{
|
{
|
||||||
return get(s, 0, s.length());
|
return get(s, 0, s.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public V get(ByteBuffer b)
|
public V get(ByteBuffer b)
|
||||||
{
|
{
|
||||||
return get(b, 0, b.remaining());
|
return get(b, 0, b.remaining());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public V getBest(String s)
|
public V getBest(String s)
|
||||||
{
|
{
|
||||||
return getBest(s, 0, s.length());
|
return getBest(s, 0, s.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public V getBest(byte[] b, int offset, int len)
|
public V getBest(byte[] b, int offset, int len)
|
||||||
{
|
{
|
||||||
return getBest(new String(b, offset, len, StandardCharsets.ISO_8859_1));
|
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:
|
||||||
|
* <ul>
|
||||||
|
* <li>utf_16</li>
|
||||||
|
* <li>utf_8</li>
|
||||||
|
* <li>utf16</li>
|
||||||
|
* <li>utf8</li>
|
||||||
|
* </ul>
|
||||||
|
* The tree has 10 nodes as follows:
|
||||||
|
* <pre>
|
||||||
|
* 1 - 6
|
||||||
|
* /
|
||||||
|
* _ - 8
|
||||||
|
* /
|
||||||
|
* u - t - f - 1 - 6
|
||||||
|
* \
|
||||||
|
* 8
|
||||||
|
* </pre>
|
||||||
|
* @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<String> keys, boolean caseSensitive)
|
||||||
{
|
{
|
||||||
return _caseInsensitive;
|
List<String> 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<String> 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.util.AbstractMap;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,11 +58,11 @@ import java.util.Set;
|
||||||
*
|
*
|
||||||
* @param <V> the Entry type
|
* @param <V> the Entry type
|
||||||
*/
|
*/
|
||||||
public class ArrayTernaryTrie<V> extends AbstractTrie<V>
|
class ArrayTernaryTrie<V> extends AbstractTrie<V>
|
||||||
{
|
{
|
||||||
private static int LO = 1;
|
private static final int LO = 1;
|
||||||
private static int EQ = 2;
|
private static final int EQ = 2;
|
||||||
private static int HI = 3;
|
private static final int HI = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Size of a Trie row is the char, and the low, equal and high
|
* The Size of a Trie row is the char, and the low, equal and high
|
||||||
|
@ -69,6 +70,13 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
|
||||||
*/
|
*/
|
||||||
private static final int ROW_SIZE = 4;
|
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
|
* 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
|
* to the next row in the Trie. This is actually a 2 dimensional
|
||||||
|
@ -93,39 +101,6 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
|
||||||
*/
|
*/
|
||||||
private char _rows;
|
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
|
* Create a Trie
|
||||||
*
|
*
|
||||||
|
@ -137,28 +112,37 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
|
||||||
* and "bar", but a capacity of only 4 is required to
|
* and "bar", but a capacity of only 4 is required to
|
||||||
* store "bar" and "bat".
|
* store "bar" and "bat".
|
||||||
*/
|
*/
|
||||||
public ArrayTernaryTrie(boolean insensitive, int capacity)
|
@SuppressWarnings("unchecked")
|
||||||
|
ArrayTernaryTrie(boolean insensitive, int capacity)
|
||||||
{
|
{
|
||||||
super(insensitive);
|
super(insensitive);
|
||||||
|
if (capacity > MAX_CAPACITY)
|
||||||
|
throw new IllegalArgumentException("ArrayTernaryTrie maximum capacity overflow (" + capacity + " > " + MAX_CAPACITY + ")");
|
||||||
_value = (V[])new Object[capacity];
|
_value = (V[])new Object[capacity];
|
||||||
_tree = new char[capacity * ROW_SIZE];
|
_tree = new char[capacity * ROW_SIZE];
|
||||||
_key = new String[capacity];
|
_key = new String[capacity];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@SuppressWarnings("unchecked")
|
||||||
* Copy Trie and change capacity by a factor
|
ArrayTernaryTrie(boolean insensitive, Map<String, V> initialValues)
|
||||||
*
|
|
||||||
* @param trie the trie to copy from
|
|
||||||
* @param factor the factor to grow the capacity by
|
|
||||||
*/
|
|
||||||
public ArrayTernaryTrie(ArrayTernaryTrie<V> trie, double factor)
|
|
||||||
{
|
{
|
||||||
super(trie.isCaseInsensitive());
|
super(insensitive);
|
||||||
int capacity = (int)(trie._value.length * factor);
|
// The calculated requiredCapacity does not take into account the
|
||||||
_rows = trie._rows;
|
// extra reserved slot for the empty string key, nor the slots
|
||||||
_value = Arrays.copyOf(trie._value, capacity);
|
// required for 'terminating' the entry (1 slot per key) so we
|
||||||
_tree = Arrays.copyOf(trie._tree, capacity * ROW_SIZE);
|
// have to add those.
|
||||||
_key = Arrays.copyOf(trie._key, capacity);
|
Set<String> 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<String, V> entry : initialValues.entrySet())
|
||||||
|
{
|
||||||
|
if (!put(entry.getKey(), entry.getValue()))
|
||||||
|
throw new AssertionError("Invalid capacity calculated (" + capacity + ") at '" + entry + "' for " + initialValues);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -527,12 +511,6 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFull()
|
|
||||||
{
|
|
||||||
return _rows + 1 == _key.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int hilo(int diff)
|
public static int hilo(int diff)
|
||||||
{
|
{
|
||||||
// branchless equivalent to return ((diff<0)?LO:HI);
|
// branchless equivalent to return ((diff<0)?LO:HI);
|
||||||
|
@ -556,24 +534,14 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Growing<V> implements Trie<V>
|
static class Growing<V> extends AbstractTrie<V>
|
||||||
{
|
{
|
||||||
private final int _growby;
|
private final int _growby;
|
||||||
private ArrayTernaryTrie<V> _trie;
|
private ArrayTernaryTrie<V> _trie;
|
||||||
|
|
||||||
public Growing()
|
Growing(boolean insensitive, int capacity, int growby)
|
||||||
{
|
|
||||||
this(1024, 1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Growing(int capacity, int growby)
|
|
||||||
{
|
|
||||||
_growby = growby;
|
|
||||||
_trie = new ArrayTernaryTrie<>(capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Growing(boolean insensitive, int capacity, int growby)
|
|
||||||
{
|
{
|
||||||
|
super(insensitive);
|
||||||
_growby = growby;
|
_growby = growby;
|
||||||
_trie = new ArrayTernaryTrie<>(insensitive, capacity);
|
_trie = new ArrayTernaryTrie<>(insensitive, capacity);
|
||||||
}
|
}
|
||||||
|
@ -591,15 +559,14 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCaseInsensitive()
|
public boolean equals(Object o)
|
||||||
{
|
{
|
||||||
return _trie.isCaseInsensitive();
|
if (this == o)
|
||||||
}
|
return true;
|
||||||
|
if (o == null || getClass() != o.getClass())
|
||||||
@Override
|
return false;
|
||||||
public boolean equals(Object obj)
|
Growing<?> growing = (Growing<?>)o;
|
||||||
{
|
return Objects.equals(_trie, growing._trie);
|
||||||
return _trie.equals(obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -692,12 +659,6 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
|
||||||
return _trie.keySet();
|
return _trie.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFull()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dump()
|
public void dump()
|
||||||
{
|
{
|
||||||
_trie.dump();
|
_trie.dump();
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,7 +48,7 @@ import java.util.Set;
|
||||||
*
|
*
|
||||||
* @param <V> the entry type
|
* @param <V> the entry type
|
||||||
*/
|
*/
|
||||||
public class ArrayTrie<V> extends AbstractTrie<V>
|
class ArrayTrie<V> extends AbstractTrie<V>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The Size of a Trie row is how many characters can be looked
|
* The Size of a Trie row is how many characters can be looked
|
||||||
|
@ -112,11 +113,6 @@ public class ArrayTrie<V> extends AbstractTrie<V>
|
||||||
*/
|
*/
|
||||||
private char _rows;
|
private char _rows;
|
||||||
|
|
||||||
public ArrayTrie()
|
|
||||||
{
|
|
||||||
this(128);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param capacity The capacity of the trie, which at the worst case
|
* @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.
|
* is the total number of characters of all keys stored in the Trie.
|
||||||
|
@ -126,14 +122,32 @@ public class ArrayTrie<V> extends AbstractTrie<V>
|
||||||
* store "bar" and "bat".
|
* store "bar" and "bat".
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public ArrayTrie(int capacity)
|
ArrayTrie(int capacity)
|
||||||
{
|
{
|
||||||
super(true);
|
super(true);
|
||||||
|
capacity++;
|
||||||
_value = (V[])new Object[capacity];
|
_value = (V[])new Object[capacity];
|
||||||
_rowIndex = new char[capacity * 32];
|
_rowIndex = new char[capacity * ROW_SIZE];
|
||||||
_key = new String[capacity];
|
_key = new String[capacity];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
ArrayTrie(Map<String, V> 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 * ROW_SIZE];
|
||||||
|
_key = new String[capacity];
|
||||||
|
for (Map.Entry<String, V> entry : initialValues.entrySet())
|
||||||
|
{
|
||||||
|
if (!put(entry.getKey(), entry.getValue()))
|
||||||
|
throw new AssertionError("Invalid capacity calculated (" + capacity + ") at '" + entry + "' for " + initialValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear()
|
public void clear()
|
||||||
{
|
{
|
||||||
|
@ -446,6 +460,18 @@ public class ArrayTrie<V> extends AbstractTrie<V>
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size()
|
||||||
|
{
|
||||||
|
return keySet().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty()
|
||||||
|
{
|
||||||
|
return keySet().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
private void keySet(Set<String> set, int t)
|
private void keySet(Set<String> set, int t)
|
||||||
{
|
{
|
||||||
if (t < _value.length && _value[t] != null)
|
if (t < _value.length && _value[t] != null)
|
||||||
|
@ -468,10 +494,4 @@ public class ArrayTrie<V> extends AbstractTrie<V>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFull()
|
|
||||||
{
|
|
||||||
return _rows + 1 >= _key.length;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 <V> the entry type
|
||||||
|
*/
|
||||||
|
class EmptyTrie<V> extends AbstractTrie<V>
|
||||||
|
{
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private static final EmptyTrie SENSITIVE = new EmptyTrie<>(false);
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private static final EmptyTrie INSENSITIVE = new EmptyTrie<>(true);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <V> EmptyTrie<V> 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<String> keySet()
|
||||||
|
{
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <V> the entry type
|
||||||
|
*/
|
||||||
|
public interface Index<V>
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 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<String> keySet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mutable String lookup data structure.
|
||||||
|
* Implementations are not thread-safe.
|
||||||
|
* @param <V> the entry type
|
||||||
|
*/
|
||||||
|
interface Mutable<V> extends Index<V>
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 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 <V> the entry type
|
||||||
|
*/
|
||||||
|
class Builder<V> extends Index.Builder<V>
|
||||||
|
{
|
||||||
|
private int maxCapacity = -1;
|
||||||
|
|
||||||
|
Builder(boolean caseSensitive, Map<String, V> 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<V> maxCapacity(int capacity)
|
||||||
|
{
|
||||||
|
this.maxCapacity = capacity;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a {@link Mutable} instance.
|
||||||
|
* @return a {@link Mutable} instance.
|
||||||
|
*/
|
||||||
|
public Mutable<V> 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<V> 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<String, V> 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 <V> the entry type
|
||||||
|
*/
|
||||||
|
class Builder<V>
|
||||||
|
{
|
||||||
|
Map<String, V> contents;
|
||||||
|
boolean caseSensitive;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new index builder instance.
|
||||||
|
*/
|
||||||
|
public Builder()
|
||||||
|
{
|
||||||
|
this(false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder(boolean caseSensitive, Map<String, V> contents)
|
||||||
|
{
|
||||||
|
this.caseSensitive = caseSensitive;
|
||||||
|
this.contents = contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, V> 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<V> 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<V> withAll(V[] values, Function<V, String> 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<V> withAll(Supplier<Map<String, V>> entriesSupplier)
|
||||||
|
{
|
||||||
|
Map<String, V> 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<V> 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<V> 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<V> mutable()
|
||||||
|
{
|
||||||
|
return new Mutable.Builder<>(caseSensitive, contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a {@link Index} instance.
|
||||||
|
*
|
||||||
|
* @return a {@link Index} instance.
|
||||||
|
*/
|
||||||
|
public Index<V> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -492,6 +492,6 @@ public abstract class IteratingCallback implements Callback
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return String.format("%s[%s]", super.toString(), _state);
|
return String.format("%s@%x[%s]", getClass().getSimpleName(), hashCode(), _state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -249,6 +249,11 @@ public class Pool<T> implements AutoCloseable, Dumpable
|
||||||
{
|
{
|
||||||
LOGGER.trace("IGNORED", e);
|
LOGGER.trace("IGNORED", e);
|
||||||
size = entries.size();
|
size = entries.size();
|
||||||
|
// Size can be 0 when the pool is in the middle of
|
||||||
|
// acquiring a connection while another thread
|
||||||
|
// removes the last one from the pool.
|
||||||
|
if (size == 0)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
index = (index + 1) % size;
|
index = (index + 1) % size;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,6 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public class StringUtil
|
public class StringUtil
|
||||||
{
|
{
|
||||||
private static final Trie<String> CHARSETS = new ArrayTrie<>(256);
|
|
||||||
|
|
||||||
public static final String ALL_INTERFACES = "0.0.0.0";
|
public static final String ALL_INTERFACES = "0.0.0.0";
|
||||||
public static final String CRLF = "\r\n";
|
public static final String CRLF = "\r\n";
|
||||||
public static final String DEFAULT_DELIMS = ",;";
|
public static final String DEFAULT_DELIMS = ",;";
|
||||||
|
@ -43,15 +41,15 @@ public class StringUtil
|
||||||
public static final String __UTF8 = "utf-8";
|
public static final String __UTF8 = "utf-8";
|
||||||
public static final String __UTF16 = "utf-16";
|
public static final String __UTF16 = "utf-16";
|
||||||
|
|
||||||
static
|
private static final Index<String> CHARSETS = new Index.Builder<String>()
|
||||||
{
|
.caseSensitive(false)
|
||||||
CHARSETS.put("utf-8", __UTF8);
|
.with("utf-8", __UTF8)
|
||||||
CHARSETS.put("utf8", __UTF8);
|
.with("utf8", __UTF8)
|
||||||
CHARSETS.put("utf-16", __UTF16);
|
.with("utf-16", __UTF16)
|
||||||
CHARSETS.put("utf16", __UTF16);
|
.with("utf16", __UTF16)
|
||||||
CHARSETS.put("iso-8859-1", __ISO_8859_1);
|
.with("iso-8859-1", __ISO_8859_1)
|
||||||
CHARSETS.put("iso_8859_1", __ISO_8859_1);
|
.with("iso_8859_1", __ISO_8859_1)
|
||||||
}
|
.build();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert alternate charset names (eg utf8) to normalized
|
* Convert alternate charset names (eg utf8) to normalized
|
||||||
|
|
|
@ -42,7 +42,7 @@ import java.util.Set;
|
||||||
*
|
*
|
||||||
* @param <V> the entry type
|
* @param <V> the entry type
|
||||||
*/
|
*/
|
||||||
public class TreeTrie<V> extends AbstractTrie<V>
|
class TreeTrie<V> extends AbstractTrie<V>
|
||||||
{
|
{
|
||||||
private static final int[] LOOKUP =
|
private static final int[] LOOKUP =
|
||||||
{
|
{
|
||||||
|
@ -63,13 +63,15 @@ public class TreeTrie<V> extends AbstractTrie<V>
|
||||||
private String _key;
|
private String _key;
|
||||||
private V _value;
|
private V _value;
|
||||||
|
|
||||||
public TreeTrie()
|
@SuppressWarnings("unchecked")
|
||||||
|
TreeTrie()
|
||||||
{
|
{
|
||||||
super(true);
|
super(true);
|
||||||
_nextIndex = new TreeTrie[INDEX];
|
_nextIndex = new TreeTrie[INDEX];
|
||||||
_c = 0;
|
_c = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
private TreeTrie(char c)
|
private TreeTrie(char c)
|
||||||
{
|
{
|
||||||
super(true);
|
super(true);
|
||||||
|
@ -231,6 +233,18 @@ public class TreeTrie<V> extends AbstractTrie<V>
|
||||||
return t._value;
|
return t._value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty()
|
||||||
|
{
|
||||||
|
return keySet().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size()
|
||||||
|
{
|
||||||
|
return keySet().size();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V getBest(String s, int offset, int len)
|
public V getBest(String s, int offset, int len)
|
||||||
{
|
{
|
||||||
|
@ -394,10 +408,4 @@ public class TreeTrie<V> extends AbstractTrie<V>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFull()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 <V> the Trie entry type
|
|
||||||
*/
|
|
||||||
public interface Trie<V>
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<String> keySet();
|
|
||||||
|
|
||||||
public boolean isFull();
|
|
||||||
|
|
||||||
public boolean isCaseInsensitive();
|
|
||||||
|
|
||||||
public void clear();
|
|
||||||
|
|
||||||
static <T> Trie<T> empty(final boolean caseInsensitive)
|
|
||||||
{
|
|
||||||
return new Trie<T>()
|
|
||||||
{
|
|
||||||
@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<String> keySet()
|
|
||||||
{
|
|
||||||
return Collections.emptySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFull()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCaseInsensitive()
|
|
||||||
{
|
|
||||||
return caseInsensitive;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Integer> builder = new Index.Builder<>();
|
||||||
|
builder.caseSensitive(true);
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
builder.with("/test/group" + i, i);
|
||||||
|
}
|
||||||
|
Index<Integer> 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<Integer> 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,30 +21,32 @@ package org.eclipse.jetty.util;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
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.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.in;
|
import static org.hamcrest.Matchers.in;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
public class TrieTest
|
public class TrieTest
|
||||||
{
|
{
|
||||||
public static Stream<Arguments> implementations()
|
public static Stream<Arguments> implementations()
|
||||||
{
|
{
|
||||||
List<Trie> impls = new ArrayList<>();
|
List<AbstractTrie<Integer>> impls = new ArrayList<>();
|
||||||
|
|
||||||
impls.add(new ArrayTrie<Integer>(128));
|
impls.add(new ArrayTrie<Integer>(128));
|
||||||
impls.add(new TreeTrie<Integer>());
|
impls.add(new ArrayTernaryTrie<Integer>(true, 128));
|
||||||
impls.add(new ArrayTernaryTrie<Integer>(128));
|
impls.add(new ArrayTernaryTrie.Growing<Integer>(true, 128, 128));
|
||||||
|
|
||||||
for (Trie<Integer> trie : impls)
|
for (AbstractTrie<Integer> trie : impls)
|
||||||
{
|
{
|
||||||
trie.put("hello", 1);
|
trie.put("hello", 1);
|
||||||
trie.put("He", 2);
|
trie.put("He", 2);
|
||||||
|
@ -62,26 +64,7 @@ public class TrieTest
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("implementations")
|
@MethodSource("implementations")
|
||||||
public void testOverflow(Trie<Integer> trie) throws Exception
|
public void testKeySet(AbstractTrie<Integer> 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<Integer> trie) throws Exception
|
|
||||||
{
|
{
|
||||||
String[] values = new String[]{
|
String[] values = new String[]{
|
||||||
"hello",
|
"hello",
|
||||||
|
@ -103,7 +86,7 @@ public class TrieTest
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("implementations")
|
@MethodSource("implementations")
|
||||||
public void testGetString(Trie<Integer> trie) throws Exception
|
public void testGetString(AbstractTrie<Integer> trie) throws Exception
|
||||||
{
|
{
|
||||||
assertEquals(1, trie.get("hello").intValue());
|
assertEquals(1, trie.get("hello").intValue());
|
||||||
assertEquals(2, trie.get("He").intValue());
|
assertEquals(2, trie.get("He").intValue());
|
||||||
|
@ -130,7 +113,7 @@ public class TrieTest
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("implementations")
|
@MethodSource("implementations")
|
||||||
public void testGetBuffer(Trie<Integer> trie) throws Exception
|
public void testGetBuffer(AbstractTrie<Integer> trie) throws Exception
|
||||||
{
|
{
|
||||||
assertEquals(1, trie.get(BufferUtil.toBuffer("xhellox"), 1, 5).intValue());
|
assertEquals(1, trie.get(BufferUtil.toBuffer("xhellox"), 1, 5).intValue());
|
||||||
assertEquals(2, trie.get(BufferUtil.toBuffer("xhellox"), 1, 2).intValue());
|
assertEquals(2, trie.get(BufferUtil.toBuffer("xhellox"), 1, 2).intValue());
|
||||||
|
@ -155,7 +138,7 @@ public class TrieTest
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("implementations")
|
@MethodSource("implementations")
|
||||||
public void testGetDirectBuffer(Trie<Integer> trie) throws Exception
|
public void testGetDirectBuffer(AbstractTrie<Integer> trie) throws Exception
|
||||||
{
|
{
|
||||||
assertEquals(1, trie.get(BufferUtil.toDirectBuffer("xhellox"), 1, 5).intValue());
|
assertEquals(1, trie.get(BufferUtil.toDirectBuffer("xhellox"), 1, 5).intValue());
|
||||||
assertEquals(2, trie.get(BufferUtil.toDirectBuffer("xhellox"), 1, 2).intValue());
|
assertEquals(2, trie.get(BufferUtil.toDirectBuffer("xhellox"), 1, 2).intValue());
|
||||||
|
@ -180,7 +163,7 @@ public class TrieTest
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("implementations")
|
@MethodSource("implementations")
|
||||||
public void testGetBestArray(Trie<Integer> trie) throws Exception
|
public void testGetBestArray(AbstractTrie<Integer> trie) throws Exception
|
||||||
{
|
{
|
||||||
assertEquals(1, trie.getBest(StringUtil.getUtf8Bytes("xhelloxxxx"), 1, 8).intValue());
|
assertEquals(1, trie.getBest(StringUtil.getUtf8Bytes("xhelloxxxx"), 1, 8).intValue());
|
||||||
assertEquals(2, trie.getBest(StringUtil.getUtf8Bytes("xhelxoxxxx"), 1, 8).intValue());
|
assertEquals(2, trie.getBest(StringUtil.getUtf8Bytes("xhelxoxxxx"), 1, 8).intValue());
|
||||||
|
@ -198,7 +181,7 @@ public class TrieTest
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("implementations")
|
@MethodSource("implementations")
|
||||||
public void testGetBestBuffer(Trie<Integer> trie) throws Exception
|
public void testGetBestBuffer(AbstractTrie<Integer> trie) throws Exception
|
||||||
{
|
{
|
||||||
assertEquals(1, trie.getBest(BufferUtil.toBuffer("xhelloxxxx"), 1, 8).intValue());
|
assertEquals(1, trie.getBest(BufferUtil.toBuffer("xhelloxxxx"), 1, 8).intValue());
|
||||||
assertEquals(2, trie.getBest(BufferUtil.toBuffer("xhelxoxxxx"), 1, 8).intValue());
|
assertEquals(2, trie.getBest(BufferUtil.toBuffer("xhelxoxxxx"), 1, 8).intValue());
|
||||||
|
@ -219,7 +202,7 @@ public class TrieTest
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("implementations")
|
@MethodSource("implementations")
|
||||||
public void testGetBestDirectBuffer(Trie<Integer> trie) throws Exception
|
public void testGetBestDirectBuffer(AbstractTrie<Integer> trie) throws Exception
|
||||||
{
|
{
|
||||||
assertEquals(1, trie.getBest(BufferUtil.toDirectBuffer("xhelloxxxx"), 1, 8).intValue());
|
assertEquals(1, trie.getBest(BufferUtil.toDirectBuffer("xhelloxxxx"), 1, 8).intValue());
|
||||||
assertEquals(2, trie.getBest(BufferUtil.toDirectBuffer("xhelxoxxxx"), 1, 8).intValue());
|
assertEquals(2, trie.getBest(BufferUtil.toDirectBuffer("xhelxoxxxx"), 1, 8).intValue());
|
||||||
|
@ -240,7 +223,7 @@ public class TrieTest
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("implementations")
|
@MethodSource("implementations")
|
||||||
public void testFull(Trie<Integer> trie) throws Exception
|
public void testFull(AbstractTrie<Integer> trie) throws Exception
|
||||||
{
|
{
|
||||||
if (!(trie instanceof ArrayTrie<?> || trie instanceof ArrayTernaryTrie<?>))
|
if (!(trie instanceof ArrayTrie<?> || trie instanceof ArrayTernaryTrie<?>))
|
||||||
return;
|
return;
|
||||||
|
@ -250,4 +233,33 @@ public class TrieTest
|
||||||
testGetBestArray(trie);
|
testGetBestArray(trie);
|
||||||
testGetBestBuffer(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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,8 @@ import java.util.Set;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
|
||||||
import org.eclipse.jetty.util.IncludeExcludeSet;
|
import org.eclipse.jetty.util.IncludeExcludeSet;
|
||||||
|
import org.eclipse.jetty.util.Index;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.TypeUtil;
|
import org.eclipse.jetty.util.TypeUtil;
|
||||||
import org.eclipse.jetty.util.URIUtil;
|
import org.eclipse.jetty.util.URIUtil;
|
||||||
|
@ -180,7 +180,10 @@ public class ClassMatcher extends AbstractSet<String>
|
||||||
|
|
||||||
public static class ByPackage extends AbstractSet<Entry> implements Predicate<String>
|
public static class ByPackage extends AbstractSet<Entry> implements Predicate<String>
|
||||||
{
|
{
|
||||||
private final ArrayTernaryTrie.Growing<Entry> _entries = new ArrayTernaryTrie.Growing<>(false, 512, 512);
|
private final Index.Mutable<Entry> _entries = new Index.Builder<Entry>()
|
||||||
|
.caseSensitive(true)
|
||||||
|
.mutable()
|
||||||
|
.build();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(String name)
|
public boolean test(String name)
|
||||||
|
@ -383,7 +386,10 @@ public class ClassMatcher extends AbstractSet<String>
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public static class ByModule extends HashSet<Entry> implements Predicate<URI>
|
public static class ByModule extends HashSet<Entry> implements Predicate<URI>
|
||||||
{
|
{
|
||||||
private final ArrayTernaryTrie.Growing<Entry> _entries = new ArrayTernaryTrie.Growing<>(false, 512, 512);
|
private final Index.Mutable<Entry> _entries = new Index.Builder<Entry>()
|
||||||
|
.caseSensitive(true)
|
||||||
|
.mutable()
|
||||||
|
.build();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(URI uri)
|
public boolean test(URI uri)
|
||||||
|
|
|
@ -29,23 +29,20 @@ import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.QuotedCSV;
|
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.StringUtil;
|
||||||
import org.eclipse.jetty.util.Trie;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an Extension Configuration, as seen during the connection Handshake process.
|
* Represents an Extension Configuration, as seen during the connection Handshake process.
|
||||||
*/
|
*/
|
||||||
public class ExtensionConfig
|
public class ExtensionConfig
|
||||||
{
|
{
|
||||||
private static final Trie<ExtensionConfig> CACHE = new ArrayTrie<>(512);
|
private static final Index<ExtensionConfig> CACHE = new Index.Builder<ExtensionConfig>()
|
||||||
|
.caseSensitive(false)
|
||||||
static
|
.with("identity", new ExtensionConfig("identity"))
|
||||||
{
|
.with("permessage-deflate", new ExtensionConfig("permessage-deflate"))
|
||||||
CACHE.put("identity", new ExtensionConfig("identity"));
|
.with("permessage-deflate; client_max_window_bits", new ExtensionConfig("permessage-deflate; client_max_window_bits"))
|
||||||
CACHE.put("permessage-deflate", new ExtensionConfig("permessage-deflate"));
|
.build();
|
||||||
CACHE.put("permessage-deflate; client_max_window_bits", new ExtensionConfig("permessage-deflate; client_max_window_bits"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a single parameterized name.
|
* Parse a single parameterized name.
|
||||||
|
|
Loading…
Reference in New Issue