MutableHttpFields.asImmutable avoids copy (#10651)
* Avoid a copy in MutableHttpFields.asImmutable if the mutable is never mutated again. * reduce instance creations needed for iterations --------- Co-authored-by: Ludovic Orban <lorban@bitronix.be>
This commit is contained in:
parent
df6995e125
commit
ffe80cd1f2
|
@ -470,7 +470,7 @@ public class ContextProvider extends ScanningAppProvider
|
|||
contextPath = "/";
|
||||
}
|
||||
// handle root with virtual host form
|
||||
else if (StringUtil.startsWithIgnoreCase(contextPath, "root-"))
|
||||
else if (StringUtil.asciiStartsWithIgnoreCase(contextPath, "root-"))
|
||||
{
|
||||
int dash = contextPath.indexOf('-');
|
||||
String virtual = contextPath.substring(dash + 1);
|
||||
|
|
|
@ -46,7 +46,7 @@ public final class HttpCompliance implements ComplianceViolation.Mode
|
|||
/**
|
||||
* The HTTP RFC(s) require that field names are case-insensitive, so for example the fields "{@code Content-Type: text/xml}"
|
||||
* and "{@code content-type: text/xml}" are considered equivalent. Jetty has been optimized to take advantage of this by
|
||||
* looking up field names in a case insensitive cache and will by default provide the standard capitalisation of a field
|
||||
* looking up field names in a case-insensitive cache and will by default provide the standard capitalisation of a field
|
||||
* name rather than create a new string with the actual capitalisation received. However, some applications have been
|
||||
* written to expect a specific capitalisation of field, so deployments of such applications must include this violation
|
||||
* in their {@link HttpCompliance} mode to prevent Jetty altering the case of the fields received. Jetty itself will still
|
||||
|
@ -56,8 +56,8 @@ public final class HttpCompliance implements ComplianceViolation.Mode
|
|||
CASE_SENSITIVE_FIELD_NAME("https://tools.ietf.org/html/rfc7230#section-3.2", "Field name is case-insensitive"),
|
||||
|
||||
/**
|
||||
* The HTTP RFC(s) require that method names are case sensitive, so that "{@code Get}" and "{@code GET}" are considered
|
||||
* different methods. Jetty releases prior to 9.4 used a case insensitive cache to match method names, thus this requirement
|
||||
* The HTTP RFC(s) require that method names are case-sensitive, so that "{@code Get}" and "{@code GET}" are considered
|
||||
* different methods. Jetty releases prior to 9.4 used a case-insensitive cache to match method names, thus this requirement
|
||||
* was violated. Deployments which wish to retain this legacy violation can include this violation in the
|
||||
* {@link HttpCompliance} mode.
|
||||
*/
|
||||
|
|
|
@ -194,7 +194,7 @@ public enum HttpHeader
|
|||
|
||||
public boolean is(String s)
|
||||
{
|
||||
return _string.equalsIgnoreCase(s);
|
||||
return StringUtil.asciiEqualsIgnoreCase(_string, s);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -95,7 +95,7 @@ public enum HttpMethod
|
|||
|
||||
public boolean is(String s)
|
||||
{
|
||||
return toString().equalsIgnoreCase(s);
|
||||
return name().equals(s);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -653,22 +653,20 @@ public class HttpParser
|
|||
_length = _string.length();
|
||||
_methodString = takeString();
|
||||
|
||||
if (Violation.CASE_INSENSITIVE_METHOD.isAllowedBy(_complianceMode))
|
||||
HttpMethod method = HttpMethod.CACHE.get(_methodString);
|
||||
if (method != null)
|
||||
{
|
||||
HttpMethod method = HttpMethod.INSENSITIVE_CACHE.get(_methodString);
|
||||
_methodString = method.asString();
|
||||
}
|
||||
else if (Violation.CASE_INSENSITIVE_METHOD.isAllowedBy(_complianceMode))
|
||||
{
|
||||
method = HttpMethod.INSENSITIVE_CACHE.get(_methodString);
|
||||
if (method != null)
|
||||
{
|
||||
if (!method.asString().equals(_methodString))
|
||||
reportComplianceViolation(Violation.CASE_INSENSITIVE_METHOD, _methodString);
|
||||
_methodString = method.asString();
|
||||
reportComplianceViolation(Violation.CASE_INSENSITIVE_METHOD, _methodString);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpMethod method = HttpMethod.CACHE.get(_methodString);
|
||||
if (method != null)
|
||||
_methodString = method.asString();
|
||||
}
|
||||
|
||||
setState(State.SPACE1);
|
||||
break;
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.net.URISyntaxException;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.http.UriCompliance.Violation;
|
||||
import org.eclipse.jetty.util.HostPort;
|
||||
|
@ -283,7 +284,7 @@ public interface HttpURI
|
|||
private final String _fragment;
|
||||
private String _uri;
|
||||
private String _canonicalPath;
|
||||
private final EnumSet<Violation> _violations = EnumSet.noneOf(Violation.class);
|
||||
private Set<Violation> _violations;
|
||||
|
||||
private Immutable(Mutable builder)
|
||||
{
|
||||
|
@ -297,7 +298,8 @@ public interface HttpURI
|
|||
_fragment = builder._fragment;
|
||||
_uri = builder._uri;
|
||||
_canonicalPath = builder._canonicalPath;
|
||||
_violations.addAll(builder._violations);
|
||||
if (builder._violations != null)
|
||||
_violations = Collections.unmodifiableSet(EnumSet.copyOf(builder._violations));
|
||||
}
|
||||
|
||||
private Immutable(String uri)
|
||||
|
@ -470,25 +472,25 @@ public interface HttpURI
|
|||
@Override
|
||||
public boolean isAmbiguous()
|
||||
{
|
||||
return UriCompliance.isAmbiguous(_violations);
|
||||
return _violations != null && UriCompliance.isAmbiguous(_violations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasViolations()
|
||||
{
|
||||
return !_violations.isEmpty();
|
||||
return _violations != null && !_violations.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasViolation(Violation violation)
|
||||
{
|
||||
return _violations.contains(violation);
|
||||
return _violations != null && _violations.contains(violation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Violation> getViolations()
|
||||
{
|
||||
return Collections.unmodifiableCollection(_violations);
|
||||
return _violations == null ? Collections.emptySet() : _violations;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -566,7 +568,7 @@ public interface HttpURI
|
|||
private String _fragment;
|
||||
private String _uri;
|
||||
private String _canonicalPath;
|
||||
private final EnumSet<Violation> _violations = EnumSet.noneOf(Violation.class);
|
||||
private Set<Violation> _violations;
|
||||
private boolean _emptySegment;
|
||||
|
||||
private Mutable()
|
||||
|
@ -709,7 +711,8 @@ public interface HttpURI
|
|||
_uri = null;
|
||||
_canonicalPath = null;
|
||||
_emptySegment = false;
|
||||
_violations.clear();
|
||||
if (_violations != null)
|
||||
_violations.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -844,25 +847,25 @@ public interface HttpURI
|
|||
@Override
|
||||
public boolean isAmbiguous()
|
||||
{
|
||||
return UriCompliance.isAmbiguous(_violations);
|
||||
return _violations != null && UriCompliance.isAmbiguous(_violations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasViolations()
|
||||
{
|
||||
return !_violations.isEmpty();
|
||||
return _violations != null && !_violations.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasViolation(Violation violation)
|
||||
{
|
||||
return _violations.contains(violation);
|
||||
return _violations != null && _violations.contains(violation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Violation> getViolations()
|
||||
{
|
||||
return Collections.unmodifiableCollection(_violations);
|
||||
return _violations == null ? Collections.emptySet() : Collections.unmodifiableCollection(_violations);
|
||||
}
|
||||
|
||||
public Mutable normalize()
|
||||
|
@ -991,7 +994,9 @@ public interface HttpURI
|
|||
_query = uri.getQuery();
|
||||
_uri = null;
|
||||
_canonicalPath = uri.getCanonicalPath();
|
||||
_violations.addAll(uri.getViolations());
|
||||
Collection<Violation> violations = uri.getViolations();
|
||||
if (!violations.isEmpty())
|
||||
_violations = EnumSet.copyOf(violations);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -1265,7 +1270,7 @@ public interface HttpURI
|
|||
{
|
||||
if (encodedCharacters == 2 && c == 'u' && !encodedUtf16)
|
||||
{
|
||||
_violations.add(Violation.UTF16_ENCODINGS);
|
||||
addViolation(Violation.UTF16_ENCODINGS);
|
||||
encodedUtf16 = true;
|
||||
encodedCharacters = 4;
|
||||
continue;
|
||||
|
@ -1281,10 +1286,10 @@ public interface HttpURI
|
|||
// other than as the NUL ASCII byte which we do not wish to allow.
|
||||
throw new IllegalArgumentException("Illegal character in path");
|
||||
case '/':
|
||||
_violations.add(Violation.AMBIGUOUS_PATH_SEPARATOR);
|
||||
addViolation(Violation.AMBIGUOUS_PATH_SEPARATOR);
|
||||
break;
|
||||
case '%':
|
||||
_violations.add(Violation.AMBIGUOUS_PATH_ENCODING);
|
||||
addViolation(Violation.AMBIGUOUS_PATH_ENCODING);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -1448,7 +1453,7 @@ public interface HttpURI
|
|||
private RuntimeException onBadUtf8()
|
||||
{
|
||||
// We just remember the violation and return null so nothing is thrown
|
||||
_violations.add(Violation.BAD_UTF8_ENCODING);
|
||||
addViolation(Violation.BAD_UTF8_ENCODING);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1470,7 +1475,7 @@ public interface HttpURI
|
|||
// So if this method is called for any segment and we have previously
|
||||
// seen an empty segment, then it was ambiguous.
|
||||
if (_emptySegment)
|
||||
_violations.add(Violation.AMBIGUOUS_EMPTY_SEGMENT);
|
||||
addViolation(Violation.AMBIGUOUS_EMPTY_SEGMENT);
|
||||
|
||||
if (end == segment)
|
||||
{
|
||||
|
@ -1481,7 +1486,7 @@ public interface HttpURI
|
|||
// If this empty segment is the first segment then it is ambiguous.
|
||||
if (segment == 0)
|
||||
{
|
||||
_violations.add(Violation.AMBIGUOUS_EMPTY_SEGMENT);
|
||||
addViolation(Violation.AMBIGUOUS_EMPTY_SEGMENT);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1499,11 +1504,19 @@ public interface HttpURI
|
|||
{
|
||||
// The segment is always ambiguous.
|
||||
if (Boolean.TRUE.equals(ambiguous))
|
||||
_violations.add(Violation.AMBIGUOUS_PATH_SEGMENT);
|
||||
addViolation(Violation.AMBIGUOUS_PATH_SEGMENT);
|
||||
// The segment is ambiguous only when followed by a parameter.
|
||||
if (param)
|
||||
_violations.add(Violation.AMBIGUOUS_PATH_PARAMETER);
|
||||
addViolation(Violation.AMBIGUOUS_PATH_PARAMETER);
|
||||
}
|
||||
}
|
||||
|
||||
private void addViolation(Violation violation)
|
||||
{
|
||||
if (_violations == null)
|
||||
_violations = EnumSet.of(violation);
|
||||
else
|
||||
_violations.add(violation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,6 @@ class ImmutableHttpFields implements HttpFields
|
|||
|
||||
protected ImmutableHttpFields(HttpField[] fields, int size)
|
||||
{
|
||||
Objects.requireNonNull(fields);
|
||||
_fields = fields;
|
||||
_size = size;
|
||||
}
|
||||
|
@ -58,8 +57,8 @@ class ImmutableHttpFields implements HttpFields
|
|||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int hash = 0;
|
||||
for (int i = _fields.length; i-- > 0; )
|
||||
int hash = 1993; // prime
|
||||
for (int i = _size; i-- > 0; )
|
||||
{
|
||||
hash ^= _fields[i].hashCode();
|
||||
}
|
||||
|
@ -128,7 +127,7 @@ class ImmutableHttpFields implements HttpFields
|
|||
@Override
|
||||
public HttpField getField(int index)
|
||||
{
|
||||
if (index >= _fields.length)
|
||||
if (index >= _size)
|
||||
throw new NoSuchElementException();
|
||||
return _fields[index];
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ class MutableHttpFields implements HttpFields.Mutable
|
|||
private static final int SIZE_INCREMENT = 4;
|
||||
|
||||
private HttpField[] _fields;
|
||||
private boolean _immutable;
|
||||
private int _size;
|
||||
|
||||
/**
|
||||
|
@ -67,7 +68,21 @@ class MutableHttpFields implements HttpFields.Mutable
|
|||
*/
|
||||
protected MutableHttpFields(HttpFields fields)
|
||||
{
|
||||
add(fields);
|
||||
if (fields instanceof ImmutableHttpFields immutable)
|
||||
{
|
||||
_immutable = true;
|
||||
_fields = immutable._fields;
|
||||
_size = immutable._size;
|
||||
}
|
||||
else if (fields != null)
|
||||
{
|
||||
_fields = new HttpField[fields.size() + SIZE_INCREMENT];
|
||||
add(fields);
|
||||
}
|
||||
else
|
||||
{
|
||||
_fields = new HttpField[INITIAL_SIZE];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,10 +135,11 @@ class MutableHttpFields implements HttpFields.Mutable
|
|||
{
|
||||
if (field != null)
|
||||
{
|
||||
if (_fields == null)
|
||||
_fields = new HttpField[INITIAL_SIZE];
|
||||
if (_size == _fields.length)
|
||||
if (_immutable || _size == _fields.length)
|
||||
{
|
||||
_immutable = false;
|
||||
_fields = Arrays.copyOf(_fields, _size + SIZE_INCREMENT);
|
||||
}
|
||||
_fields[_size++] = field;
|
||||
}
|
||||
return this;
|
||||
|
@ -132,14 +148,15 @@ class MutableHttpFields implements HttpFields.Mutable
|
|||
@Override
|
||||
public Mutable add(HttpFields fields)
|
||||
{
|
||||
if (_fields == null)
|
||||
_fields = new HttpField[fields.size() + SIZE_INCREMENT];
|
||||
else if (_size + fields.size() >= _fields.length)
|
||||
_fields = Arrays.copyOf(_fields, _size + fields.size() + SIZE_INCREMENT);
|
||||
|
||||
if (fields.size() == 0)
|
||||
return this;
|
||||
|
||||
if (_immutable || _size + fields.size() >= _fields.length)
|
||||
{
|
||||
_immutable = false;
|
||||
_fields = Arrays.copyOf(_fields, _size + fields.size() + SIZE_INCREMENT);
|
||||
}
|
||||
|
||||
if (fields instanceof org.eclipse.jetty.http.ImmutableHttpFields immutable)
|
||||
{
|
||||
System.arraycopy(immutable._fields, 0, _fields, _size, immutable._size);
|
||||
|
@ -163,12 +180,27 @@ class MutableHttpFields implements HttpFields.Mutable
|
|||
@Override
|
||||
public HttpFields asImmutable()
|
||||
{
|
||||
return new org.eclipse.jetty.http.ImmutableHttpFields(Arrays.copyOf(_fields, _size));
|
||||
_immutable = true;
|
||||
return new ImmutableHttpFields(_fields, _size);
|
||||
}
|
||||
|
||||
private void copyImmutable()
|
||||
{
|
||||
if (_immutable)
|
||||
{
|
||||
_immutable = false;
|
||||
_fields = Arrays.copyOf(_fields, _fields.length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mutable clear()
|
||||
{
|
||||
if (_immutable)
|
||||
{
|
||||
_fields = new HttpField[_fields.length];
|
||||
_immutable = false;
|
||||
}
|
||||
_size = 0;
|
||||
return this;
|
||||
}
|
||||
|
@ -176,7 +208,7 @@ class MutableHttpFields implements HttpFields.Mutable
|
|||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int hash = 0;
|
||||
int hash = 2099; // prime
|
||||
for (int i = _size; i-- > 0; )
|
||||
{
|
||||
HttpField field = _fields[i];
|
||||
|
@ -211,6 +243,32 @@ class MutableHttpFields implements HttpFields.Mutable
|
|||
return _fields[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getField(HttpHeader header)
|
||||
{
|
||||
// default impl overridden for efficiency
|
||||
for (int i = 0; i < _size; i++)
|
||||
{
|
||||
HttpField f = _fields[i];
|
||||
if (f != null && f.getHeader() == header)
|
||||
return f;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getField(String name)
|
||||
{
|
||||
// default impl overridden for efficiency
|
||||
for (int i = 0; i < _size; i++)
|
||||
{
|
||||
HttpField f = _fields[i];
|
||||
if (f != null && f.is(name))
|
||||
return f;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<HttpField> iterator()
|
||||
{
|
||||
|
@ -243,18 +301,20 @@ class MutableHttpFields implements HttpFields.Mutable
|
|||
@Override
|
||||
public ListIterator<HttpField> listIterator()
|
||||
{
|
||||
return new Listerator(0);
|
||||
return listIterator(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<HttpField> listIterator(int index)
|
||||
{
|
||||
copyImmutable();
|
||||
return new Listerator(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mutable put(HttpField field)
|
||||
{
|
||||
copyImmutable();
|
||||
boolean put = false;
|
||||
|
||||
for (int i = 0; i < _size; i++)
|
||||
|
@ -326,6 +386,7 @@ class MutableHttpFields implements HttpFields.Mutable
|
|||
|
||||
public <T> Mutable computeField(T header, BiFunction<T, List<HttpField>, HttpField> computeFn, BiPredicate<HttpField, T> matcher)
|
||||
{
|
||||
copyImmutable();
|
||||
// Look for first occurrence
|
||||
int first = -1;
|
||||
for (int i = 0; i < _size; i++)
|
||||
|
@ -418,7 +479,18 @@ class MutableHttpFields implements HttpFields.Mutable
|
|||
private void remove(int i)
|
||||
{
|
||||
_size--;
|
||||
System.arraycopy(_fields, i + 1, _fields, i, _size - i);
|
||||
if (_immutable)
|
||||
{
|
||||
_immutable = false;
|
||||
HttpField[] fields = _fields;
|
||||
_fields = new HttpField[fields.length];
|
||||
System.arraycopy(fields, 0, _fields, 0, i);
|
||||
System.arraycopy(fields, i + 1, _fields, i, _size - i);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.arraycopy(_fields, i + 1, _fields, i, _size - i);
|
||||
}
|
||||
_fields[_size] = null;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
package org.eclipse.jetty.http;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
@ -127,12 +128,13 @@ public final class UriCompliance implements ComplianceViolation.Mode
|
|||
}
|
||||
}
|
||||
|
||||
public static final EnumSet<Violation> AMBIGUOUS_VIOLATIONS = EnumSet.of(
|
||||
public static final Set<Violation> NO_VIOLATION = Collections.unmodifiableSet(EnumSet.noneOf(Violation.class));
|
||||
public static final Set<Violation> AMBIGUOUS_VIOLATIONS = Collections.unmodifiableSet(EnumSet.of(
|
||||
Violation.AMBIGUOUS_EMPTY_SEGMENT,
|
||||
Violation.AMBIGUOUS_PATH_ENCODING,
|
||||
Violation.AMBIGUOUS_PATH_PARAMETER,
|
||||
Violation.AMBIGUOUS_PATH_SEGMENT,
|
||||
Violation.AMBIGUOUS_PATH_SEPARATOR);
|
||||
Violation.AMBIGUOUS_PATH_SEPARATOR));
|
||||
|
||||
/**
|
||||
* Compliance mode that exactly follows <a href="https://tools.ietf.org/html/rfc3986">RFC3986</a>,
|
||||
|
@ -143,7 +145,8 @@ public final class UriCompliance implements ComplianceViolation.Mode
|
|||
/**
|
||||
* Compliance mode that allows all unambiguous violations.
|
||||
*/
|
||||
public static final UriCompliance UNAMBIGUOUS = new UriCompliance("UNAMBIGUOUS", complementOf(AMBIGUOUS_VIOLATIONS));
|
||||
public static final UriCompliance UNAMBIGUOUS = new UriCompliance("UNAMBIGUOUS",
|
||||
complementOf(EnumSet.copyOf(AMBIGUOUS_VIOLATIONS)));
|
||||
|
||||
/**
|
||||
* The default compliance mode allows no violations from <a href="https://tools.ietf.org/html/rfc3986">RFC3986</a>
|
||||
|
@ -171,7 +174,7 @@ public final class UriCompliance implements ComplianceViolation.Mode
|
|||
private static final AtomicInteger __custom = new AtomicInteger();
|
||||
private static final List<UriCompliance> KNOWN_MODES = List.of(DEFAULT, LEGACY, RFC3986, UNAMBIGUOUS, UNSAFE);
|
||||
|
||||
public static boolean isAmbiguous(EnumSet<Violation> violations)
|
||||
public static boolean isAmbiguous(Set<Violation> violations)
|
||||
{
|
||||
if (violations.isEmpty())
|
||||
return false;
|
||||
|
@ -277,7 +280,7 @@ public final class UriCompliance implements ComplianceViolation.Mode
|
|||
{
|
||||
Objects.requireNonNull(violations);
|
||||
_name = name;
|
||||
_allowed = unmodifiableSet(violations.isEmpty() ? noneOf(Violation.class) : copyOf(violations));
|
||||
_allowed = violations.isEmpty() ? NO_VIOLATION : unmodifiableSet(copyOf(violations));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.util.ListIterator;
|
|||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -32,6 +33,7 @@ import org.eclipse.jetty.util.BufferUtil;
|
|||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
|
@ -179,6 +181,101 @@ public class HttpFieldsTest
|
|||
assertThrows(NoSuchElementException.class, () -> header.getField(2));
|
||||
}
|
||||
|
||||
public static Stream<Arguments> afterAsImmutable()
|
||||
{
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
(Consumer<HttpFields.Mutable>)m -> m.remove("name0"),
|
||||
(Consumer<HttpFields.Mutable>)m ->
|
||||
{
|
||||
assertThat(m.size(), is(1));
|
||||
assertThat(m.get("name1"), is("value1"));
|
||||
}
|
||||
),
|
||||
Arguments.of(
|
||||
(Consumer<HttpFields.Mutable>)m -> m.remove("name1"),
|
||||
(Consumer<HttpFields.Mutable>)m ->
|
||||
{
|
||||
assertThat(m.size(), is(1));
|
||||
assertThat(m.get("name0"), is("value0"));
|
||||
}
|
||||
),
|
||||
Arguments.of(
|
||||
(Consumer<HttpFields.Mutable>)m ->
|
||||
{
|
||||
ListIterator<HttpField> i = m.listIterator();
|
||||
i.next();
|
||||
i.remove();
|
||||
},
|
||||
(Consumer<HttpFields.Mutable>)m ->
|
||||
{
|
||||
assertThat(m.size(), is(1));
|
||||
assertThat(m.get("name1"), is("value1"));
|
||||
}
|
||||
),
|
||||
Arguments.of(
|
||||
(Consumer<HttpFields.Mutable>)m -> m.remove("name2"),
|
||||
(Consumer<HttpFields.Mutable>)m ->
|
||||
{
|
||||
assertThat(m.size(), is(2));
|
||||
assertThat(m.get("name0"), is("value0"));
|
||||
assertThat(m.get("name1"), is("value1"));
|
||||
}
|
||||
),
|
||||
Arguments.of(
|
||||
(Consumer<HttpFields.Mutable>)m -> m.add("name2", "value2"),
|
||||
(Consumer<HttpFields.Mutable>)m ->
|
||||
{
|
||||
assertThat(m.size(), is(3));
|
||||
assertThat(m.get("name0"), is("value0"));
|
||||
assertThat(m.get("name1"), is("value1"));
|
||||
assertThat(m.get("name2"), is("value2"));
|
||||
}
|
||||
),
|
||||
Arguments.of(
|
||||
(Consumer<HttpFields.Mutable>)m -> m.put("name2", "value2"),
|
||||
(Consumer<HttpFields.Mutable>)m ->
|
||||
{
|
||||
assertThat(m.size(), is(3));
|
||||
assertThat(m.get("name0"), is("value0"));
|
||||
assertThat(m.get("name1"), is("value1"));
|
||||
assertThat(m.get("name2"), is("value2"));
|
||||
}
|
||||
),
|
||||
Arguments.of(
|
||||
(Consumer<HttpFields.Mutable>)m -> m.put("name1", "ONE"),
|
||||
(Consumer<HttpFields.Mutable>)m ->
|
||||
{
|
||||
assertThat(m.size(), is(2));
|
||||
assertThat(m.get("name0"), is("value0"));
|
||||
assertThat(m.get("name1"), is("ONE"));
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("afterAsImmutable")
|
||||
public void testMutationAfterAsImmutable(Consumer<HttpFields.Mutable> mutation, Consumer<HttpFields.Mutable> check)
|
||||
{
|
||||
HttpFields.Mutable mutable = HttpFields.build();
|
||||
HttpFields immutable = mutable
|
||||
.put("name0", "value0")
|
||||
.put("name1", "value1").asImmutable();
|
||||
|
||||
assertThat(immutable.size(), is(2));
|
||||
assertThat(immutable.get("name0"), is("value0"));
|
||||
assertThat(immutable.get("name1"), is("value1"));
|
||||
|
||||
mutation.accept(mutable);
|
||||
|
||||
assertThat(immutable.size(), is(2));
|
||||
assertThat(immutable.get("name0"), is("value0"));
|
||||
assertThat(immutable.get("name1"), is("value1"));
|
||||
|
||||
check.accept(mutable);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("mutables")
|
||||
public void testMutable(HttpFields.Mutable mutable)
|
||||
|
|
|
@ -479,10 +479,10 @@ public class ArrayByteBufferPool implements ByteBufferPool, Dumpable
|
|||
private RetainedBucket(int capacity, int poolSize)
|
||||
{
|
||||
if (poolSize <= ConcurrentPool.OPTIMAL_MAX_SIZE)
|
||||
_pool = new ConcurrentPool<>(ConcurrentPool.StrategyType.THREAD_ID, poolSize, true);
|
||||
_pool = new ConcurrentPool<>(ConcurrentPool.StrategyType.THREAD_ID, poolSize, false);
|
||||
else
|
||||
_pool = new CompoundPool<>(
|
||||
new ConcurrentPool<>(ConcurrentPool.StrategyType.THREAD_ID, ConcurrentPool.OPTIMAL_MAX_SIZE, true),
|
||||
new ConcurrentPool<>(ConcurrentPool.StrategyType.THREAD_ID, ConcurrentPool.OPTIMAL_MAX_SIZE, false),
|
||||
new QueuedPool<>(poolSize - ConcurrentPool.OPTIMAL_MAX_SIZE)
|
||||
);
|
||||
_capacity = capacity;
|
||||
|
|
|
@ -556,8 +556,11 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("updateable {}", _updateable.size());
|
||||
|
||||
for (SelectorUpdate update : _updateable)
|
||||
while (true)
|
||||
{
|
||||
SelectorUpdate update = _updateable.pollFirst();
|
||||
if (update == null)
|
||||
break;
|
||||
if (_selector == null)
|
||||
break;
|
||||
try
|
||||
|
@ -571,7 +574,6 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
|
|||
LOG.warn("Cannot update selector {}", ManagedSelector.this, x);
|
||||
}
|
||||
}
|
||||
_updateable.clear();
|
||||
|
||||
Selector selector;
|
||||
int updates;
|
||||
|
|
|
@ -326,7 +326,7 @@ public class ConnectorServer extends AbstractLifeCycle
|
|||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return _host != null ? _host.hashCode() : 0;
|
||||
return _host != null ? _host.hashCode() : 101;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1123,7 +1123,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ
|
|||
|
||||
protected class HttpStreamOverHTTP1 implements HttpStream
|
||||
{
|
||||
private final String _id;
|
||||
private final long _id;
|
||||
private final String _method;
|
||||
private final HttpURI.Mutable _uri;
|
||||
private final HttpVersion _version;
|
||||
|
@ -1141,10 +1141,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ
|
|||
|
||||
protected HttpStreamOverHTTP1(String method, String uri, HttpVersion version)
|
||||
{
|
||||
_id = Objects.requireNonNull(version).toString() + '#' + _streamIdGenerator.getAndIncrement();
|
||||
_id = _streamIdGenerator.getAndIncrement();
|
||||
_method = method;
|
||||
_uri = uri == null ? null : HttpURI.build(method, uri);
|
||||
_version = version;
|
||||
_version = Objects.requireNonNull(version);
|
||||
|
||||
if (_uri != null && _uri.getPath() == null && _uri.getScheme() != null && _uri.hasAuthority())
|
||||
_uri.path("/");
|
||||
|
@ -1364,7 +1364,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Writ
|
|||
@Override
|
||||
public String getId()
|
||||
{
|
||||
return _id;
|
||||
return Long.toString(_id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -57,6 +57,18 @@ public class ResponseHttpFields implements HttpFields.Mutable
|
|||
_fields.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getField(String name)
|
||||
{
|
||||
return _fields.getField(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getField(HttpHeader header)
|
||||
{
|
||||
return _fields.getField(header);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getField(int index)
|
||||
{
|
||||
|
@ -116,9 +128,9 @@ public class ResponseHttpFields implements HttpFields.Mutable
|
|||
@Override
|
||||
public Iterator<HttpField> iterator()
|
||||
{
|
||||
Iterator<HttpField> i = _fields.iterator();
|
||||
return new Iterator<>()
|
||||
{
|
||||
private final Iterator<HttpField> i = _fields.iterator();
|
||||
private HttpField _current;
|
||||
|
||||
@Override
|
||||
|
|
|
@ -544,7 +544,7 @@ public interface Attributes
|
|||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int hash = 0;
|
||||
int hash = 113;
|
||||
for (String name : getAttributeNameSet())
|
||||
hash += name.hashCode() ^ getAttribute(name).hashCode();
|
||||
return hash;
|
||||
|
|
|
@ -221,7 +221,7 @@ public class ConcurrentPool<P> implements Pool<P>, Dumpable
|
|||
case FIRST -> 0;
|
||||
case RANDOM -> ThreadLocalRandom.current().nextInt(size);
|
||||
case ROUND_ROBIN -> nextIndex.getAndUpdate(c -> Math.max(0, c + 1)) % size;
|
||||
case THREAD_ID -> (int)(Thread.currentThread().getId() % size);
|
||||
case THREAD_ID -> (int)((Thread.currentThread().getId() * 31) % size);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -225,18 +225,27 @@ public class StringUtil
|
|||
return String.valueOf(chars);
|
||||
}
|
||||
|
||||
public static boolean startsWithIgnoreCase(String s, String w)
|
||||
/**
|
||||
* Check for string equality, ignoring {@link StandardCharsets#US_ASCII} case differences.
|
||||
* @param string The string to check
|
||||
* @param other The other string to check
|
||||
* @return true if the strings are equal, ignoring {@link StandardCharsets#US_ASCII} case differences.
|
||||
*/
|
||||
public static boolean asciiEqualsIgnoreCase(String string, String other)
|
||||
{
|
||||
if (w == null)
|
||||
return true;
|
||||
if (string == null)
|
||||
return other == null;
|
||||
|
||||
if (s == null || s.length() < w.length())
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < w.length(); i++)
|
||||
if (string.length() != other.length())
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < string.length(); i++)
|
||||
{
|
||||
char c1 = s.charAt(i);
|
||||
char c2 = w.charAt(i);
|
||||
char c1 = string.charAt(i);
|
||||
char c2 = other.charAt(i);
|
||||
if (c1 != c2)
|
||||
{
|
||||
if (c1 <= 127)
|
||||
|
@ -250,23 +259,86 @@ public class StringUtil
|
|||
return true;
|
||||
}
|
||||
|
||||
public static boolean endsWithIgnoreCase(String s, String w)
|
||||
/**
|
||||
* Check for a string prefix, ignoring {@link StandardCharsets#US_ASCII} case differences.
|
||||
* @param string The string to check
|
||||
* @param prefix The sub string to look for as a prefix
|
||||
* @return true if the string ends with the substring, ignoring {@link StandardCharsets#US_ASCII} case differences.
|
||||
* @deprecated Use {@link #asciiEndsWithIgnoreCase(String, String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean startsWithIgnoreCase(String string, String prefix)
|
||||
{
|
||||
if (w == null)
|
||||
return asciiStartsWithIgnoreCase(string, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a string prefix, ignoring {@link StandardCharsets#US_ASCII} case differences.
|
||||
* @param string The string to check
|
||||
* @param prefix The sub string to look for as a prefix
|
||||
* @return true if the string ends with the substring, ignoring {@link StandardCharsets#US_ASCII} case differences.
|
||||
*/
|
||||
public static boolean asciiStartsWithIgnoreCase(String string, String prefix)
|
||||
{
|
||||
if (isEmpty(prefix))
|
||||
return true;
|
||||
if (s == null)
|
||||
|
||||
if (string == null || string.length() < prefix.length())
|
||||
return false;
|
||||
|
||||
int sl = s.length();
|
||||
int wl = w.length();
|
||||
|
||||
if (sl < wl)
|
||||
return false;
|
||||
|
||||
for (int i = wl; i-- > 0; )
|
||||
for (int i = 0; i < prefix.length(); i++)
|
||||
{
|
||||
char c1 = s.charAt(--sl);
|
||||
char c2 = w.charAt(i);
|
||||
char c1 = string.charAt(i);
|
||||
char c2 = prefix.charAt(i);
|
||||
if (c1 != c2)
|
||||
{
|
||||
if (c1 <= 127)
|
||||
c1 = LOWERCASES[c1];
|
||||
if (c2 <= 127)
|
||||
c2 = LOWERCASES[c2];
|
||||
if (c1 != c2)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a string suffix, ignoring {@link StandardCharsets#US_ASCII} case differences.
|
||||
* @param string The string to check
|
||||
* @param suffix The sub string to look for as a suffix
|
||||
* @return true if the string ends with the substring, ignoring {@link StandardCharsets#US_ASCII} case differences.
|
||||
* @deprecated Use {@link #asciiEndsWithIgnoreCase(String, String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean endsWithIgnoreCase(String string, String suffix)
|
||||
{
|
||||
return asciiEndsWithIgnoreCase(string, suffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a string suffix, ignoring {@link StandardCharsets#US_ASCII} case differences.
|
||||
* @param string The string to check
|
||||
* @param suffix The sub string to look for as a suffix
|
||||
* @return true if the string ends with the substring, ignoring {@link StandardCharsets#US_ASCII} case differences.
|
||||
*/
|
||||
public static boolean asciiEndsWithIgnoreCase(String string, String suffix)
|
||||
{
|
||||
if (isEmpty(suffix))
|
||||
return true;
|
||||
if (string == null)
|
||||
return false;
|
||||
|
||||
int stringLength = string.length();
|
||||
int suffixLength = suffix.length();
|
||||
|
||||
if (stringLength < suffixLength)
|
||||
return false;
|
||||
|
||||
for (int i = suffixLength; i-- > 0; )
|
||||
{
|
||||
char c1 = string.charAt(--stringLength);
|
||||
char c2 = suffix.charAt(i);
|
||||
if (c1 != c2)
|
||||
{
|
||||
if (c1 <= 127)
|
||||
|
|
|
@ -29,55 +29,65 @@ import static org.hamcrest.Matchers.sameInstance;
|
|||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
// @checkstyle-disable-check : AvoidEscapedUnicodeCharactersCheck
|
||||
public class StringUtilTest
|
||||
{
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("ReferenceEquality")
|
||||
public void testAsciiToLowerCase()
|
||||
{
|
||||
String lc = "\u0690bc def 1\u06903";
|
||||
assertEquals(StringUtil.asciiToLowerCase("\u0690Bc DeF 1\u06903"), lc);
|
||||
assertTrue(StringUtil.asciiToLowerCase(lc) == lc);
|
||||
assertSame(StringUtil.asciiToLowerCase(lc), lc);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartsWithIgnoreCase()
|
||||
public void testAsciiEqualsIgnoreCase()
|
||||
{
|
||||
assertTrue(StringUtil.asciiEqualsIgnoreCase(null, null));
|
||||
assertTrue(StringUtil.asciiEqualsIgnoreCase("", ""));
|
||||
assertTrue(StringUtil.asciiEqualsIgnoreCase("AbC", "aBc"));
|
||||
|
||||
assertTrue(StringUtil.startsWithIgnoreCase("\u0690b\u0690defg", "\u0690b\u0690"));
|
||||
assertTrue(StringUtil.startsWithIgnoreCase("\u0690bcdefg", "\u0690bc"));
|
||||
assertTrue(StringUtil.startsWithIgnoreCase("\u0690bcdefg", "\u0690Bc"));
|
||||
assertTrue(StringUtil.startsWithIgnoreCase("\u0690Bcdefg", "\u0690bc"));
|
||||
assertTrue(StringUtil.startsWithIgnoreCase("\u0690Bcdefg", "\u0690Bc"));
|
||||
assertTrue(StringUtil.startsWithIgnoreCase("\u0690bcdefg", ""));
|
||||
assertTrue(StringUtil.startsWithIgnoreCase("\u0690bcdefg", null));
|
||||
assertTrue(StringUtil.startsWithIgnoreCase("\u0690bcdefg", "\u0690bcdefg"));
|
||||
|
||||
assertFalse(StringUtil.startsWithIgnoreCase(null, "xyz"));
|
||||
assertFalse(StringUtil.startsWithIgnoreCase("\u0690bcdefg", "xyz"));
|
||||
assertFalse(StringUtil.startsWithIgnoreCase("\u0690", "xyz"));
|
||||
assertFalse(StringUtil.asciiEqualsIgnoreCase("AbC", "aBcd"));
|
||||
assertFalse(StringUtil.asciiEqualsIgnoreCase("AbCd", "aBc"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEndsWithIgnoreCase()
|
||||
public void testAsciiStartsWithIgnoreCase()
|
||||
{
|
||||
assertTrue(StringUtil.endsWithIgnoreCase("\u0690bcd\u0690f\u0690", "\u0690f\u0690"));
|
||||
assertTrue(StringUtil.endsWithIgnoreCase("\u0690bcdefg", "efg"));
|
||||
assertTrue(StringUtil.endsWithIgnoreCase("\u0690bcdefg", "eFg"));
|
||||
assertTrue(StringUtil.endsWithIgnoreCase("\u0690bcdeFg", "efg"));
|
||||
assertTrue(StringUtil.endsWithIgnoreCase("\u0690bcdeFg", "eFg"));
|
||||
assertTrue(StringUtil.endsWithIgnoreCase("\u0690bcdefg", ""));
|
||||
assertTrue(StringUtil.endsWithIgnoreCase("\u0690bcdefg", null));
|
||||
assertTrue(StringUtil.endsWithIgnoreCase("\u0690bcdefg", "\u0690bcdefg"));
|
||||
assertTrue(StringUtil.asciiStartsWithIgnoreCase("\u0690b\u0690defg", "\u0690b\u0690"));
|
||||
assertTrue(StringUtil.asciiStartsWithIgnoreCase("\u0690bcdefg", "\u0690bc"));
|
||||
assertTrue(StringUtil.asciiStartsWithIgnoreCase("\u0690bcdefg", "\u0690Bc"));
|
||||
assertTrue(StringUtil.asciiStartsWithIgnoreCase("\u0690Bcdefg", "\u0690bc"));
|
||||
assertTrue(StringUtil.asciiStartsWithIgnoreCase("\u0690Bcdefg", "\u0690Bc"));
|
||||
assertTrue(StringUtil.asciiStartsWithIgnoreCase("\u0690bcdefg", ""));
|
||||
assertTrue(StringUtil.asciiStartsWithIgnoreCase("\u0690bcdefg", null));
|
||||
assertTrue(StringUtil.asciiStartsWithIgnoreCase("\u0690bcdefg", "\u0690bcdefg"));
|
||||
|
||||
assertFalse(StringUtil.endsWithIgnoreCase(null, "xyz"));
|
||||
assertFalse(StringUtil.endsWithIgnoreCase("\u0690bcdefg", "xyz"));
|
||||
assertFalse(StringUtil.endsWithIgnoreCase("\u0690", "xyz"));
|
||||
assertFalse(StringUtil.asciiStartsWithIgnoreCase(null, "xyz"));
|
||||
assertFalse(StringUtil.asciiStartsWithIgnoreCase("\u0690bcdefg", "xyz"));
|
||||
assertFalse(StringUtil.asciiStartsWithIgnoreCase("\u0690", "xyz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsciiEndsWithIgnoreCase()
|
||||
{
|
||||
assertTrue(StringUtil.asciiEndsWithIgnoreCase("\u0690bcd\u0690f\u0690", "\u0690f\u0690"));
|
||||
assertTrue(StringUtil.asciiEndsWithIgnoreCase("\u0690bcdefg", "efg"));
|
||||
assertTrue(StringUtil.asciiEndsWithIgnoreCase("\u0690bcdefg", "eFg"));
|
||||
assertTrue(StringUtil.asciiEndsWithIgnoreCase("\u0690bcdeFg", "efg"));
|
||||
assertTrue(StringUtil.asciiEndsWithIgnoreCase("\u0690bcdeFg", "eFg"));
|
||||
assertTrue(StringUtil.asciiEndsWithIgnoreCase("\u0690bcdefg", ""));
|
||||
assertTrue(StringUtil.asciiEndsWithIgnoreCase("\u0690bcdefg", null));
|
||||
assertTrue(StringUtil.asciiEndsWithIgnoreCase("\u0690bcdefg", "\u0690bcdefg"));
|
||||
|
||||
assertFalse(StringUtil.asciiEndsWithIgnoreCase(null, "xyz"));
|
||||
assertFalse(StringUtil.asciiEndsWithIgnoreCase("\u0690bcdefg", "xyz"));
|
||||
assertFalse(StringUtil.asciiEndsWithIgnoreCase("\u0690", "xyz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -88,7 +88,6 @@ public class ServletContextRequest extends ContextRequest implements ServletCont
|
|||
throw new IllegalStateException("could not find %s for %s".formatted(ServletContextRequest.class.getSimpleName(), request));
|
||||
}
|
||||
|
||||
private final List<ServletRequestAttributeListener> _requestAttributeListeners = new ArrayList<>();
|
||||
private final ServletApiRequest _servletApiRequest;
|
||||
private final ServletContextResponse _response;
|
||||
private final MatchedResource<ServletHandler.MappedServlet> _matchedResource;
|
||||
|
@ -96,6 +95,7 @@ public class ServletContextRequest extends ContextRequest implements ServletCont
|
|||
private final String _decodedPathInContext;
|
||||
private final ServletChannel _servletChannel;
|
||||
private final SessionManager _sessionManager;
|
||||
private List<ServletRequestAttributeListener> _requestAttributeListeners;
|
||||
private Charset _queryEncoding;
|
||||
private HttpFields _trailers;
|
||||
private ManagedSession _managedSession;
|
||||
|
@ -389,20 +389,27 @@ public class ServletContextRequest extends ContextRequest implements ServletCont
|
|||
@Override
|
||||
public List<ServletRequestAttributeListener> getRequestAttributeListeners()
|
||||
{
|
||||
if (_requestAttributeListeners == null)
|
||||
_requestAttributeListeners = new ArrayList<>();
|
||||
return _requestAttributeListeners;
|
||||
}
|
||||
|
||||
public void addEventListener(final EventListener listener)
|
||||
public void addEventListener(EventListener listener)
|
||||
{
|
||||
if (listener instanceof ServletRequestAttributeListener)
|
||||
_requestAttributeListeners.add((ServletRequestAttributeListener)listener);
|
||||
if (listener instanceof ServletRequestAttributeListener attributeListener)
|
||||
{
|
||||
if (_requestAttributeListeners == null)
|
||||
_requestAttributeListeners = new ArrayList<>();
|
||||
_requestAttributeListeners.add(attributeListener);
|
||||
}
|
||||
if (listener instanceof AsyncListener)
|
||||
throw new IllegalArgumentException(listener.getClass().toString());
|
||||
}
|
||||
|
||||
public void removeEventListener(final EventListener listener)
|
||||
public void removeEventListener(EventListener listener)
|
||||
{
|
||||
_requestAttributeListeners.remove(listener);
|
||||
if (_requestAttributeListeners != null)
|
||||
_requestAttributeListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -104,7 +104,7 @@ public class JakartaWebSocketExtension implements Extension
|
|||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return name != null ? name.hashCode() : 0;
|
||||
return name != null ? name.hashCode() : 41;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -189,7 +189,7 @@ public class JakartaWebSocketCreator implements WebSocketCreator
|
|||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return (baseConfig != null ? baseConfig.hashCode() : 0);
|
||||
return (baseConfig != null ? baseConfig.hashCode() : 47);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -104,7 +104,7 @@ public class JakartaWebSocketExtension implements Extension
|
|||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return name != null ? name.hashCode() : 0;
|
||||
return name != null ? name.hashCode() : 79;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -189,7 +189,7 @@ public class JakartaWebSocketCreator implements WebSocketCreator
|
|||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return (baseConfig != null ? baseConfig.hashCode() : 0);
|
||||
return (baseConfig != null ? baseConfig.hashCode() : 83);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue