Headers Performance Boost and Interface Simplification

Motivation:
A degradation in performance has been observed from the 4.0 branch as documented in https://github.com/netty/netty/issues/3962.

Modifications:
- Simplify Headers class hierarchy.
- Restore the DefaultHeaders to be based upon DefaultHttpHeaders from 4.0.
- Make various other modifications that are causing hot spots.

Result:
Performance is now on par with 4.0.
This commit is contained in:
Scott Mitchell 2015-08-12 19:05:37 -07:00
parent e6ca9882a1
commit ba6ce5449e
84 changed files with 3193 additions and 4116 deletions

View File

@ -0,0 +1,185 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.ValueConverter;
import io.netty.util.HashingStrategy;
import io.netty.util.internal.StringUtil;
import java.util.Collection;
import java.util.Iterator;
import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER;
import static io.netty.util.internal.StringUtil.COMMA;
/**
* Will add multiple values for the same header as single header with a comma separated list of values.
* <p>
* Please refer to section <a href="https://tools.ietf.org/html/rfc7230#section-3.2.2">RFC 7230, 3.2.2</a>.
*/
public class CombinedHttpHeaders extends DefaultHttpHeaders {
public CombinedHttpHeaders(boolean validate) {
super(new CombinedHttpHeadersImpl(CASE_INSENSITIVE_HASHER, valueConverter(validate), nameValidator(validate)));
}
private static final class CombinedHttpHeadersImpl extends DefaultHeaders<CharSequence> {
/**
* An estimate of the size of a header value.
*/
private static final int VALUE_LENGTH_ESTIMATE = 10;
private CsvValueEscaper<Object> objectEscaper;
private CsvValueEscaper<CharSequence> charSequenceEscaper;
private CsvValueEscaper<Object> objectEscaper() {
if (objectEscaper == null) {
objectEscaper = new CsvValueEscaper<Object>() {
@Override
public CharSequence escape(Object value) {
return StringUtil.escapeCsv(valueConverter().convertObject(value));
}
};
}
return objectEscaper;
}
private CsvValueEscaper<CharSequence> charSequenceEscaper() {
if (charSequenceEscaper == null) {
charSequenceEscaper = new CsvValueEscaper<CharSequence>() {
@Override
public CharSequence escape(CharSequence value) {
return StringUtil.escapeCsv(value);
}
};
}
return charSequenceEscaper;
}
public CombinedHttpHeadersImpl(HashingStrategy<CharSequence> nameHashingStrategy,
ValueConverter<CharSequence> valueConverter,
io.netty.handler.codec.DefaultHeaders.NameValidator<CharSequence> nameValidator) {
super(nameHashingStrategy, valueConverter, nameValidator);
}
@Override
public CombinedHttpHeadersImpl add(CharSequence name, CharSequence value) {
return addEscapedValue(name, StringUtil.escapeCsv(value));
}
@Override
public CombinedHttpHeadersImpl add(CharSequence name, CharSequence... values) {
return addEscapedValue(name, commaSeparate(charSequenceEscaper(), values));
}
@Override
public CombinedHttpHeadersImpl add(CharSequence name, Iterable<? extends CharSequence> values) {
return addEscapedValue(name, commaSeparate(charSequenceEscaper(), values));
}
@Override
public CombinedHttpHeadersImpl addObject(CharSequence name, Iterable<?> values) {
return addEscapedValue(name, commaSeparate(objectEscaper(), values));
}
@Override
public CombinedHttpHeadersImpl addObject(CharSequence name, Object... values) {
return addEscapedValue(name, commaSeparate(objectEscaper(), values));
}
@Override
public CombinedHttpHeadersImpl set(CharSequence name, CharSequence... values) {
super.set(name, commaSeparate(charSequenceEscaper(), values));
return this;
}
@Override
public CombinedHttpHeadersImpl set(CharSequence name, Iterable<? extends CharSequence> values) {
super.set(name, commaSeparate(charSequenceEscaper(), values));
return this;
}
@Override
public CombinedHttpHeadersImpl setObject(CharSequence name, Object... values) {
super.set(name, commaSeparate(objectEscaper(), values));
return this;
}
@Override
public CombinedHttpHeadersImpl setObject(CharSequence name, Iterable<?> values) {
super.set(name, commaSeparate(objectEscaper(), values));
return this;
}
private CombinedHttpHeadersImpl addEscapedValue(CharSequence name, CharSequence escapedValue) {
CharSequence currentValue = super.get(name);
if (currentValue == null) {
super.add(name, escapedValue);
} else {
super.set(name, commaSeparateEscapedValues(currentValue, escapedValue));
}
return this;
}
private static <T> CharSequence commaSeparate(CsvValueEscaper<T> escaper, T... values) {
StringBuilder sb = new StringBuilder(values.length * VALUE_LENGTH_ESTIMATE);
if (values.length > 0) {
int end = values.length - 1;
for (int i = 0; i < end; i++) {
sb.append(escaper.escape(values[i])).append(COMMA);
}
sb.append(escaper.escape(values[end]));
}
return sb;
}
private static <T> CharSequence commaSeparate(CsvValueEscaper<T> escaper, Iterable<? extends T> values) {
@SuppressWarnings("rawtypes")
final StringBuilder sb = values instanceof Collection
? new StringBuilder(((Collection) values).size() * VALUE_LENGTH_ESTIMATE) : new StringBuilder();
Iterator<? extends T> iterator = values.iterator();
if (iterator.hasNext()) {
T next = iterator.next();
while (iterator.hasNext()) {
sb.append(escaper.escape(next)).append(COMMA);
next = iterator.next();
}
sb.append(escaper.escape(next));
}
return sb;
}
private CharSequence commaSeparateEscapedValues(CharSequence currentValue, CharSequence value) {
return new StringBuilder(currentValue.length() + 1 + value.length())
.append(currentValue)
.append(COMMA)
.append(value);
}
/**
* Escapes comma separated values (CSV).
*
* @param <T> The type that a concrete implementation handles
*/
private interface CsvValueEscaper<T> {
/**
* Appends the value to the specified {@link StringBuilder}, escaping if necessary.
*
* @param value the value to be appended, escaped if necessary
*/
CharSequence escape(T value);
}
}
}

View File

@ -15,10 +15,10 @@
*/
package io.netty.handler.codec.http;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
/**
* Default implementation of a {@link FullHttpResponse}.
*/
@ -33,7 +33,7 @@ public class DefaultFullHttpResponse extends DefaultHttpResponse implements Full
}
public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status, ByteBuf content) {
this(version, status, content, false);
this(version, status, content, true);
}
public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status, boolean validateHeaders) {
@ -46,18 +46,16 @@ public class DefaultFullHttpResponse extends DefaultHttpResponse implements Full
}
public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status,
ByteBuf content, boolean singleFieldHeaders) {
this(version, status, content, true, singleFieldHeaders);
ByteBuf content, boolean validateHeaders) {
this(version, status, content, validateHeaders, false);
}
public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status,
ByteBuf content, boolean validateHeaders, boolean singleFieldHeaders) {
super(version, status, validateHeaders, singleFieldHeaders);
if (content == null) {
throw new NullPointerException("content");
}
this.content = content;
trailingHeaders = new DefaultHttpHeaders(validateHeaders, singleFieldHeaders);
this.content = checkNotNull(content, "content");
this.trailingHeaders = singleFieldHeaders ? new CombinedHttpHeaders(validateHeaders)
: new DefaultHttpHeaders(validateHeaders);
this.validateHeaders = validateHeaders;
}

View File

@ -15,11 +15,14 @@
*/
package io.netty.handler.codec.http;
import io.netty.handler.codec.CharSequenceValueConverter;
import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.DefaultTextHeaders;
import io.netty.handler.codec.DefaultTextHeaders.CharSequenceConverter;
import io.netty.handler.codec.TextHeaders;
import io.netty.handler.codec.DefaultHeaders.NameValidator;
import io.netty.handler.codec.HeadersUtils;
import io.netty.handler.codec.ValueConverter;
import io.netty.util.AsciiString;
import io.netty.util.ByteProcessor;
import io.netty.util.internal.PlatformDependent;
import java.util.ArrayList;
import java.util.Calendar;
@ -30,56 +33,58 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER;
import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/**
* Default implementation of {@link HttpHeaders}.
*/
public class DefaultHttpHeaders extends HttpHeaders {
private static final int HIGHEST_INVALID_NAME_CHAR_MASK = ~63;
private static final int HIGHEST_INVALID_VALUE_CHAR_MASK = ~15;
private static final ByteProcessor HEADER_NAME_VALIDATOR = new ByteProcessor() {
@Override
public boolean process(byte value) throws Exception {
validateChar((char) (value & 0xFF));
return true;
}
};
static final NameValidator<CharSequence> HttpNameValidator = new NameValidator<CharSequence>() {
@Override
public void validateName(CharSequence name) {
if (name instanceof AsciiString) {
try {
((AsciiString) name).forEachByte(HEADER_NAME_VALIDATOR);
} catch (Exception e) {
PlatformDependent.throwException(e);
}
} else {
checkNotNull(name, "name");
// Go through each character in the name
for (int index = 0; index < name.length(); ++index) {
validateChar(name.charAt(index));
}
}
}
};
/**
* A look-up table used for checking if a character in a header name is prohibited.
*/
private static final byte[] LOOKUP_TABLE = new byte[~HIGHEST_INVALID_NAME_CHAR_MASK + 1];
static {
LOOKUP_TABLE['\t'] = -1;
LOOKUP_TABLE['\n'] = -1;
LOOKUP_TABLE[0x0b] = -1;
LOOKUP_TABLE['\f'] = -1;
LOOKUP_TABLE[' '] = -1;
LOOKUP_TABLE[','] = -1;
LOOKUP_TABLE[':'] = -1;
LOOKUP_TABLE[';'] = -1;
LOOKUP_TABLE['='] = -1;
}
private final TextHeaders headers;
private final DefaultHeaders<CharSequence> headers;
public DefaultHttpHeaders() {
this(true);
}
public DefaultHttpHeaders(boolean validate) {
this(validate, false);
this(validate, nameValidator(validate));
}
protected DefaultHttpHeaders(boolean validate, boolean singleHeaderFields) {
this(true, validate ? HeaderNameValidator.INSTANCE : DefaultTextHeaders.NO_NAME_VALIDATOR, singleHeaderFields);
protected DefaultHttpHeaders(boolean validate, NameValidator<CharSequence> nameValidator) {
this(new DefaultHeaders<CharSequence>(CASE_INSENSITIVE_HASHER, valueConverter(validate), nameValidator));
}
protected DefaultHttpHeaders(boolean validate,
DefaultHeaders.NameValidator<CharSequence> nameValidator,
boolean singleHeaderFields) {
headers = new DefaultTextHeaders(
new TreeMap<CharSequence, Object>(AsciiString.CHARSEQUENCE_CASE_INSENSITIVE_ORDER),
nameValidator,
validate ? HeaderValueConverterAndValidator.INSTANCE : HeaderValueConverter.INSTANCE,
singleHeaderFields);
protected DefaultHttpHeaders(DefaultHeaders<CharSequence> headers) {
this.headers = headers;
}
@Override
@ -102,24 +107,28 @@ public class DefaultHttpHeaders extends HttpHeaders {
}
}
@Deprecated
@Override
public HttpHeaders add(String name, Object value) {
headers.addObject(name, value);
return this;
}
@Deprecated
@Override
public HttpHeaders add(CharSequence name, Object value) {
headers.addObject(name, value);
return this;
}
@Deprecated
@Override
public HttpHeaders add(String name, Iterable<?> values) {
headers.addObject(name, values);
return this;
}
@Deprecated
@Override
public HttpHeaders add(CharSequence name, Iterable<?> values) {
headers.addObject(name, values);
@ -138,36 +147,42 @@ public class DefaultHttpHeaders extends HttpHeaders {
return this;
}
@Deprecated
@Override
public HttpHeaders remove(String name) {
headers.remove(name);
return this;
}
@Deprecated
@Override
public HttpHeaders remove(CharSequence name) {
headers.remove(name);
return this;
}
@Deprecated
@Override
public HttpHeaders set(String name, Object value) {
headers.setObject(name, value);
return this;
}
@Deprecated
@Override
public HttpHeaders set(CharSequence name, Object value) {
headers.setObject(name, value);
return this;
}
@Deprecated
@Override
public HttpHeaders set(String name, Iterable<?> values) {
headers.setObject(name, values);
return this;
}
@Deprecated
@Override
public HttpHeaders set(CharSequence name, Iterable<?> values) {
headers.setObject(name, values);
@ -192,14 +207,16 @@ public class DefaultHttpHeaders extends HttpHeaders {
return this;
}
@Deprecated
@Override
public String get(String name) {
return headers.getAndConvert(name);
return get((CharSequence) name);
}
@Deprecated
@Override
public String get(CharSequence name) {
return headers.getAndConvert(name);
return HeadersUtils.getAsString(headers, name);
}
@Override
@ -232,16 +249,19 @@ public class DefaultHttpHeaders extends HttpHeaders {
return headers.getTimeMillis(name, defaultValue);
}
@Deprecated
@Override
public List<String> getAll(String name) {
return headers.getAllAndConvert(name);
return getAll((CharSequence) name);
}
@Deprecated
@Override
public List<String> getAll(CharSequence name) {
return headers.getAllAndConvert(name);
return HeadersUtils.getAllAsString(headers, name);
}
@Deprecated
@Override
public List<Entry<String, String>> entries() {
if (isEmpty()) {
@ -255,30 +275,22 @@ public class DefaultHttpHeaders extends HttpHeaders {
return entriesConverted;
}
/**
* @deprecated Use {@link #iteratorCharSequence()}.
*/
@Override
@Deprecated
@Override
public Iterator<Map.Entry<String, String>> iterator() {
return headers.iteratorConverted();
return HeadersUtils.iteratorAsString(headers);
}
/**
* @deprecated Future major releases will have this be the default iterator.
* Get an iterator to traverse over the underlying name/value pairs.
* <p>
* This iterator should be preferred over {@link #iterator()} if {@link AsciiString} objects are used.
*/
@Override
@Deprecated
@Override
public Iterator<Entry<CharSequence, CharSequence>> iteratorCharSequence() {
return headers.iterator();
}
@Deprecated
@Override
public boolean contains(String name) {
return headers.contains(name);
return contains((CharSequence) name);
}
@Override
@ -298,88 +310,90 @@ public class DefaultHttpHeaders extends HttpHeaders {
@Override
public boolean contains(String name, String value, boolean ignoreCase) {
return headers.contains(name, value, ignoreCase);
return contains((CharSequence) name, (CharSequence) value, ignoreCase);
}
@Override
public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) {
return headers.contains(name, value, ignoreCase);
return headers.contains(name, value, ignoreCase ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER);
}
@Deprecated
@Override
public Set<String> names() {
return HeadersUtils.namesAsString(headers);
}
@Override
public Set<String> names() {
return headers.namesAndConvert(String.CASE_INSENSITIVE_ORDER);
public boolean equals(Object o) {
if (!(o instanceof DefaultHttpHeaders)) {
return false;
}
return headers.equals(((DefaultHttpHeaders) o).headers, CASE_SENSITIVE_HASHER);
}
@Override
public int hashCode() {
return headers.size();
return headers.hashCode(CASE_SENSITIVE_HASHER);
}
@Override
public boolean equals(Object other) {
if (!(other instanceof DefaultHttpHeaders)) {
return false;
}
DefaultHttpHeaders headers = (DefaultHttpHeaders) other;
return DefaultHeaders.comparatorEquals(this.headers, headers.headers,
AsciiString.CHARSEQUENCE_CASE_SENSITIVE_ORDER);
}
static final class HeaderNameValidator implements DefaultHeaders.NameValidator<CharSequence> {
public static final HeaderNameValidator INSTANCE = new HeaderNameValidator();
private HeaderNameValidator() {
}
@Override
public void validate(CharSequence name) {
// Go through each character in the name
for (int index = 0; index < name.length(); index++) {
char character = name.charAt(index);
// Check to see if the character is not an ASCII character
if (character > 127) {
throw new IllegalArgumentException("a header name cannot contain non-ASCII characters: " + name);
}
// Check for prohibited characters.
if ((character & HIGHEST_INVALID_NAME_CHAR_MASK) == 0 && LOOKUP_TABLE[character] != 0) {
throw new IllegalArgumentException(
"a header name cannot contain the following prohibited characters: =,;: \\t\\r\\n\\v\\f: " +
name);
}
private static void validateChar(char character) {
switch (character) {
case '\t':
case '\n':
case 0x0b:
case '\f':
case '\r':
case ' ':
case ',':
case ':':
case ';':
case '=':
throw new IllegalArgumentException(
"a header name cannot contain the following prohibited characters: =,;: \\t\\r\\n\\v\\f: " +
character);
default:
// Check to see if the character is not an ASCII character, or invalid
if (character > 127) {
throw new IllegalArgumentException("a header name cannot contain non-ASCII character: " +
character);
}
}
}
private static class HeaderValueConverter extends CharSequenceConverter {
static ValueConverter<CharSequence> valueConverter(boolean validate) {
return validate ? HeaderValueConverterAndValidator.INSTANCE : HeaderValueConverter.INSTANCE;
}
public static final HeaderValueConverter INSTANCE = new HeaderValueConverter();
@SuppressWarnings("unchecked")
static NameValidator<CharSequence> nameValidator(boolean validate) {
return validate ? HttpNameValidator : NameValidator.NOT_NULL;
}
private static class HeaderValueConverter extends CharSequenceValueConverter {
static final HeaderValueConverter INSTANCE = new HeaderValueConverter();
@Override
public CharSequence convertObject(Object value) {
checkNotNull(value, "value");
CharSequence seq;
if (value instanceof CharSequence) {
seq = (CharSequence) value;
} else if (value instanceof Number) {
seq = value.toString();
} else if (value instanceof Date) {
seq = HttpHeaderDateFormat.get().format((Date) value);
} else if (value instanceof Calendar) {
seq = HttpHeaderDateFormat.get().format(((Calendar) value).getTime());
} else {
seq = value.toString();
return (CharSequence) value;
}
return seq;
if (value instanceof Number) {
return value.toString();
}
if (value instanceof Date) {
return HttpHeaderDateFormat.get().format((Date) value);
}
if (value instanceof Calendar) {
return HttpHeaderDateFormat.get().format(((Calendar) value).getTime());
}
return value.toString();
}
}
private static final class HeaderValueConverterAndValidator extends HeaderValueConverter {
public static final HeaderValueConverterAndValidator INSTANCE = new HeaderValueConverterAndValidator();
static final HeaderValueConverterAndValidator INSTANCE = new HeaderValueConverterAndValidator();
@Override
public CharSequence convertObject(Object value) {

View File

@ -15,6 +15,8 @@
*/
package io.netty.handler.codec.http;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/**
* The default {@link HttpMessage} implementation.
*/
@ -33,12 +35,10 @@ public abstract class DefaultHttpMessage extends DefaultHttpObject implements Ht
/**
* Creates a new instance.
*/
protected DefaultHttpMessage(final HttpVersion version, boolean validateHeaders, boolean singleHeaderFields) {
if (version == null) {
throw new NullPointerException("version");
}
this.version = version;
headers = new DefaultHttpHeaders(validateHeaders, singleHeaderFields);
protected DefaultHttpMessage(final HttpVersion version, boolean validateHeaders, boolean singleFieldHeaders) {
this.version = checkNotNull(version, "version");
headers = singleFieldHeaders ? new CombinedHttpHeaders(validateHeaders)
: new DefaultHttpHeaders(validateHeaders);
}
@Override

View File

@ -15,6 +15,8 @@
*/
package io.netty.handler.codec.http;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/**
* The default {@link HttpRequest} implementation.
*/
@ -44,14 +46,8 @@ public class DefaultHttpRequest extends DefaultHttpMessage implements HttpReques
*/
public DefaultHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, boolean validateHeaders) {
super(httpVersion, validateHeaders, false);
if (method == null) {
throw new NullPointerException("method");
}
if (uri == null) {
throw new NullPointerException("uri");
}
this.method = method;
this.uri = uri;
this.method = checkNotNull(method, "method");
this.uri = checkNotNull(uri, "uri");
}
@Override

View File

@ -50,12 +50,15 @@ public class DefaultHttpResponse extends DefaultHttpMessage implements HttpRespo
* @param version the HTTP version of this response
* @param status the getStatus of this response
* @param validateHeaders validate the header names and values when adding them to the {@link HttpHeaders}
* @param singleHeaderFields determines if HTTP headers with multiple values should be added as a single
* field or as multiple header fields.
* @param singleFieldHeaders {@code true} to check and enforce that headers with the same name are appended
* to the same entry and comma separated.
* See <a href="https://tools.ietf.org/html/rfc7230#section-3.2.2">RFC 7230, 3.2.2</a>.
* {@code false} to allow multiple header entries with the same name to
* coexist.
*/
public DefaultHttpResponse(HttpVersion version, HttpResponseStatus status, boolean validateHeaders,
boolean singleHeaderFields) {
super(version, validateHeaders, singleHeaderFields);
boolean singleFieldHeaders) {
super(version, validateHeaders, singleFieldHeaders);
if (status == null) {
throw new NullPointerException("status");
}

View File

@ -17,8 +17,7 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.DefaultTextHeaders;
import io.netty.handler.codec.DefaultHeaders.NameValidator;
import io.netty.util.internal.StringUtil;
import java.util.Map.Entry;
@ -27,7 +26,6 @@ import java.util.Map.Entry;
* The default {@link LastHttpContent} implementation.
*/
public class DefaultLastHttpContent extends DefaultHttpContent implements LastHttpContent {
private final HttpHeaders trailingHeaders;
private final boolean validateHeaders;
@ -109,26 +107,21 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
}
private static final class TrailingHttpHeaders extends DefaultHttpHeaders {
private static final class TrailingHttpHeadersNameValidator implements
DefaultHeaders.NameValidator<CharSequence> {
private static final TrailingHttpHeadersNameValidator INSTANCE = new TrailingHttpHeadersNameValidator();
private static final NameValidator<CharSequence> TrailerNameValidator = new NameValidator<CharSequence>() {
@Override
public void validate(CharSequence name) {
HeaderNameValidator.INSTANCE.validate(name);
if (HttpHeaderNames.CONTENT_LENGTH.equalsIgnoreCase(name)
|| HttpHeaderNames.TRANSFER_ENCODING.equalsIgnoreCase(name)
|| HttpHeaderNames.TRAILER.equalsIgnoreCase(name)) {
public void validateName(CharSequence name) {
DefaultHttpHeaders.HttpNameValidator.validateName(name);
if (HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(name)
|| HttpHeaderNames.TRANSFER_ENCODING.contentEqualsIgnoreCase(name)
|| HttpHeaderNames.TRAILER.contentEqualsIgnoreCase(name)) {
throw new IllegalArgumentException("prohibited trailing header: " + name);
}
}
}
};
@SuppressWarnings({ "unchecked" })
TrailingHttpHeaders(boolean validate) {
super(validate,
validate ? TrailingHttpHeadersNameValidator.INSTANCE : DefaultTextHeaders.NO_NAME_VALIDATOR,
false);
super(validate, validate ? TrailerNameValidator : NameValidator.NOT_NULL);
}
}
}

View File

@ -0,0 +1,158 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
public class EmptyHttpHeaders extends HttpHeaders {
static final Iterator<Entry<CharSequence, CharSequence>> EMPTY_CHARS_ITERATOR =
Collections.<Entry<CharSequence, CharSequence>>emptyList().iterator();
public static final EmptyHttpHeaders INSTANCE = new EmptyHttpHeaders();
protected EmptyHttpHeaders() {
}
@Override
public String get(String name) {
return null;
}
@Override
public Integer getInt(CharSequence name) {
return null;
}
@Override
public int getInt(CharSequence name, int defaultValue) {
return defaultValue;
}
@Override
public Short getShort(CharSequence name) {
return null;
}
@Override
public short getShort(CharSequence name, short defaultValue) {
return defaultValue;
}
@Override
public Long getTimeMillis(CharSequence name) {
return null;
}
@Override
public long getTimeMillis(CharSequence name, long defaultValue) {
return defaultValue;
}
@Override
public List<String> getAll(String name) {
return Collections.emptyList();
}
@Override
public List<Entry<String, String>> entries() {
return Collections.emptyList();
}
@Override
public boolean contains(String name) {
return false;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public int size() {
return 0;
}
@Override
public Set<String> names() {
return Collections.emptySet();
}
@Override
public HttpHeaders add(String name, Object value) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders add(String name, Iterable<?> values) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders addInt(CharSequence name, int value) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders addShort(CharSequence name, short value) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders set(String name, Object value) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders set(String name, Iterable<?> values) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders setInt(CharSequence name, int value) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders setShort(CharSequence name, short value) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders remove(String name) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders clear() {
throw new UnsupportedOperationException("read only");
}
@Override
public Iterator<Entry<String, String>> iterator() {
return entries().iterator();
}
@Override
public Iterator<Entry<CharSequence, CharSequence>> iteratorCharSequence() {
return EMPTY_CHARS_ITERATOR;
}
}

View File

@ -220,7 +220,7 @@ public class HttpClientUpgradeHandler extends HttpObjectAggregator implements Ch
throw new IllegalStateException(
"Switching Protocols response missing UPGRADE header");
}
if (!AsciiString.equalsIgnoreCase(upgradeCodec.protocol(), upgradeHeader)) {
if (!AsciiString.contentEqualsIgnoreCase(upgradeCodec.protocol(), upgradeHeader)) {
throw new IllegalStateException(
"Switching Protocols response with unexpected UPGRADE protocol: "
+ upgradeHeader);

View File

@ -96,7 +96,7 @@ public class HttpContentCompressor extends HttpContentEncoder {
protected Result beginEncode(HttpResponse headers, String acceptEncoding) throws Exception {
String contentEncoding = headers.headers().get(HttpHeaderNames.CONTENT_ENCODING);
if (contentEncoding != null &&
!HttpHeaderValues.IDENTITY.equalsIgnoreCase(contentEncoding)) {
!HttpHeaderValues.IDENTITY.contentEqualsIgnoreCase(contentEncoding)) {
return null;
}

View File

@ -15,6 +15,10 @@
*/
package io.netty.handler.codec.http;
import static io.netty.handler.codec.http.HttpHeaderValues.DEFLATE;
import static io.netty.handler.codec.http.HttpHeaderValues.GZIP;
import static io.netty.handler.codec.http.HttpHeaderValues.X_DEFLATE;
import static io.netty.handler.codec.http.HttpHeaderValues.X_GZIP;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.compression.ZlibCodecFactory;
import io.netty.handler.codec.compression.ZlibWrapper;
@ -47,16 +51,13 @@ public class HttpContentDecompressor extends HttpContentDecoder {
@Override
protected EmbeddedChannel newContentDecoder(String contentEncoding) throws Exception {
if ("gzip".equalsIgnoreCase(contentEncoding) || "x-gzip".equalsIgnoreCase(contentEncoding)) {
if (GZIP.contentEqualsIgnoreCase(contentEncoding) ||
X_GZIP.contentEqualsIgnoreCase(contentEncoding)) {
return new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP));
}
if ("deflate".equalsIgnoreCase(contentEncoding) || "x-deflate".equalsIgnoreCase(contentEncoding)) {
ZlibWrapper wrapper;
if (strict) {
wrapper = ZlibWrapper.ZLIB;
} else {
wrapper = ZlibWrapper.ZLIB_OR_NONE;
}
if (DEFLATE.contentEqualsIgnoreCase(contentEncoding) ||
X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) {
final ZlibWrapper wrapper = strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE;
// To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly.
return new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(wrapper));
}

View File

@ -32,14 +32,14 @@ public final class HttpHeaderUtil {
*/
public static boolean isKeepAlive(HttpMessage message) {
CharSequence connection = message.headers().get(HttpHeaderNames.CONNECTION);
if (connection != null && HttpHeaderValues.CLOSE.equalsIgnoreCase(connection)) {
if (connection != null && HttpHeaderValues.CLOSE.contentEqualsIgnoreCase(connection)) {
return false;
}
if (message.protocolVersion().isKeepAliveDefault()) {
return !HttpHeaderValues.CLOSE.equalsIgnoreCase(connection);
return !HttpHeaderValues.CLOSE.contentEqualsIgnoreCase(connection);
} else {
return HttpHeaderValues.KEEP_ALIVE.equalsIgnoreCase(connection);
return HttpHeaderValues.KEEP_ALIVE.contentEqualsIgnoreCase(connection);
}
}
@ -193,7 +193,7 @@ public final class HttpHeaderUtil {
if (value == null) {
return false;
}
if (HttpHeaderValues.CONTINUE.equalsIgnoreCase(value)) {
if (HttpHeaderValues.CONTINUE.contentEqualsIgnoreCase(value)) {
return true;
}
@ -231,7 +231,6 @@ public final class HttpHeaderUtil {
m.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED);
m.headers().remove(HttpHeaderNames.CONTENT_LENGTH);
} else {
// Make a copy to be able to modify values while iterating
List<String> encodings = m.headers().getAll(HttpHeaderNames.TRANSFER_ENCODING);
if (encodings.isEmpty()) {
return;
@ -240,7 +239,7 @@ public final class HttpHeaderUtil {
Iterator<CharSequence> valuesIt = values.iterator();
while (valuesIt.hasNext()) {
CharSequence value = valuesIt.next();
if (HttpHeaderValues.CHUNKED.equalsIgnoreCase(value)) {
if (HttpHeaderValues.CHUNKED.contentEqualsIgnoreCase(value)) {
valuesIt.remove();
}
}

View File

@ -17,11 +17,11 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.handler.codec.Headers;
import io.netty.util.AsciiString;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
@ -36,135 +36,11 @@ import static io.netty.util.internal.ObjectUtil.checkNotNull;
* commonly used utility methods that accesses an {@link HttpMessage}.
*/
public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>> {
static final Iterator<Entry<CharSequence, CharSequence>> EMPTY_CHARS_ITERATOR =
Collections.<Entry<CharSequence, CharSequence>>emptyList().iterator();
public static final HttpHeaders EMPTY_HEADERS = new HttpHeaders() {
@Override
public String get(String name) {
return null;
}
@Override
public Integer getInt(CharSequence name) {
return null;
}
@Override
public int getInt(CharSequence name, int defaultValue) {
return defaultValue;
}
@Override
public Short getShort(CharSequence name) {
return null;
}
@Override
public short getShort(CharSequence name, short defaultValue) {
return defaultValue;
}
@Override
public Long getTimeMillis(CharSequence name) {
return null;
}
@Override
public long getTimeMillis(CharSequence name, long defaultValue) {
return defaultValue;
}
@Override
public List<String> getAll(String name) {
return Collections.emptyList();
}
@Override
public List<Entry<String, String>> entries() {
return Collections.emptyList();
}
@Override
public boolean contains(String name) {
return false;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public int size() {
return 0;
}
@Override
public Set<String> names() {
return Collections.emptySet();
}
@Override
public HttpHeaders add(String name, Object value) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders add(String name, Iterable<?> values) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders addInt(CharSequence name, int value) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders addShort(CharSequence name, short value) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders set(String name, Object value) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders set(String name, Iterable<?> values) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders setInt(CharSequence name, int value) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders setShort(CharSequence name, short value) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders remove(String name) {
throw new UnsupportedOperationException("read only");
}
@Override
public HttpHeaders clear() {
throw new UnsupportedOperationException("read only");
}
@Override
public Iterator<Entry<String, String>> iterator() {
return entries().iterator();
}
@Override
public Iterator<Entry<CharSequence, CharSequence>> iteratorCharSequence() {
return EMPTY_CHARS_ITERATOR;
}
};
/**
* @deprecated Use {@link EmptyHttpHeaders#INSTANCE}.
*/
@Deprecated
public static final HttpHeaders EMPTY_HEADERS = EmptyHttpHeaders.INSTANCE;
/**
* @deprecated Use {@link HttpHeaderNames} instead.
@ -1261,7 +1137,7 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
*/
@Deprecated
public static boolean equalsIgnoreCase(CharSequence name1, CharSequence name2) {
return AsciiString.equalsIgnoreCase(name1, name2);
return AsciiString.contentEqualsIgnoreCase(name1, name2);
}
static void encode(HttpHeaders headers, ByteBuf buf) throws Exception {
@ -1305,28 +1181,36 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
protected HttpHeaders() { }
/**
* @deprecated Use {@link #get(CharSequence)}
* @see {@link #get(CharSequence)}
*/
@Deprecated
public abstract String get(String name);
/**
* @deprecated Use {@link #getAsString(CharSequence)}
* <p>
* Returns the value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name The name of the header to search
* @return The first header value or {@code null} if there is no such header
*/
@Deprecated
public String get(CharSequence name) {
return get(name.toString());
}
/**
* @deprecated Future releases will use {@link CharSequence} instead of {@link String}.
* <p>
* Returns the value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name The name of the header to search
* @return The first header value or {@code defaultValue} if there is no such header
*/
@Deprecated
public String get(CharSequence name, String defaultValue) {
String value = get(name);
if (value == null) {
@ -1401,39 +1285,55 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
/**
* @see {@link #getAll(CharSequence)}
*/
@Deprecated
public abstract List<String> getAll(String name);
/**
* @deprecated Use {@link #getAllAsString(CharSequence)}
* <p>
* Returns the values of headers with the specified name
*
* @param name The name of the headers to search
* @return A {@link List} of header values which will be empty if no values
* are found
*/
@Deprecated
public List<String> getAll(CharSequence name) {
return getAll(name.toString());
}
/**
* @deprecated Use {@link #iteratorCharSequence()}
* <p>
* Returns a new {@link List} that contains all headers in this object. Note that modifying the
* returned {@link List} will not affect the state of this object. If you intend to enumerate over the header
* entries only, use {@link #iterator()} instead, which has much less overhead.
*/
@Deprecated
public abstract List<Map.Entry<String, String>> entries();
/**
* @deprecated Use {@link #contains(CharSequence)}
* <p>
* @see {@link #contains(CharSequence)}
*/
@Deprecated
public abstract boolean contains(String name);
/**
* @deprecated Use {@link #iteratorCharSequence()}.
* @deprecated It is preferred to use {@link #iteratorCharSequence()} unless you need {@link String}.
* If {@link String} is required then use {@link #iteratorAsString()}.
* <p>
* {@inheritDoc}
*/
@Override
@Deprecated
public abstract Iterator<Entry<String, String>> iterator();
/**
* @deprecated In future releases this method will be renamed to {@code iterator()}.
* @return Iterator over the name/value header pairs.
*/
@Deprecated
public abstract Iterator<Entry<CharSequence, CharSequence>> iteratorCharSequence();
@ -1458,18 +1358,24 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
public abstract int size();
/**
* @deprecated Future releases will return a {@link Set} of type {@link CharSequence}.
* <p>
* Returns a new {@link Set} that contains the names of all headers in this object. Note that modifying the
* returned {@link Set} will not affect the state of this object. If you intend to enumerate over the header
* entries only, use {@link #iterator()} instead, which has much less overhead.
*/
@Deprecated
public abstract Set<String> names();
/**
* @see {@link #add(CharSequence, Object)}
* @deprecated Use {@link #add(CharSequence, Object)}
*/
@Deprecated
public abstract HttpHeaders add(String name, Object value);
/**
* @deprecated Future releases will have a {@code addObject} method.
* <p>
* Adds a new header with the specified name and value.
*
* If the specified value is not a {@link String}, it is converted
@ -1482,16 +1388,20 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
*
* @return {@code this}
*/
@Deprecated
public HttpHeaders add(CharSequence name, Object value) {
return add(name.toString(), value);
}
/**
* @see {@link #add(CharSequence, Iterable)}
* @deprecated Use {@link #add(CharSequence, Iterable)}
*/
@Deprecated
public abstract HttpHeaders add(String name, Iterable<?> values);
/**
* @deprecated Future release will have an {@code addObject(...)}.
* <p>
* Adds a new header with the specified name and values.
*
* This getMethod can be represented approximately as the following code:
@ -1508,6 +1418,7 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
* @param values The values of the headers being set
* @return {@code this}
*/
@Deprecated
public HttpHeaders add(CharSequence name, Iterable<?> values) {
return add(name.toString(), values);
}
@ -1544,11 +1455,14 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
public abstract HttpHeaders addShort(CharSequence name, short value);
/**
* @see {@link #set(CharSequence, Object)}
* @deprecated Use {@link #set(CharSequence, Object)}
*/
@Deprecated
public abstract HttpHeaders set(String name, Object value);
/**
* @deprecated Future release will have a {@code setObject(...)}.
* <p>
* Sets a header with the specified name and value.
*
* If there is an existing header with the same name, it is removed.
@ -1561,16 +1475,20 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
* @param value The value of the header being set
* @return {@code this}
*/
@Deprecated
public HttpHeaders set(CharSequence name, Object value) {
return set(name.toString(), value);
}
/**
* @see {@link #set(CharSequence, Iterable)}
* @deprecated {@link #set(CharSequence, Iterable)}
*/
@Deprecated
public abstract HttpHeaders set(String name, Iterable<?> values);
/**
* @deprecated Future release will have a {@code setObject(...)}.
* <p>
* Sets a header with the specified name and values.
*
* If there is an existing header with the same name, it is removed.
@ -1589,6 +1507,7 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
* @param values The values of the headers being set
* @return {@code this}
*/
@Deprecated
public HttpHeaders set(CharSequence name, Iterable<?> values) {
return set(name.toString(), values);
}
@ -1649,16 +1568,20 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
public abstract HttpHeaders setShort(CharSequence name, short value);
/**
* @see {@link #remove(CharSequence)}
* @deprecated {@link #remove(CharSequence)}
*/
@Deprecated
public abstract HttpHeaders remove(String name);
/**
* @deprecated Future releases the signature will change.
* <p>
* Removes the header with the specified name.
*
* @param name The name of the header to remove
* @return {@code this}
*/
@Deprecated
public HttpHeaders remove(CharSequence name) {
return remove(name.toString());
}
@ -1694,12 +1617,38 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
}
/**
* Returns {@code true} if a header with the name and value exists.
*
* @param name the headername
* @param value the value
* @param ignoreCase {@code true} if case should be ignored
* @return contains {@code true} if it contains it {@code false} otherwise
* {@link Headers#get(Object)} and convert the result to a {@link String}.
* @param name the name of the header to retrieve
* @return the first header value if the header is found. {@code null} if there's no such header.
*/
public final String getAsString(CharSequence name) {
return get(name);
}
/**
* {@link Headers#getAll(Object)} and convert each element of {@link List} to a {@link String}.
* @param name the name of the header to retrieve
* @return a {@link List} of header values or an empty {@link List} if no values are found.
*/
public final List<String> getAllAsString(CharSequence name) {
return getAll(name);
}
/**
* {@link Iterator} that converts each {@link Entry}'s key and value to a {@link String}.
*/
public final Iterator<Entry<String, String>> iteratorAsString() {
return iterator();
}
/**
* Returns {@code true} if a header with the {@code name} and {@code value} exists, {@code false} otherwise.
* <p>
* If {@code ignoreCase} is {@code true} then a case insensitive compare is done on the value.
* @param name the name of the header to find
* @param value the value of the header to find
* @param ignoreCase {@code true} then a case insensitive compare is run to compare values.
* otherwise a case sensitive compare is run to compare values.
*/
public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) {
return contains(name.toString(), value.toString(), ignoreCase);

View File

@ -21,6 +21,8 @@ import io.netty.util.CharsetUtil;
import java.util.HashMap;
import java.util.Map;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/**
* The request getMethod of HTTP or its derived protocols, such as
* <a href="http://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol">RTSP</a> and
@ -86,8 +88,7 @@ public class HttpMethod implements Comparable<HttpMethod> {
*/
public static final HttpMethod CONNECT = new HttpMethod("CONNECT", true);
private static final Map<String, HttpMethod> methodMap =
new HashMap<String, HttpMethod>();
private static final Map<String, HttpMethod> methodMap = new HashMap<String, HttpMethod>();
static {
methodMap.put(OPTIONS.toString(), OPTIONS);
@ -107,21 +108,8 @@ public class HttpMethod implements Comparable<HttpMethod> {
* will be returned. Otherwise, a new instance will be returned.
*/
public static HttpMethod valueOf(String name) {
if (name == null) {
throw new NullPointerException("name");
}
name = name.trim();
if (name.isEmpty()) {
throw new IllegalArgumentException("empty name");
}
HttpMethod result = methodMap.get(name);
if (result != null) {
return result;
} else {
return new HttpMethod(name);
}
return result != null ? result : new HttpMethod(name);
}
private final String name;
@ -143,7 +131,7 @@ public class HttpMethod implements Comparable<HttpMethod> {
throw new NullPointerException("name");
}
name = name.trim();
name = checkNotNull(name, "name").trim();
if (name.isEmpty()) {
throw new IllegalArgumentException("empty name");
}

View File

@ -625,9 +625,9 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
} else {
splitHeader(line);
CharSequence headerName = name;
if (!HttpHeaderNames.CONTENT_LENGTH.equalsIgnoreCase(headerName) &&
!HttpHeaderNames.TRANSFER_ENCODING.equalsIgnoreCase(headerName) &&
!HttpHeaderNames.TRAILER.equalsIgnoreCase(headerName)) {
if (!HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(headerName) &&
!HttpHeaderNames.TRANSFER_ENCODING.contentEqualsIgnoreCase(headerName) &&
!HttpHeaderNames.TRAILER.contentEqualsIgnoreCase(headerName)) {
trailer.trailingHeaders().add(headerName, value);
}
lastHeader = name;

View File

@ -689,13 +689,13 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
return null;
}
String[] contents = splitMultipartHeader(newline);
if (HttpHeaderNames.CONTENT_DISPOSITION.equalsIgnoreCase(contents[0])) {
if (HttpHeaderNames.CONTENT_DISPOSITION.contentEqualsIgnoreCase(contents[0])) {
boolean checkSecondArg;
if (currentStatus == MultiPartStatus.DISPOSITION) {
checkSecondArg = HttpHeaderValues.FORM_DATA.equalsIgnoreCase(contents[1]);
checkSecondArg = HttpHeaderValues.FORM_DATA.contentEqualsIgnoreCase(contents[1]);
} else {
checkSecondArg = HttpHeaderValues.ATTACHMENT.equalsIgnoreCase(contents[1])
|| HttpHeaderValues.FILE.equalsIgnoreCase(contents[1]);
checkSecondArg = HttpHeaderValues.ATTACHMENT.contentEqualsIgnoreCase(contents[1])
|| HttpHeaderValues.FILE.contentEqualsIgnoreCase(contents[1]);
}
if (checkSecondArg) {
// read next values and store them in the map as Attribute
@ -723,7 +723,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
currentFieldAttributes.put(attribute.getName(), attribute);
}
}
} else if (HttpHeaderNames.CONTENT_TRANSFER_ENCODING.equalsIgnoreCase(contents[0])) {
} else if (HttpHeaderNames.CONTENT_TRANSFER_ENCODING.contentEqualsIgnoreCase(contents[0])) {
Attribute attribute;
try {
attribute = factory.createAttribute(request, HttpHeaderNames.CONTENT_TRANSFER_ENCODING.toString(),
@ -733,8 +733,9 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
} catch (IllegalArgumentException e) {
throw new ErrorDataDecoderException(e);
}
currentFieldAttributes.put(HttpHeaderNames.CONTENT_TRANSFER_ENCODING, attribute);
} else if (HttpHeaderNames.CONTENT_LENGTH.equalsIgnoreCase(contents[0])) {
} else if (HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(contents[0])) {
Attribute attribute;
try {
attribute = factory.createAttribute(request, HttpHeaderNames.CONTENT_LENGTH.toString(),
@ -744,10 +745,11 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
} catch (IllegalArgumentException e) {
throw new ErrorDataDecoderException(e);
}
currentFieldAttributes.put(HttpHeaderNames.CONTENT_LENGTH, attribute);
} else if (HttpHeaderNames.CONTENT_TYPE.equalsIgnoreCase(contents[0])) {
} else if (HttpHeaderNames.CONTENT_TYPE.contentEqualsIgnoreCase(contents[0])) {
// Take care of possible "multipart/mixed"
if (HttpHeaderValues.MULTIPART_MIXED.equalsIgnoreCase(contents[1])) {
if (HttpHeaderValues.MULTIPART_MIXED.contentEqualsIgnoreCase(contents[1])) {
if (currentStatus == MultiPartStatus.DISPOSITION) {
String values = StringUtil.substringAfter(contents[2], '=');
multipartMixedBoundary = "--" + values;

View File

@ -753,7 +753,7 @@ public class HttpPostRequestEncoder implements ChunkedInput<HttpContent> {
if (transferEncoding != null) {
headers.remove(HttpHeaderNames.TRANSFER_ENCODING);
for (CharSequence v : transferEncoding) {
if (HttpHeaderValues.CHUNKED.equalsIgnoreCase(v)) {
if (HttpHeaderValues.CHUNKED.contentEqualsIgnoreCase(v)) {
// ignore
} else {
headers.add(HttpHeaderNames.TRANSFER_ENCODING, v);

View File

@ -201,13 +201,13 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
HttpHeaders headers = response.headers();
CharSequence upgrade = headers.get(HttpHeaderNames.UPGRADE);
if (!WEBSOCKET.equalsIgnoreCase(upgrade)) {
if (!WEBSOCKET.contentEqualsIgnoreCase(upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
+ upgrade);
}
CharSequence connection = headers.get(HttpHeaderNames.CONNECTION);
if (!HttpHeaderValues.UPGRADE.equalsIgnoreCase(connection)) {
if (!HttpHeaderValues.UPGRADE.contentEqualsIgnoreCase(connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: "
+ connection);
}

View File

@ -206,12 +206,12 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
}
CharSequence upgrade = headers.get(HttpHeaderNames.UPGRADE);
if (!HttpHeaderValues.WEBSOCKET.equalsIgnoreCase(upgrade)) {
if (!HttpHeaderValues.WEBSOCKET.contentEqualsIgnoreCase(upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade);
}
CharSequence connection = headers.get(HttpHeaderNames.CONNECTION);
if (!HttpHeaderValues.UPGRADE.equalsIgnoreCase(connection)) {
if (!HttpHeaderValues.UPGRADE.contentEqualsIgnoreCase(connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection);
}

View File

@ -207,12 +207,12 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
}
CharSequence upgrade = headers.get(HttpHeaderNames.UPGRADE);
if (!HttpHeaderValues.WEBSOCKET.equalsIgnoreCase(upgrade)) {
if (!HttpHeaderValues.WEBSOCKET.contentEqualsIgnoreCase(upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade);
}
CharSequence connection = headers.get(HttpHeaderNames.CONNECTION);
if (!HttpHeaderValues.UPGRADE.equalsIgnoreCase(connection)) {
if (!HttpHeaderValues.UPGRADE.contentEqualsIgnoreCase(connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection);
}

View File

@ -217,12 +217,12 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
}
CharSequence upgrade = headers.get(HttpHeaderNames.UPGRADE);
if (!HttpHeaderValues.WEBSOCKET.equalsIgnoreCase(upgrade)) {
if (!HttpHeaderValues.WEBSOCKET.contentEqualsIgnoreCase(upgrade)) {
throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade);
}
CharSequence connection = headers.get(HttpHeaderNames.CONNECTION);
if (!HttpHeaderValues.UPGRADE.equalsIgnoreCase(connection)) {
if (!HttpHeaderValues.UPGRADE.contentEqualsIgnoreCase(connection)) {
throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection);
}

View File

@ -30,7 +30,7 @@ import io.netty.handler.codec.http.HttpResponseStatus;
import java.util.regex.Pattern;
import static io.netty.handler.codec.http.HttpVersion.*;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
/**
* <p>
@ -107,8 +107,8 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
protected FullHttpResponse newHandshakeResponse(FullHttpRequest req, HttpHeaders headers) {
// Serve the WebSocket handshake request.
if (!HttpHeaderValues.UPGRADE.equalsIgnoreCase(req.headers().get(HttpHeaderNames.CONNECTION))
|| !HttpHeaderValues.WEBSOCKET.equalsIgnoreCase(req.headers().get(HttpHeaderNames.UPGRADE))) {
if (!HttpHeaderValues.UPGRADE.contentEqualsIgnoreCase(req.headers().get(HttpHeaderNames.CONNECTION))
|| !HttpHeaderValues.WEBSOCKET.contentEqualsIgnoreCase(req.headers().get(HttpHeaderNames.UPGRADE))) {
throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade");
}
@ -131,6 +131,7 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
// New handshake getMethod with a challenge:
res.headers().add(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, req.headers().get(HttpHeaderNames.ORIGIN));
res.headers().add(HttpHeaderNames.SEC_WEBSOCKET_LOCATION, uri());
String subprotocols = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL);
if (subprotocols != null) {
String selectedSubprotocol = selectSubprotocol(subprotocols);
@ -160,6 +161,7 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
// Old Hixie 75 handshake getMethod with no challenge:
res.headers().add(HttpHeaderNames.WEBSOCKET_ORIGIN, req.headers().get(HttpHeaderNames.ORIGIN));
res.headers().add(HttpHeaderNames.WEBSOCKET_LOCATION, uri());
String protocol = req.headers().get(HttpHeaderNames.WEBSOCKET_PROTOCOL);
if (protocol != null) {
res.headers().add(HttpHeaderNames.WEBSOCKET_PROTOCOL, selectSubprotocol(protocol));

View File

@ -142,6 +142,7 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker {
res.headers().add(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET);
res.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE);
res.headers().add(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT, accept);
String subprotocols = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL);
if (subprotocols != null) {
String selectedSubprotocol = selectSubprotocol(subprotocols);

View File

@ -141,6 +141,7 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
res.headers().add(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET);
res.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE);
res.headers().add(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT, accept);
String subprotocols = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL);
if (subprotocols != null) {
String selectedSubprotocol = selectSubprotocol(subprotocols);

View File

@ -139,6 +139,7 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
res.headers().add(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET);
res.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE);
res.headers().add(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT, accept);
String subprotocols = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL);
if (subprotocols != null) {
String selectedSubprotocol = selectSubprotocol(subprotocols);

View File

@ -15,18 +15,28 @@
*/
package io.netty.handler.codec.spdy;
import io.netty.handler.codec.DefaultTextHeaders;
import io.netty.handler.codec.TextHeaders;
import io.netty.handler.codec.CharSequenceValueConverter;
import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.Headers;
import io.netty.handler.codec.HeadersUtils;
import java.util.LinkedHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeaders {
import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER;
import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER;
public class DefaultSpdyHeaders extends DefaultHeaders<CharSequence> implements SpdyHeaders {
private static final NameValidator<CharSequence> SpydNameValidator = new NameValidator<CharSequence>() {
@Override
public void validateName(CharSequence name) {
SpdyCodecUtil.validateHeaderName(name);
}
};
public DefaultSpdyHeaders() {
super(new LinkedHashMap<CharSequence, Object>(),
HeaderNameValidator.INSTANCE,
HeaderValueConverterAndValidator.INSTANCE,
false);
super(CASE_INSENSITIVE_HASHER, HeaderValueConverterAndValidator.INSTANCE, SpydNameValidator);
}
@Override
@ -120,7 +130,7 @@ public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeader
}
@Override
public SpdyHeaders add(TextHeaders headers) {
public SpdyHeaders add(Headers<? extends CharSequence> headers) {
super.add(headers);
return this;
}
@ -216,13 +226,13 @@ public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeader
}
@Override
public SpdyHeaders set(TextHeaders headers) {
public SpdyHeaders set(Headers<? extends CharSequence> headers) {
super.set(headers);
return this;
}
@Override
public SpdyHeaders setAll(TextHeaders headers) {
public SpdyHeaders setAll(Headers<? extends CharSequence> headers) {
super.setAll(headers);
return this;
}
@ -233,23 +243,38 @@ public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeader
return this;
}
private static class HeaderNameValidator implements NameValidator<CharSequence> {
public static final HeaderNameValidator INSTANCE = new HeaderNameValidator();
@Override
public void validate(CharSequence name) {
SpdyCodecUtil.validateHeaderName(name);
}
@Override
public String getAsString(CharSequence name) {
return HeadersUtils.getAsString(this, name);
}
private static class HeaderValueConverterAndValidator extends CharSequenceConverter {
@Override
public List<String> getAllAsString(CharSequence name) {
return HeadersUtils.getAllAsString(this, name);
}
@Override
public Iterator<Entry<String, String>> iteratorAsString() {
return HeadersUtils.iteratorAsString(this);
}
@Override
public boolean contains(CharSequence name, CharSequence value) {
return contains(name, value, false);
}
@Override
public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) {
return contains(name, value,
ignoreCase ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER);
}
private static final class HeaderValueConverterAndValidator extends CharSequenceValueConverter {
public static final HeaderValueConverterAndValidator INSTANCE = new HeaderValueConverterAndValidator();
@Override
public CharSequence convertObject(Object value) {
CharSequence seq;
final CharSequence seq;
if (value instanceof CharSequence) {
seq = (CharSequence) value;
} else {

View File

@ -15,14 +15,18 @@
*/
package io.netty.handler.codec.spdy;
import io.netty.handler.codec.TextHeaders;
import io.netty.handler.codec.Headers;
import io.netty.util.AsciiString;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
/**
* Provides the constants for the standard SPDY HTTP header names and commonly
* used utility methods that access a {@link SpdyHeadersFrame}.
*/
public interface SpdyHeaders extends TextHeaders {
public interface SpdyHeaders extends Headers<CharSequence> {
/**
* SPDY HTTP header names
@ -102,7 +106,7 @@ public interface SpdyHeaders extends TextHeaders {
SpdyHeaders addTimeMillis(CharSequence name, long value);
@Override
SpdyHeaders add(TextHeaders headers);
SpdyHeaders add(Headers<? extends CharSequence> headers);
@Override
SpdyHeaders set(CharSequence name, CharSequence value);
@ -150,11 +154,41 @@ public interface SpdyHeaders extends TextHeaders {
SpdyHeaders setObject(CharSequence name, Object... values);
@Override
SpdyHeaders set(TextHeaders headers);
SpdyHeaders set(Headers<? extends CharSequence> headers);
@Override
SpdyHeaders setAll(TextHeaders headers);
SpdyHeaders setAll(Headers<? extends CharSequence> headers);
@Override
SpdyHeaders clear();
/**
* {@link Headers#get(Object)} and convert the result to a {@link String}.
* @param name the name of the header to retrieve
* @return the first header value if the header is found. {@code null} if there's no such header.
*/
String getAsString(CharSequence name);
/**
* {@link Headers#getAll(Object)} and convert each element of {@link List} to a {@link String}.
* @param name the name of the header to retrieve
* @return a {@link List} of header values or an empty {@link List} if no values are found.
*/
List<String> getAllAsString(CharSequence name);
/**
* {@link #iterator()} that converts each {@link Entry}'s key and value to a {@link String}.
*/
Iterator<Entry<String, String>> iteratorAsString();
/**
* Returns {@code true} if a header with the {@code name} and {@code value} exists, {@code false} otherwise.
* <p>
* If {@code ignoreCase} is {@code true} then a case insensitive compare is done on the value.
* @param name the name of the header to find
* @param value the value of the header to find
* @param ignoreCase {@code true} then a case insensitive compare is run to compare values.
* otherwise a case sensitive compare is run to compare values.
*/
boolean contains(CharSequence name, CharSequence value, boolean ignoreCase);
}

View File

@ -350,9 +350,9 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
throws Exception {
// Create the first line of the request from the name/value pairs
SpdyHeaders headers = requestFrame.headers();
HttpMethod method = HttpMethod.valueOf(headers.getAndConvert(METHOD));
String url = headers.getAndConvert(PATH);
HttpVersion httpVersion = HttpVersion.valueOf(headers.getAndConvert(VERSION));
HttpMethod method = HttpMethod.valueOf(headers.getAsString(METHOD));
String url = headers.getAsString(PATH);
HttpVersion httpVersion = HttpVersion.valueOf(headers.getAsString(VERSION));
headers.remove(METHOD);
headers.remove(PATH);
headers.remove(VERSION);
@ -386,7 +386,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
// Create the first line of the response from the name/value pairs
SpdyHeaders headers = responseFrame.headers();
HttpResponseStatus status = HttpResponseStatus.parseLine(headers.get(STATUS));
HttpVersion version = HttpVersion.valueOf(headers.getAndConvert(VERSION));
HttpVersion version = HttpVersion.valueOf(headers.getAsString(VERSION));
headers.remove(STATUS);
headers.remove(VERSION);

View File

@ -0,0 +1,201 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
import io.netty.handler.codec.http.HttpHeadersTestUtils.HeaderValue;
import org.junit.Test;
import java.util.Collections;
import static io.netty.util.AsciiString.contentEquals;
import static org.junit.Assert.assertTrue;
public class CombinedHttpHeadersTest {
private static final String HEADER_NAME = "testHeader";
@Test
public void addCharSequencesCsv() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asList());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void addCharSequencesCsvWithExistingHeader() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asList());
headers.add(HEADER_NAME, HeaderValue.FIVE.subset(4));
assertCsvValues(headers, HeaderValue.FIVE);
}
@Test
public void addCharSequencesCsvWithValueContainingComma() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.add(HEADER_NAME, HeaderValue.SIX_QUOTED.subset(4));
assertTrue(contentEquals(HeaderValue.SIX_QUOTED.subsetAsCsvString(4), headers.get(HEADER_NAME)));
assertTrue(contentEquals(HeaderValue.SIX_QUOTED.subsetAsCsvString(4), headers.getAll(HEADER_NAME).get(0)));
}
@Test
public void addCharSequencesCsvWithValueContainingCommas() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.add(HEADER_NAME, HeaderValue.EIGHT.subset(6));
assertTrue(contentEquals(HeaderValue.EIGHT.subsetAsCsvString(6), headers.get(HEADER_NAME)));
assertTrue(contentEquals(HeaderValue.EIGHT.subsetAsCsvString(6), headers.getAll(HEADER_NAME).get(0)));
}
@Test (expected = NullPointerException.class)
public void addCharSequencesCsvNullValue() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
final String value = null;
headers.add(HEADER_NAME, value);
}
@Test
public void addCharSequencesCsvMultipleTimes() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
for (int i = 0; i < 5; ++i) {
headers.add(HEADER_NAME, "value");
}
assertTrue(contentEquals("value,value,value,value,value", headers.get(HEADER_NAME)));
}
@Test
public void addCharSequenceCsv() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
addValues(headers, HeaderValue.ONE, HeaderValue.TWO, HeaderValue.THREE);
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void addCharSequenceCsvSingleValue() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
addValues(headers, HeaderValue.ONE);
assertCsvValue(headers, HeaderValue.ONE);
}
@Test
public void addIterableCsv() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asList());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void addIterableCsvWithExistingHeader() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asList());
headers.add(HEADER_NAME, HeaderValue.FIVE.subset(4));
assertCsvValues(headers, HeaderValue.FIVE);
}
@Test
public void addIterableCsvSingleValue() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.add(HEADER_NAME, HeaderValue.ONE.asList());
assertCsvValue(headers, HeaderValue.ONE);
}
@Test
public void addIterableCsvEmtpy() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.add(HEADER_NAME, Collections.<CharSequence>emptyList());
assertTrue(contentEquals("", headers.getAll(HEADER_NAME).get(0)));
}
@Test
public void addObjectCsv() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
addObjectValues(headers, HeaderValue.ONE, HeaderValue.TWO, HeaderValue.THREE);
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void addObjectsCsv() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asList());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void addObjectsIterableCsv() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asList());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void addObjectsCsvWithExistingHeader() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asList());
headers.add(HEADER_NAME, HeaderValue.FIVE.subset(4));
assertCsvValues(headers, HeaderValue.FIVE);
}
@Test
public void setCharSequenceCsv() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.set(HEADER_NAME, HeaderValue.THREE.asList());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void setIterableCsv() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.set(HEADER_NAME, HeaderValue.THREE.asList());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void setObjectObjectsCsv() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.set(HEADER_NAME, HeaderValue.THREE.asList());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void setObjectIterableCsv() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.set(HEADER_NAME, HeaderValue.THREE.asList());
assertCsvValues(headers, HeaderValue.THREE);
}
private static CombinedHttpHeaders newCombinedHttpHeaders() {
return new CombinedHttpHeaders(true);
}
private static void assertCsvValues(final CombinedHttpHeaders headers, final HeaderValue headerValue) {
assertTrue(contentEquals(headerValue.asCsv(), headers.get(HEADER_NAME)));
assertTrue(contentEquals(headerValue.asCsv(), headers.getAll(HEADER_NAME).get(0)));
}
private static void assertCsvValue(final CombinedHttpHeaders headers, final HeaderValue headerValue) {
assertTrue(contentEquals(headerValue.toString(), headers.get(HEADER_NAME)));
assertTrue(contentEquals(headerValue.toString(), headers.getAll(HEADER_NAME).get(0)));
}
private static void addValues(final CombinedHttpHeaders headers, HeaderValue... headerValues) {
for (HeaderValue v: headerValues) {
headers.add(HEADER_NAME, v.toString());
}
}
private static void addObjectValues(final CombinedHttpHeaders headers, HeaderValue... headerValues) {
for (HeaderValue v: headerValues) {
headers.add(HEADER_NAME, v.toString());
}
}
}

View File

@ -15,15 +15,24 @@
*/
package io.netty.handler.codec.http;
import io.netty.handler.codec.http.HttpHeadersTestUtils.HeaderValue;
import io.netty.util.AsciiString;
import org.junit.Test;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import static io.netty.util.AsciiString.contentEquals;
import static java.util.Arrays.asList;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class DefaultHttpHeadersTest {
private static final String HEADER_NAME = "testHeader";
@Test
public void keysShouldBeCaseInsensitive() {
@ -57,4 +66,113 @@ public class DefaultHttpHeadersTest {
assertEquals(headers2, headers1);
assertEquals(headers1.hashCode(), headers2.hashCode());
}
@Test
public void testRemoveTransferEncodingIgnoreCase() {
HttpMessage message = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
message.headers().set(HttpHeaderNames.TRANSFER_ENCODING, "Chunked");
assertFalse(message.headers().isEmpty());
HttpHeaderUtil.setTransferEncodingChunked(message, false);
assertTrue(message.headers().isEmpty());
}
// Test for https://github.com/netty/netty/issues/1690
@Test
public void testGetOperations() {
HttpHeaders headers = new DefaultHttpHeaders();
headers.add("Foo", "1");
headers.add("Foo", "2");
assertEquals("1", headers.get("Foo"));
List<String> values = headers.getAll("Foo");
assertEquals(2, values.size());
assertEquals("1", values.get(0));
assertEquals("2", values.get(1));
}
@Test
public void testEquansIgnoreCase() {
assertThat(AsciiString.contentEqualsIgnoreCase(null, null), is(true));
assertThat(AsciiString.contentEqualsIgnoreCase(null, "foo"), is(false));
assertThat(AsciiString.contentEqualsIgnoreCase("bar", null), is(false));
assertThat(AsciiString.contentEqualsIgnoreCase("FoO", "fOo"), is(true));
}
@Test(expected = NullPointerException.class)
public void testSetNullHeaderValueValidate() {
HttpHeaders headers = new DefaultHttpHeaders(true);
headers.set("test", (CharSequence) null);
}
@Test(expected = NullPointerException.class)
public void testSetNullHeaderValueNotValidate() {
HttpHeaders headers = new DefaultHttpHeaders(false);
headers.set("test", (CharSequence) null);
}
@Test
public void addCharSequences() {
final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asList());
assertDefaultValues(headers, HeaderValue.THREE);
}
@Test
public void addIterable() {
final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asList());
assertDefaultValues(headers, HeaderValue.THREE);
}
@Test
public void addObjects() {
final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asList());
assertDefaultValues(headers, HeaderValue.THREE);
}
@Test
public void setCharSequences() {
final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders();
headers.set(HEADER_NAME, HeaderValue.THREE.asList());
assertDefaultValues(headers, HeaderValue.THREE);
}
@Test
public void setIterable() {
final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders();
headers.set(HEADER_NAME, HeaderValue.THREE.asList());
assertDefaultValues(headers, HeaderValue.THREE);
}
@Test
public void setObjectObjects() {
final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders();
headers.set(HEADER_NAME, HeaderValue.THREE.asList());
assertDefaultValues(headers, HeaderValue.THREE);
}
@Test
public void setObjectIterable() {
final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders();
headers.set(HEADER_NAME, HeaderValue.THREE.asList());
assertDefaultValues(headers, HeaderValue.THREE);
}
private static void assertDefaultValues(final DefaultHttpHeaders headers, final HeaderValue headerValue) {
assertTrue(contentEquals(headerValue.asList().get(0), headers.get(HEADER_NAME)));
List<CharSequence> expected = headerValue.asList();
List<String> actual = headers.getAll(HEADER_NAME);
assertEquals(expected.size(), actual.size());
Iterator<CharSequence> eItr = expected.iterator();
Iterator<String> aItr = actual.iterator();
while (eItr.hasNext()) {
assertTrue(contentEquals(eItr.next(), aItr.next()));
}
}
private static DefaultHttpHeaders newDefaultDefaultHttpHeaders() {
return new DefaultHttpHeaders(true);
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
import io.netty.util.AsciiString;
import org.junit.Test;
import java.util.List;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
public class HttpHeaderUtilTest {
@Test
public void testRemoveTransferEncodingIgnoreCase() {
HttpMessage message = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
message.headers().set(HttpHeaderNames.TRANSFER_ENCODING, "Chunked");
assertFalse(message.headers().isEmpty());
HttpHeaderUtil.setTransferEncodingChunked(message, false);
assertTrue(message.headers().isEmpty());
}
// Test for https://github.com/netty/netty/issues/1690
@Test
public void testGetOperations() {
HttpHeaders headers = new DefaultHttpHeaders();
headers.add("Foo", "1");
headers.add("Foo", "2");
assertEquals("1", headers.get("Foo"));
List<String> values = headers.getAll("Foo");
assertEquals(2, values.size());
assertEquals("1", values.get(0));
assertEquals("2", values.get(1));
}
@Test
public void testEquansIgnoreCase() {
assertThat(AsciiString.contentEqualsIgnoreCase(null, null), is(true));
assertThat(AsciiString.contentEqualsIgnoreCase(null, "foo"), is(false));
assertThat(AsciiString.contentEqualsIgnoreCase("bar", null), is(false));
assertThat(AsciiString.contentEqualsIgnoreCase("FoO", "fOo"), is(true));
}
}

View File

@ -52,10 +52,10 @@ public class HttpHeadersTest {
@Test
public void testEquansIgnoreCase() {
assertThat(AsciiString.equalsIgnoreCase(null, null), is(true));
assertThat(AsciiString.equalsIgnoreCase(null, "foo"), is(false));
assertThat(AsciiString.equalsIgnoreCase("bar", null), is(false));
assertThat(AsciiString.equalsIgnoreCase("FoO", "fOo"), is(true));
assertThat(AsciiString.contentEqualsIgnoreCase(null, null), is(true));
assertThat(AsciiString.contentEqualsIgnoreCase(null, "foo"), is(false));
assertThat(AsciiString.contentEqualsIgnoreCase("bar", null), is(false));
assertThat(AsciiString.contentEqualsIgnoreCase("FoO", "fOo"), is(true));
}
@Test(expected = NullPointerException.class)

View File

@ -0,0 +1,130 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static io.netty.util.internal.StringUtil.COMMA;
import static io.netty.util.internal.StringUtil.DOUBLE_QUOTE;
class HttpHeadersTestUtils {
public enum HeaderValue {
UNKNOWN("Unknown", 0),
ONE("One", 1),
TWO("Two", 2),
THREE("Three", 3),
FOUR("Four", 4),
FIVE("Five", 5),
SIX_QUOTED("Six,", 6),
SEVEN_QUOTED("Seven; , GMT", 7),
EIGHT("Eight", 8);
private final int nr;
private final String value;
private List<CharSequence> array;
HeaderValue(final String value, final int nr) {
this.nr = nr;
this.value = value;
}
@Override
public String toString() {
return value;
}
public List<CharSequence> asList() {
if (array == null) {
List<CharSequence> list = new ArrayList<CharSequence>(nr);
for (int i = 1; i <= nr; i++) {
list.add(of(i).toString());
}
array = list;
}
return array;
}
public List<CharSequence> subset(int from) {
assert from > 0;
--from;
final int size = nr - from;
final int end = from + size;
List<CharSequence> list = new ArrayList<CharSequence>(size);
List<CharSequence> fullList = asList();
for (int i = from; i < end; ++i) {
list.add(fullList.get(i));
}
return list;
}
public String subsetAsCsvString(final int from) {
final List<CharSequence> subset = subset(from);
return asCsv(subset);
}
public String asCsv(final List<CharSequence> arr) {
if (arr == null || arr.isEmpty()) {
return "";
}
final StringBuilder sb = new StringBuilder(arr.size() * 10);
final int end = arr.size() - 1;
for (int i = 0; i < end; ++i) {
quoted(sb, arr.get(i)).append(COMMA);
}
quoted(sb, arr.get(end));
return sb.toString();
}
public CharSequence asCsv() {
return asCsv(asList());
}
private static StringBuilder quoted(final StringBuilder sb, final CharSequence value) {
if (contains(value, COMMA) && !contains(value, DOUBLE_QUOTE)) {
return sb.append(DOUBLE_QUOTE).append(value).append(DOUBLE_QUOTE);
}
return sb.append(value);
}
private static boolean contains(CharSequence value, char c) {
for (int i = 0; i < value.length(); ++i) {
if (value.charAt(i) == c) {
return true;
}
}
return false;
}
private static final Map<Integer, HeaderValue> MAP;
static {
final Map<Integer, HeaderValue> map = new HashMap<Integer, HeaderValue>();
for (HeaderValue v : values()) {
final int nr = v.nr;
map.put(Integer.valueOf(nr), v);
}
MAP = map;
}
public static HeaderValue of(final int nr) {
final HeaderValue v = MAP.get(Integer.valueOf(nr));
return v == null ? UNKNOWN : v;
}
}
}

View File

@ -84,7 +84,7 @@ public class SpdySessionHandlerTest {
assertEquals(last, spdyHeadersFrame.isLast());
for (CharSequence name: headers.names()) {
List<CharSequence> expectedValues = headers.getAll(name);
List<CharSequence> receivedValues = new ArrayList<CharSequence>(spdyHeadersFrame.headers().getAll(name));
List<CharSequence> receivedValues = spdyHeadersFrame.headers().getAll(name);
assertTrue(receivedValues.containsAll(expectedValues));
receivedValues.removeAll(expectedValues);
assertTrue(receivedValues.isEmpty());

View File

@ -14,19 +14,16 @@
*/
package io.netty.handler.codec.http2;
import io.netty.handler.codec.BinaryHeaders;
import io.netty.handler.codec.DefaultBinaryHeaders;
import io.netty.handler.codec.ByteStringValueConverter;
import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.Headers;
import io.netty.util.ByteString;
import java.io.Serializable;
import java.util.Comparator;
import java.util.List;
import java.util.TreeMap;
public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2Headers {
public class DefaultHttp2Headers extends DefaultHeaders<ByteString> implements Http2Headers {
private HeaderEntry<ByteString> firstNonPseudo = head;
public DefaultHttp2Headers() {
super(new TreeMap<ByteString, Object>(Http2HeaderNameComparator.INSTANCE));
super(ByteStringValueConverter.INSTANCE);
}
@Override
@ -120,7 +117,7 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He
}
@Override
public Http2Headers add(BinaryHeaders headers) {
public Http2Headers add(Headers<? extends ByteString> headers) {
super.add(headers);
return this;
}
@ -216,13 +213,13 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He
}
@Override
public Http2Headers set(BinaryHeaders headers) {
public Http2Headers set(Headers<? extends ByteString> headers) {
super.set(headers);
return this;
}
@Override
public Http2Headers setAll(BinaryHeaders headers) {
public Http2Headers setAll(Headers<? extends ByteString> headers) {
super.setAll(headers);
return this;
}
@ -289,43 +286,37 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He
}
@Override
public int hashCode() {
return size();
protected final HeaderEntry<ByteString> newHeaderEntry(int h, ByteString name, ByteString value,
HeaderEntry<ByteString> next) {
return new Http2HeaderEntry(h, name, value, next);
}
@Override
public boolean equals(Object other) {
if (!(other instanceof Http2Headers)) {
return false;
}
Http2Headers headers = (Http2Headers) other;
return DefaultHeaders.comparatorEquals(this, headers, ByteString.DEFAULT_COMPARATOR);
}
private final class Http2HeaderEntry extends HeaderEntry<ByteString> {
protected Http2HeaderEntry(int hash, ByteString key, ByteString value, HeaderEntry<ByteString> next) {
super(hash, key);
this.value = value;
this.next = next;
private static class Http2HeaderNameComparator implements Comparator<ByteString>, Serializable {
public static final Http2HeaderNameComparator INSTANCE = new Http2HeaderNameComparator();
private static final long serialVersionUID = 1109871697664666478L;
@Override
public int compare(ByteString one, ByteString two) {
// Reserved header names come first.
final boolean isPseudoHeader1 = !one.isEmpty() && one.byteAt(0) == ':';
final boolean isPseudoHeader2 = !two.isEmpty() && two.byteAt(0) == ':';
if (isPseudoHeader1 != isPseudoHeader2) {
return isPseudoHeader1 ? -1 : 1;
}
final int delta = one.hashCode() - two.hashCode();
if (delta == 0) {
// If the hash code matches it's very likely for the two strings to be equal
// and thus we optimistically compare them with the much faster equals method.
if (one.equals(two)) {
return 0;
} else {
return ByteString.DEFAULT_COMPARATOR.compare(one, two);
// Make sure the pseudo headers fields are first in iteration order
if (!key.isEmpty() && key.byteAt(0) == ':') {
after = firstNonPseudo;
before = firstNonPseudo.before();
} else {
after = head;
before = head.before();
if (firstNonPseudo == head) {
firstNonPseudo = this;
}
}
return delta;
pointNeighborsToThis();
}
@Override
protected void remove() {
if (this == firstNonPseudo) {
firstNonPseudo = firstNonPseudo.after();
}
super.remove();
}
}
}

View File

@ -15,11 +15,11 @@
package io.netty.handler.codec.http2;
import io.netty.handler.codec.BinaryHeaders;
import io.netty.handler.codec.EmptyBinaryHeaders;
import io.netty.handler.codec.EmptyHeaders;
import io.netty.handler.codec.Headers;
import io.netty.util.ByteString;
public final class EmptyHttp2Headers extends EmptyBinaryHeaders implements Http2Headers {
public final class EmptyHttp2Headers extends EmptyHeaders<ByteString> implements Http2Headers {
public static final EmptyHttp2Headers INSTANCE = new EmptyHttp2Headers();
private EmptyHttp2Headers() {
@ -116,7 +116,7 @@ public final class EmptyHttp2Headers extends EmptyBinaryHeaders implements Http2
}
@Override
public Http2Headers add(BinaryHeaders headers) {
public Http2Headers add(Headers<? extends ByteString> headers) {
super.add(headers);
return this;
}
@ -212,13 +212,13 @@ public final class EmptyHttp2Headers extends EmptyBinaryHeaders implements Http2
}
@Override
public Http2Headers set(BinaryHeaders headers) {
public Http2Headers set(Headers<? extends ByteString> headers) {
super.set(headers);
return this;
}
@Override
public Http2Headers setAll(BinaryHeaders headers) {
public Http2Headers setAll(Headers<? extends ByteString> headers) {
super.setAll(headers);
return this;
}

View File

@ -15,19 +15,19 @@
package io.netty.handler.codec.http2;
import io.netty.handler.codec.BinaryHeaders;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import io.netty.handler.codec.Headers;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil;
/**
* A collection of headers sent or received via HTTP/2.
*/
public interface Http2Headers extends BinaryHeaders {
public interface Http2Headers extends Headers<ByteString> {
/**
* HTTP/2 pseudo-headers names.
@ -129,7 +129,7 @@ public interface Http2Headers extends BinaryHeaders {
Http2Headers addTimeMillis(ByteString name, long value);
@Override
Http2Headers add(BinaryHeaders headers);
Http2Headers add(Headers<? extends ByteString> headers);
@Override
Http2Headers set(ByteString name, ByteString value);
@ -177,10 +177,10 @@ public interface Http2Headers extends BinaryHeaders {
Http2Headers setTimeMillis(ByteString name, long value);
@Override
Http2Headers set(BinaryHeaders headers);
Http2Headers set(Headers<? extends ByteString> headers);
@Override
Http2Headers setAll(BinaryHeaders headers);
Http2Headers setAll(Headers<? extends ByteString> headers);
@Override
Http2Headers clear();

View File

@ -275,7 +275,7 @@ public final class HttpUtil {
out.path(new AsciiString(request.uri()));
out.method(new AsciiString(request.method().toString()));
String value = inHeaders.get(HttpHeaderNames.HOST);
String value = inHeaders.getAsString(HttpHeaderNames.HOST);
if (value != null) {
URI hostUri = URI.create(value);
// The authority MUST NOT include the deprecated "userinfo" subcomponent
@ -324,8 +324,8 @@ public final class HttpUtil {
AsciiString aValue = AsciiString.of(entry.getValue());
// https://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-8.1.2.2
// makes a special exception for TE
if (!aName.equalsIgnoreCase(HttpHeaderNames.TE) ||
aValue.equalsIgnoreCase(HttpHeaderValues.TRAILERS)) {
if (!aName.contentEqualsIgnoreCase(HttpHeaderNames.TE) ||
aValue.contentEqualsIgnoreCase(HttpHeaderValues.TRAILERS)) {
out.add(aName, aValue);
}
}
@ -334,7 +334,7 @@ public final class HttpUtil {
}
/**
* A visitor which translates HTTP/2 headers to HTTP/1 headers
* Utility which translates HTTP/2 headers to HTTP/1 headers.
*/
private static final class Http2ToHttpHeaderTranslator {
/**

View File

@ -14,12 +14,6 @@
*/
package io.netty.handler.codec.http2;
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
import java.util.Iterator;
import java.util.Map.Entry;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.FullHttpMessage;
@ -27,7 +21,12 @@ import io.netty.handler.codec.http.HttpHeaders;
import io.netty.util.AsciiString;
import io.netty.util.collection.IntObjectHashMap;
import io.netty.util.collection.IntObjectMap;
import io.netty.util.internal.PlatformDependent;
import java.util.Iterator;
import java.util.Map.Entry;
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
/**
* Translate header/data/priority HTTP/2 frame events into HTTP events. Just as {@link InboundHttp2ToHttpAdapter}
@ -136,14 +135,10 @@ public final class InboundHttp2ToHttpPriorityAdapter extends InboundHttp2ToHttpA
* @param http2Headers The target HTTP/2 headers
*/
private static void addHttpHeadersToHttp2Headers(HttpHeaders httpHeaders, final Http2Headers http2Headers) {
try {
Iterator<Entry<CharSequence, CharSequence>> iter = httpHeaders.iteratorCharSequence();
while (iter.hasNext()) {
Entry<CharSequence, CharSequence> entry = iter.next();
http2Headers.add(AsciiString.of(entry.getKey()), AsciiString.of(entry.getValue()));
}
} catch (Exception ex) {
PlatformDependent.throwException(ex);
Iterator<Entry<CharSequence, CharSequence>> iter = httpHeaders.iteratorCharSequence();
while (iter.hasNext()) {
Entry<CharSequence, CharSequence> entry = iter.next();
http2Headers.add(AsciiString.of(entry.getKey()), AsciiString.of(entry.getValue()));
}
}

View File

@ -49,7 +49,6 @@ import java.util.Random;
import java.util.concurrent.CountDownLatch;
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT;
import static io.netty.handler.codec.http2.Http2TestUtil.as;
import static io.netty.handler.codec.http2.Http2TestUtil.runInChannel;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
@ -67,9 +66,9 @@ import static org.mockito.Mockito.verify;
* Test for data decompression in the HTTP/2 codec.
*/
public class DataCompressionHttp2Test {
private static final AsciiString GET = as("GET");
private static final AsciiString POST = as("POST");
private static final AsciiString PATH = as("/some/path");
private static final AsciiString GET = new AsciiString("GET");
private static final AsciiString POST = new AsciiString("POST");
private static final AsciiString PATH = new AsciiString("/some/path");
@Mock
private Http2FrameListener serverListener;

View File

@ -15,9 +15,29 @@
package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.AsciiString;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.EventExecutor;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_HEADER_SIZE;
import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_UNSIGNED_INT;
import static io.netty.handler.codec.http2.Http2TestUtil.as;
import static io.netty.handler.codec.http2.Http2TestUtil.randomString;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
@ -29,26 +49,6 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.EventExecutor;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
/**
* Integration tests for {@link DefaultHttp2FrameReader} and {@link DefaultHttp2FrameWriter}.
@ -365,8 +365,9 @@ public class DefaultHttp2FrameIOTest {
}
private static Http2Headers dummyHeaders() {
return new DefaultHttp2Headers().method(as("GET")).scheme(as("https")).authority(as("example.org"))
.path(as("/some/path")).add(as("accept"), as("*/*"));
return new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https"))
.authority(new AsciiString("example.org")).path(new AsciiString("/some/path"))
.add(new AsciiString("accept"), new AsciiString("*/*"));
}
private static Http2Headers largeHeaders() {
@ -374,7 +375,7 @@ public class DefaultHttp2FrameIOTest {
for (int i = 0; i < 100; ++i) {
String key = "this-is-a-test-header-key-" + i;
String value = "this-is-a-test-header-value-" + i;
headers.add(as(key), as(value));
headers.add(new AsciiString(key), new AsciiString(value));
}
return headers;
}

View File

@ -15,12 +15,11 @@
package io.netty.handler.codec.http2;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link DefaultHttp2HeaderTableListSize}.
*/

View File

@ -15,22 +15,21 @@
package io.netty.handler.codec.http2;
import com.twitter.hpack.Encoder;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.util.AsciiString;
import org.junit.Before;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_HEADER_SIZE;
import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_HEADER_TABLE_SIZE;
import static io.netty.handler.codec.http2.Http2TestUtil.as;
import static io.netty.handler.codec.http2.Http2TestUtil.randomBytes;
import static io.netty.util.CharsetUtil.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.io.ByteArrayOutputStream;
import org.junit.Before;
import org.junit.Test;
import com.twitter.hpack.Encoder;
/**
* Tests for {@link DefaultHttp2HeadersDecoder}.
@ -51,7 +50,7 @@ public class DefaultHttp2HeadersDecoderTest {
Http2Headers headers = decoder.decodeHeaders(buf);
assertEquals(3, headers.size());
assertEquals("GET", headers.method().toString());
assertEquals("avalue", headers.get(as("akey")).toString());
assertEquals("avalue", headers.get(new AsciiString("akey")).toString());
} finally {
buf.release();
}

View File

@ -15,14 +15,14 @@
package io.netty.handler.codec.http2;
import static io.netty.handler.codec.http2.Http2TestUtil.as;
import static org.junit.Assert.assertTrue;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.util.AsciiString;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
* Tests for {@link DefaultHttp2HeadersEncoder}.
*/
@ -55,7 +55,7 @@ public class DefaultHttp2HeadersEncoderTest {
}
private static Http2Headers headers() {
return new DefaultHttp2Headers().method(as("GET")).add(as("a"), as("1"))
.add(as("a"), as("2"));
return new DefaultHttp2Headers().method(new AsciiString("GET")).add(new AsciiString("a"), new AsciiString("1"))
.add(new AsciiString("a"), new AsciiString("2"));
}
}

View File

@ -16,42 +16,83 @@
package io.netty.handler.codec.http2;
import io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;
import static java.util.Arrays.asList;
import static io.netty.util.ByteString.fromAscii;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class DefaultHttp2HeadersTest {
@Test
public void pseudoHeadersMustComeFirstWhenIterating() {
DefaultHttp2Headers headers = new DefaultHttp2Headers();
headers.add(bs("name1"), bs("value1"), bs("value2"));
headers.method(bs("POST"));
headers.add(bs("2name"), bs("value3"));
headers.path(bs("/index.html"));
headers.status(bs("200"));
headers.authority(bs("netty.io"));
headers.add(bs("name3"), bs("value4"));
headers.scheme(bs("https"));
Http2Headers headers = newHeaders();
Iterator<Entry<ByteString, ByteString>> iter = headers.iterator();
List<ByteString> names = new ArrayList<ByteString>();
for (int i = 0; i < 5; i++) {
names.add(iter.next().getKey());
}
assertTrue(names.containsAll(asList(bs(":method"), bs(":status"), bs(":path"), bs(":scheme"),
bs(":authority"))));
verifyPseudoHeadersFirst(headers);
verifyAllPseudoHeadersPresent(headers);
}
private static ByteString bs(String str) {
return new ByteString(str, CharsetUtil.US_ASCII);
@Test
public void pseudoHeadersWithRemovePreservesPseudoIterationOrder() {
Http2Headers headers = newHeaders();
Set<ByteString> nonPseudoHeaders = new HashSet<ByteString>(headers.size());
for (Entry<ByteString, ByteString> entry : headers) {
if (entry.getKey().isEmpty() || entry.getKey().byteAt(0) != ':') {
nonPseudoHeaders.add(entry.getKey());
}
}
// Remove all the non-pseudo headers and verify
for (ByteString nonPseudoHeader : nonPseudoHeaders) {
assertTrue(headers.remove(nonPseudoHeader));
verifyPseudoHeadersFirst(headers);
verifyAllPseudoHeadersPresent(headers);
}
// Add back all non-pseudo headers
for (ByteString nonPseudoHeader : nonPseudoHeaders) {
headers.add(nonPseudoHeader, fromAscii("goo"));
verifyPseudoHeadersFirst(headers);
verifyAllPseudoHeadersPresent(headers);
}
}
private static void verifyAllPseudoHeadersPresent(Http2Headers headers) {
for (PseudoHeaderName pseudoName : PseudoHeaderName.values()) {
assertNotNull(headers.get(pseudoName.value()));
}
}
private static void verifyPseudoHeadersFirst(Http2Headers headers) {
ByteString lastNonPseudoName = null;
for (Entry<ByteString, ByteString> entry: headers) {
if (entry.getKey().isEmpty() || entry.getKey().byteAt(0) != ':') {
lastNonPseudoName = entry.getKey();
} else if (lastNonPseudoName != null) {
fail("All pseudo headers must be fist in iteration. Pseudo header " + entry.getKey() +
" is after a non pseudo header " + lastNonPseudoName);
}
}
}
private static Http2Headers newHeaders() {
Http2Headers headers = new DefaultHttp2Headers();
headers.add(fromAscii("name1"), fromAscii("value1"), fromAscii("value2"));
headers.method(fromAscii("POST"));
headers.add(fromAscii("2name"), fromAscii("value3"));
headers.path(fromAscii("/index.html"));
headers.status(fromAscii("200"));
headers.authority(fromAscii("netty.io"));
headers.add(fromAscii("name3"), fromAscii("value4"));
headers.scheme(fromAscii("https"));
return headers;
}
}

View File

@ -15,28 +15,6 @@
package io.netty.handler.codec.http2;
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import static io.netty.handler.codec.http2.Http2TestUtil.as;
import static io.netty.handler.codec.http2.Http2TestUtil.randomString;
import static io.netty.handler.codec.http2.Http2TestUtil.runInChannel;
import static io.netty.handler.codec.http2.Http2TestUtil.FrameCountDown;
import static io.netty.util.CharsetUtil.UTF_8;
import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
@ -53,15 +31,11 @@ import io.netty.channel.ChannelPromise;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http2.Http2TestUtil.FrameCountDown;
import io.netty.handler.codec.http2.Http2TestUtil.Http2Runnable;
import io.netty.util.AsciiString;
import io.netty.util.NetUtil;
import io.netty.util.concurrent.Future;
import java.io.ByteArrayOutputStream;
import java.net.InetSocketAddress;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -70,6 +44,32 @@ import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.io.ByteArrayOutputStream;
import java.net.InetSocketAddress;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import static io.netty.handler.codec.http2.Http2TestUtil.randomString;
import static io.netty.handler.codec.http2.Http2TestUtil.runInChannel;
import static io.netty.util.CharsetUtil.UTF_8;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* Tests the full HTTP/2 framing stack including the connection and preface handlers.
*/
@ -495,8 +495,9 @@ public class Http2ConnectionRoundtripTest {
}
private static Http2Headers dummyHeaders() {
return new DefaultHttp2Headers().method(as("GET")).scheme(as("https"))
.authority(as("example.org")).path(as("/some/path/resource2")).add(randomString(), randomString());
return new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https"))
.authority(new AsciiString("example.org")).path(new AsciiString("/some/path/resource2"))
.add(randomString(), randomString());
}
private void mockFlowControl(Http2FrameListener listener) throws Http2Exception {

View File

@ -29,6 +29,7 @@ import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http2.Http2TestUtil.Http2Runnable;
import io.netty.util.AsciiString;
import io.netty.util.NetUtil;
import io.netty.util.concurrent.Future;
import org.junit.After;
@ -46,7 +47,6 @@ import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static io.netty.handler.codec.http2.Http2TestUtil.as;
import static io.netty.handler.codec.http2.Http2TestUtil.randomString;
import static io.netty.handler.codec.http2.Http2TestUtil.runInChannel;
import static io.netty.util.CharsetUtil.UTF_8;
@ -393,7 +393,8 @@ public class Http2FrameRoundtripTest {
}
private static Http2Headers headers() {
return new DefaultHttp2Headers().method(as("GET")).scheme(as("https"))
.authority(as("example.org")).path(as("/some/path/resource2")).add(randomString(), randomString());
return new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https"))
.authority(new AsciiString("example.org")).path(new AsciiString("/some/path/resource2"))
.add(randomString(), randomString());
}
}

View File

@ -15,16 +15,16 @@
package io.netty.handler.codec.http2;
import static io.netty.handler.codec.http2.Http2TestUtil.as;
import static io.netty.handler.codec.http2.Http2TestUtil.randomString;
import static org.junit.Assert.assertEquals;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.util.AsciiString;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static io.netty.handler.codec.http2.Http2TestUtil.randomString;
import static org.junit.Assert.assertEquals;
/**
* Tests for encoding/decoding HTTP2 header blocks.
*/
@ -55,23 +55,23 @@ public class Http2HeaderBlockIOTest {
@Test
public void successiveCallsShouldSucceed() throws Http2Exception {
Http2Headers in =
new DefaultHttp2Headers().method(as("GET")).scheme(as("https"))
.authority(as("example.org")).path(as("/some/path"))
.add(as("accept"), as("*/*"));
new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https"))
.authority(new AsciiString("example.org")).path(new AsciiString("/some/path"))
.add(new AsciiString("accept"), new AsciiString("*/*"));
assertRoundtripSuccessful(in);
in =
new DefaultHttp2Headers().method(as("GET")).scheme(as("https"))
.authority(as("example.org")).path(as("/some/path/resource1"))
.add(as("accept"), as("image/jpeg"))
.add(as("cache-control"), as("no-cache"));
new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https"))
.authority(new AsciiString("example.org")).path(new AsciiString("/some/path/resource1"))
.add(new AsciiString("accept"), new AsciiString("image/jpeg"))
.add(new AsciiString("cache-control"), new AsciiString("no-cache"));
assertRoundtripSuccessful(in);
in =
new DefaultHttp2Headers().method(as("GET")).scheme(as("https"))
.authority(as("example.org")).path(as("/some/path/resource2"))
.add(as("accept"), as("image/png"))
.add(as("cache-control"), as("no-cache"));
new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https"))
.authority(new AsciiString("example.org")).path(new AsciiString("/some/path/resource2"))
.add(new AsciiString("accept"), new AsciiString("image/png"))
.add(new AsciiString("cache-control"), new AsciiString("no-cache"));
assertRoundtripSuccessful(in);
}
@ -91,11 +91,14 @@ public class Http2HeaderBlockIOTest {
}
private static Http2Headers headers() {
return new DefaultHttp2Headers().method(as("GET")).scheme(as("https"))
.authority(as("example.org")).path(as("/some/path/resource2"))
.add(as("accept"), as("image/png")).add(as("cache-control"), as("no-cache"))
.add(as("custom"), as("value1")).add(as("custom"), as("value2"))
.add(as("custom"), as("value3")).add(as("custom"), as("custom4"))
return new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https"))
.authority(new AsciiString("example.org")).path(new AsciiString("/some/path/resource2"))
.add(new AsciiString("accept"), new AsciiString("image/png"))
.add(new AsciiString("cache-control"), new AsciiString("no-cache"))
.add(new AsciiString("custom"), new AsciiString("value1"))
.add(new AsciiString("custom"), new AsciiString("value2"))
.add(new AsciiString("custom"), new AsciiString("value3"))
.add(new AsciiString("custom"), new AsciiString("custom4"))
.add(randomString(), randomString());
}
}

View File

@ -52,20 +52,6 @@ final class Http2TestUtil {
});
}
/**
* Converts a {@link String} into an {@link AsciiString}.
*/
public static AsciiString as(String value) {
return new AsciiString(value);
}
/**
* Converts a byte array into a {@link ByteString}.
*/
public static ByteString bs(byte[] value) {
return new ByteString(value);
}
/**
* Returns a byte array filled with random data.
*/
@ -86,7 +72,7 @@ final class Http2TestUtil {
* Returns an {@link AsciiString} that wraps a randomly-filled byte array.
*/
public static ByteString randomString() {
return bs(randomBytes());
return new ByteString(randomBytes());
}
private Http2TestUtil() {

View File

@ -14,23 +14,6 @@
*/
package io.netty.handler.codec.http2;
import static io.netty.handler.codec.http.HttpMethod.GET;
import static io.netty.handler.codec.http.HttpMethod.POST;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import static io.netty.handler.codec.http2.Http2TestUtil.as;
import static io.netty.util.CharsetUtil.UTF_8;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyShort;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
@ -54,15 +37,9 @@ import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http2.Http2TestUtil.FrameCountDown;
import io.netty.util.AsciiString;
import io.netty.util.NetUtil;
import io.netty.util.concurrent.Future;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -71,6 +48,29 @@ import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import static io.netty.handler.codec.http.HttpMethod.GET;
import static io.netty.handler.codec.http.HttpMethod.POST;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import static io.netty.util.CharsetUtil.UTF_8;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyShort;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
/**
* Testing the {@link HttpToHttp2ConnectionHandler} for {@link FullHttpRequest} objects into HTTP/2 frames
*/
@ -122,10 +122,11 @@ public class HttpToHttp2ConnectionHandlerTest {
httpHeaders.add("foo", "goo2");
httpHeaders.add("foo2", "goo2");
final Http2Headers http2Headers =
new DefaultHttp2Headers().method(as("GET")).path(as("/example"))
.authority(as("www.example.org:5555")).scheme(as("http"))
.add(as("foo"), as("goo")).add(as("foo"), as("goo2"))
.add(as("foo2"), as("goo2"));
new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/example"))
.authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http"))
.add(new AsciiString("foo"), new AsciiString("goo"))
.add(new AsciiString("foo"), new AsciiString("goo2"))
.add(new AsciiString("foo2"), new AsciiString("goo2"));
ChannelPromise writePromise = newPromise();
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise);
@ -161,10 +162,11 @@ public class HttpToHttp2ConnectionHandlerTest {
httpHeaders.add("foo", "goo2");
httpHeaders.add("foo2", "goo2");
final Http2Headers http2Headers =
new DefaultHttp2Headers().method(as("POST")).path(as("/example"))
.authority(as("www.example.org:5555")).scheme(as("http"))
.add(as("foo"), as("goo")).add(as("foo"), as("goo2"))
.add(as("foo2"), as("goo2"));
new DefaultHttp2Headers().method(new AsciiString("POST")).path(new AsciiString("/example"))
.authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http"))
.add(new AsciiString("foo"), new AsciiString("goo"))
.add(new AsciiString("foo"), new AsciiString("goo2"))
.add(new AsciiString("foo2"), new AsciiString("goo2"));
ChannelPromise writePromise = newPromise();
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise);
@ -202,14 +204,16 @@ public class HttpToHttp2ConnectionHandlerTest {
httpHeaders.add("foo", "goo2");
httpHeaders.add("foo2", "goo2");
final Http2Headers http2Headers =
new DefaultHttp2Headers().method(as("POST")).path(as("/example"))
.authority(as("www.example.org:5555")).scheme(as("http"))
.add(as("foo"), as("goo")).add(as("foo"), as("goo2"))
.add(as("foo2"), as("goo2"));
new DefaultHttp2Headers().method(new AsciiString("POST")).path(new AsciiString("/example"))
.authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http"))
.add(new AsciiString("foo"), new AsciiString("goo"))
.add(new AsciiString("foo"), new AsciiString("goo2"))
.add(new AsciiString("foo2"), new AsciiString("goo2"));
request.trailingHeaders().add("trailing", "bar");
final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers().add(as("trailing"), as("bar"));
final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers()
.add(new AsciiString("trailing"), new AsciiString("bar"));
ChannelPromise writePromise = newPromise();
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise);
@ -251,17 +255,19 @@ public class HttpToHttp2ConnectionHandlerTest {
httpHeaders.add("foo", "goo2");
httpHeaders.add("foo2", "goo2");
final Http2Headers http2Headers =
new DefaultHttp2Headers().method(as("POST")).path(as("/example"))
.authority(as("www.example.org:5555")).scheme(as("http"))
.add(as("foo"), as("goo")).add(as("foo"), as("goo2"))
.add(as("foo2"), as("goo2"));
new DefaultHttp2Headers().method(new AsciiString("POST")).path(new AsciiString("/example"))
.authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http"))
.add(new AsciiString("foo"), new AsciiString("goo"))
.add(new AsciiString("foo"), new AsciiString("goo2"))
.add(new AsciiString("foo2"), new AsciiString("goo2"));
final DefaultHttpContent httpContent = new DefaultHttpContent(Unpooled.copiedBuffer(text, UTF_8));
final LastHttpContent lastHttpContent = new DefaultLastHttpContent(Unpooled.copiedBuffer(text2, UTF_8));
lastHttpContent.trailingHeaders().add("trailing", "bar");
final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers().add(as("trailing"), as("bar"));
final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers()
.add(new AsciiString("trailing"), new AsciiString("bar"));
ChannelPromise writePromise = newPromise();
ChannelFuture writeFuture = clientChannel.write(request, writePromise);

View File

@ -59,7 +59,6 @@ import java.util.concurrent.CountDownLatch;
import static io.netty.handler.codec.http2.Http2CodecUtil.getEmbeddedHttp2Exception;
import static io.netty.handler.codec.http2.Http2Exception.isStreamError;
import static io.netty.handler.codec.http2.Http2TestUtil.as;
import static io.netty.handler.codec.http2.Http2TestUtil.runInChannel;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
@ -210,8 +209,9 @@ public class InboundHttp2ToHttpAdapterTest {
httpHeaders.set(HttpUtil.ExtensionHeaderNames.AUTHORITY.text(), "example.org");
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0);
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")).scheme(as("https"))
.authority(as("example.org")).path(as("/some/path/resource2"));
final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).
scheme(new AsciiString("https")).authority(new AsciiString("example.org"))
.path(new AsciiString("/some/path/resource2"));
runInChannel(clientChannel, new Http2Runnable() {
@Override
public void run() {
@ -232,10 +232,10 @@ public class InboundHttp2ToHttpAdapterTest {
@Test
public void clientRequestSingleHeaderNonAsciiShouldThrow() throws Exception {
final Http2Headers http2Headers = new DefaultHttp2Headers()
.method(as("GET"))
.scheme(as("https"))
.authority(as("example.org"))
.path(as("/some/path/resource2"))
.method(new AsciiString("GET"))
.scheme(new AsciiString("https"))
.authority(new AsciiString("example.org"))
.path(new AsciiString("/some/path/resource2"))
.add(new AsciiString("çã".getBytes(CharsetUtil.UTF_8)),
new AsciiString("Ãã".getBytes(CharsetUtil.UTF_8)));
runInChannel(clientChannel, new Http2Runnable() {
@ -259,8 +259,8 @@ public class InboundHttp2ToHttpAdapterTest {
HttpHeaders httpHeaders = request.headers();
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length());
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")).path(
as("/some/path/resource2"));
final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
new AsciiString("/some/path/resource2"));
runInChannel(clientChannel, new Http2Runnable() {
@Override
public void run() {
@ -289,8 +289,8 @@ public class InboundHttp2ToHttpAdapterTest {
HttpHeaders httpHeaders = request.headers();
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length());
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")).path(
as("/some/path/resource2"));
final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
new AsciiString("/some/path/resource2"));
final int midPoint = text.length() / 2;
runInChannel(clientChannel, new Http2Runnable() {
@Override
@ -323,8 +323,8 @@ public class InboundHttp2ToHttpAdapterTest {
HttpHeaders httpHeaders = request.headers();
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length());
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")).path(
as("/some/path/resource2"));
final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
new AsciiString("/some/path/resource2"));
runInChannel(clientChannel, new Http2Runnable() {
@Override
public void run() {
@ -361,10 +361,12 @@ public class InboundHttp2ToHttpAdapterTest {
trailingHeaders.set("FoO", "goo");
trailingHeaders.set("foO2", "goo2");
trailingHeaders.add("fOo2", "goo3");
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")).path(
as("/some/path/resource2"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers().set(as("foo"), as("goo"))
.set(as("foo2"), as("goo2")).add(as("foo2"), as("goo3"));
final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
new AsciiString("/some/path/resource2"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers()
.set(new AsciiString("foo"), new AsciiString("goo"))
.set(new AsciiString("foo2"), new AsciiString("goo2"))
.add(new AsciiString("foo2"), new AsciiString("goo3"));
runInChannel(clientChannel, new Http2Runnable() {
@Override
public void run() {
@ -398,10 +400,12 @@ public class InboundHttp2ToHttpAdapterTest {
trailingHeaders.set("Foo", "goo");
trailingHeaders.set("fOo2", "goo2");
trailingHeaders.add("foO2", "goo3");
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")).path(
as("/some/path/resource2"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers().set(as("foo"), as("goo"))
.set(as("foo2"), as("goo2")).add(as("foo2"), as("goo3"));
final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
new AsciiString("/some/path/resource2"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers()
.set(new AsciiString("foo"), new AsciiString("goo"))
.set(new AsciiString("foo2"), new AsciiString("goo2"))
.add(new AsciiString("foo2"), new AsciiString("goo3"));
runInChannel(clientChannel, new Http2Runnable() {
@Override
public void run() {
@ -441,10 +445,10 @@ public class InboundHttp2ToHttpAdapterTest {
httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3);
httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), 123);
httpHeaders2.setInt(HttpHeaderNames.CONTENT_LENGTH, text2.length());
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("PUT")).path(
as("/some/path/resource"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(as("PUT")).path(
as("/some/path/resource2"));
final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("PUT")).path(
new AsciiString("/some/path/resource"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(new AsciiString("PUT")).path(
new AsciiString("/some/path/resource2"));
runInChannel(clientChannel, new Http2Runnable() {
@Override
public void run() {
@ -488,10 +492,10 @@ public class InboundHttp2ToHttpAdapterTest {
HttpHeaders httpHeaders2 = request2.headers();
httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
httpHeaders2.setInt(HttpHeaderNames.CONTENT_LENGTH, text2.length());
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("PUT")).path(
as("/some/path/resource"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(as("PUT")).path(
as("/some/path/resource2"));
final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("PUT")).path(
new AsciiString("/some/path/resource"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(new AsciiString("PUT")).path(
new AsciiString("/some/path/resource2"));
HttpHeaders httpHeaders3 = request3.headers();
httpHeaders3.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
httpHeaders3.setInt(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3);
@ -549,7 +553,8 @@ public class InboundHttp2ToHttpAdapterTest {
httpHeaders = request.headers();
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0);
final Http2Headers http2Headers3 = new DefaultHttp2Headers().method(as("GET")).path(as("/push/test"));
final Http2Headers http2Headers3 = new DefaultHttp2Headers().method(new AsciiString("GET"))
.path(new AsciiString("/push/test"));
runInChannel(clientChannel, new Http2Runnable() {
@Override
public void run() {
@ -563,9 +568,10 @@ public class InboundHttp2ToHttpAdapterTest {
capturedRequests = requestCaptor.getAllValues();
assertEquals(request, capturedRequests.get(0));
final Http2Headers http2Headers = new DefaultHttp2Headers().status(as("200"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers().status(as("201")).scheme(as("https"))
.authority(as("example.org"));
final Http2Headers http2Headers = new DefaultHttp2Headers().status(new AsciiString("200"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers().status(new AsciiString("201"))
.scheme(new AsciiString("https"))
.authority(new AsciiString("example.org"));
runInChannel(serverConnectedChannel, new Http2Runnable() {
@Override
public void run() {
@ -597,8 +603,10 @@ public class InboundHttp2ToHttpAdapterTest {
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.set(HttpHeaderNames.EXPECT, HttpHeaderValues.CONTINUE);
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0);
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("PUT")).path(as("/info/test"))
.set(as(HttpHeaderNames.EXPECT.toString()), as(HttpHeaderValues.CONTINUE.toString()));
final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("PUT"))
.path(new AsciiString("/info/test"))
.set(new AsciiString(HttpHeaderNames.EXPECT.toString()),
new AsciiString(HttpHeaderValues.CONTINUE.toString()));
final FullHttpMessage response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
final String text = "a big payload";
final ByteBuf payload = Unpooled.copiedBuffer(text.getBytes());
@ -624,7 +632,7 @@ public class InboundHttp2ToHttpAdapterTest {
httpHeaders = response.headers();
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0);
final Http2Headers http2HeadersResponse = new DefaultHttp2Headers().status(as("100"));
final Http2Headers http2HeadersResponse = new DefaultHttp2Headers().status(new AsciiString("100"));
runInChannel(serverConnectedChannel, new Http2Runnable() {
@Override
public void run() {
@ -661,7 +669,7 @@ public class InboundHttp2ToHttpAdapterTest {
httpHeaders = response2.headers();
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0);
final Http2Headers http2HeadersResponse2 = new DefaultHttp2Headers().status(as("200"));
final Http2Headers http2HeadersResponse2 = new DefaultHttp2Headers().status(new AsciiString("200"));
runInChannel(serverConnectedChannel, new Http2Runnable() {
@Override
public void run() {

View File

@ -16,15 +16,21 @@
package io.netty.handler.codec.stomp;
import io.netty.handler.codec.DefaultTextHeaders;
import io.netty.handler.codec.TextHeaders;
import io.netty.handler.codec.CharSequenceValueConverter;
import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.Headers;
import io.netty.handler.codec.HeadersUtils;
import java.util.TreeMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
public class DefaultStompHeaders extends DefaultTextHeaders implements StompHeaders {
import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER;
import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER;
public class DefaultStompHeaders extends DefaultHeaders<CharSequence> implements StompHeaders {
public DefaultStompHeaders() {
super(new TreeMap<CharSequence, Object>(), NO_NAME_VALIDATOR, CharSequenceConverter.INSTANCE, false);
super(CharSequenceValueConverter.INSTANCE);
}
@Override
@ -118,7 +124,7 @@ public class DefaultStompHeaders extends DefaultTextHeaders implements StompHead
}
@Override
public StompHeaders add(TextHeaders headers) {
public StompHeaders add(Headers<? extends CharSequence> headers) {
super.add(headers);
return this;
}
@ -214,13 +220,13 @@ public class DefaultStompHeaders extends DefaultTextHeaders implements StompHead
}
@Override
public StompHeaders set(TextHeaders headers) {
public StompHeaders set(Headers<? extends CharSequence> headers) {
super.set(headers);
return this;
}
@Override
public StompHeaders setAll(TextHeaders headers) {
public StompHeaders setAll(Headers<? extends CharSequence> headers) {
super.setAll(headers);
return this;
}
@ -230,4 +236,30 @@ public class DefaultStompHeaders extends DefaultTextHeaders implements StompHead
super.clear();
return this;
}
@Override
public String getAsString(CharSequence name) {
return HeadersUtils.getAsString(this, name);
}
@Override
public List<String> getAllAsString(CharSequence name) {
return HeadersUtils.getAllAsString(this, name);
}
@Override
public Iterator<Entry<String, String>> iteratorAsString() {
return HeadersUtils.iteratorAsString(this);
}
@Override
public boolean contains(CharSequence name, CharSequence value) {
return contains(name, value, false);
}
@Override
public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) {
return contains(name, value,
ignoreCase ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER);
}
}

View File

@ -15,14 +15,18 @@
*/
package io.netty.handler.codec.stomp;
import io.netty.handler.codec.TextHeaders;
import io.netty.handler.codec.Headers;
import io.netty.util.AsciiString;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
/**
* The multimap data structure for the STOMP header names and values. It also provides the constants for the standard
* STOMP header names and values.
*/
public interface StompHeaders extends TextHeaders {
public interface StompHeaders extends Headers<CharSequence> {
AsciiString ACCEPT_VERSION = new AsciiString("accept-version");
AsciiString HOST = new AsciiString("host");
@ -90,7 +94,7 @@ public interface StompHeaders extends TextHeaders {
StompHeaders addTimeMillis(CharSequence name, long value);
@Override
StompHeaders add(TextHeaders headers);
StompHeaders add(Headers<? extends CharSequence> headers);
@Override
StompHeaders set(CharSequence name, CharSequence value);
@ -138,11 +142,41 @@ public interface StompHeaders extends TextHeaders {
StompHeaders setTimeMillis(CharSequence name, long value);
@Override
StompHeaders set(TextHeaders headers);
StompHeaders set(Headers<? extends CharSequence> headers);
@Override
StompHeaders setAll(TextHeaders headers);
StompHeaders setAll(Headers<? extends CharSequence> headers);
@Override
StompHeaders clear();
/**
* {@link Headers#get(Object)} and convert the result to a {@link String}.
* @param name the name of the header to retrieve
* @return the first header value if the header is found. {@code null} if there's no such header.
*/
String getAsString(CharSequence name);
/**
* {@link Headers#getAll(Object)} and convert each element of {@link List} to a {@link String}.
* @param name the name of the header to retrieve
* @return a {@link List} of header values or an empty {@link List} if no values are found.
*/
List<String> getAllAsString(CharSequence name);
/**
* {@link #iterator()} that converts each {@link Entry}'s key and value to a {@link String}.
*/
Iterator<Entry<String, String>> iteratorAsString();
/**
* Returns {@code true} if a header with the {@code name} and {@code value} exists, {@code false} otherwise.
* <p>
* If {@code ignoreCase} is {@code true} then a case insensitive compare is done on the value.
* @param name the name of the header to find
* @param value the value of the header to find
* @param ignoreCase {@code true} then a case insensitive compare is run to compare values.
* otherwise a case sensitive compare is run to compare values.
*/
boolean contains(CharSequence name, CharSequence value, boolean ignoreCase);
}

View File

@ -15,6 +15,9 @@
*/
package io.netty.handler.codec.stomp;
import java.util.List;
import java.util.Map.Entry;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.AsciiHeadersEncoder;
@ -22,10 +25,6 @@ import io.netty.handler.codec.AsciiHeadersEncoder.NewlineType;
import io.netty.handler.codec.AsciiHeadersEncoder.SeparatorType;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.PlatformDependent;
import java.util.List;
import java.util.Map.Entry;
/**
* Encodes a {@link StompFrame} or a {@link StompSubframe} into a {@link ByteBuf}.

View File

@ -18,8 +18,8 @@ package io.netty.handler.codec.stomp;
public final class StompTestConstants {
public static final String CONNECT_FRAME =
"CONNECT\n" +
"accept-version:1.1,1.2\n" +
"host:stomp.github.org\n" +
"accept-version:1.1,1.2\n" +
'\n' +
'\0';
public static final String CONNECTED_FRAME =

View File

@ -1,135 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec;
import io.netty.util.ByteString;
/**
* A typical {@code ByteString} multimap used by protocols that use binary headers (such as HTTP/2) for the
* representation of arbitrary key-value data. {@link ByteString} is just a wrapper around a byte array but provides
* some additional utility when handling text data.
*/
public interface BinaryHeaders extends Headers<ByteString> {
@Override
BinaryHeaders add(ByteString name, ByteString value);
@Override
BinaryHeaders add(ByteString name, Iterable<? extends ByteString> values);
@Override
BinaryHeaders add(ByteString name, ByteString... values);
@Override
BinaryHeaders addObject(ByteString name, Object value);
@Override
BinaryHeaders addObject(ByteString name, Iterable<?> values);
@Override
BinaryHeaders addObject(ByteString name, Object... values);
@Override
BinaryHeaders addBoolean(ByteString name, boolean value);
@Override
BinaryHeaders addByte(ByteString name, byte value);
@Override
BinaryHeaders addChar(ByteString name, char value);
@Override
BinaryHeaders addShort(ByteString name, short value);
@Override
BinaryHeaders addInt(ByteString name, int value);
@Override
BinaryHeaders addLong(ByteString name, long value);
@Override
BinaryHeaders addFloat(ByteString name, float value);
@Override
BinaryHeaders addDouble(ByteString name, double value);
@Override
BinaryHeaders addTimeMillis(ByteString name, long value);
/**
* See {@link Headers#add(Headers)}
*/
BinaryHeaders add(BinaryHeaders headers);
@Override
BinaryHeaders set(ByteString name, ByteString value);
@Override
BinaryHeaders set(ByteString name, Iterable<? extends ByteString> values);
@Override
BinaryHeaders set(ByteString name, ByteString... values);
@Override
BinaryHeaders setObject(ByteString name, Object value);
@Override
BinaryHeaders setObject(ByteString name, Iterable<?> values);
@Override
BinaryHeaders setObject(ByteString name, Object... values);
@Override
BinaryHeaders setBoolean(ByteString name, boolean value);
@Override
BinaryHeaders setByte(ByteString name, byte value);
@Override
BinaryHeaders setChar(ByteString name, char value);
@Override
BinaryHeaders setShort(ByteString name, short value);
@Override
BinaryHeaders setInt(ByteString name, int value);
@Override
BinaryHeaders setLong(ByteString name, long value);
@Override
BinaryHeaders setFloat(ByteString name, float value);
@Override
BinaryHeaders setDouble(ByteString name, double value);
@Override
BinaryHeaders setTimeMillis(ByteString name, long value);
/**
* See {@link Headers#set(Headers)}
*/
BinaryHeaders set(BinaryHeaders headers);
/**
* See {@link Headers#setAll(Headers)}
*/
BinaryHeaders setAll(BinaryHeaders headers);
@Override
BinaryHeaders clear();
}

View File

@ -0,0 +1,140 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.netty.handler.codec;
import java.nio.charset.Charset;
import java.text.ParseException;
import io.netty.handler.codec.DefaultHeaders.HeaderDateFormat;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.PlatformDependent;
/**
* Converts to/from native types, general {@link Object}, and {@link ByteString}s.
*/
public final class ByteStringValueConverter implements ValueConverter<ByteString> {
public static final ByteStringValueConverter INSTANCE = new ByteStringValueConverter();
private static final Charset DEFAULT_CHARSET = CharsetUtil.UTF_8;
private ByteStringValueConverter() {
}
@Override
public ByteString convertObject(Object value) {
if (value instanceof ByteString) {
return (ByteString) value;
}
if (value instanceof CharSequence) {
return new ByteString((CharSequence) value, DEFAULT_CHARSET);
}
return new ByteString(value.toString(), DEFAULT_CHARSET);
}
@Override
public ByteString convertInt(int value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public ByteString convertLong(long value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public ByteString convertDouble(double value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public ByteString convertChar(char value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public ByteString convertBoolean(boolean value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public ByteString convertFloat(float value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public int convertToInt(ByteString value) {
return value.parseAsciiInt();
}
@Override
public long convertToLong(ByteString value) {
return value.parseAsciiLong();
}
@Override
public ByteString convertTimeMillis(long value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public long convertToTimeMillis(ByteString value) {
try {
return HeaderDateFormat.get().parse(value.toString());
} catch (ParseException e) {
PlatformDependent.throwException(e);
}
return 0;
}
@Override
public double convertToDouble(ByteString value) {
return value.parseAsciiDouble();
}
@Override
public char convertToChar(ByteString value) {
return value.parseChar();
}
@Override
public boolean convertToBoolean(ByteString value) {
return value.byteAt(0) != 0;
}
@Override
public float convertToFloat(ByteString value) {
return value.parseAsciiFloat();
}
@Override
public ByteString convertShort(short value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public short convertToShort(ByteString value) {
return value.parseAsciiShort();
}
@Override
public ByteString convertByte(byte value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public byte convertToByte(ByteString value) {
return value.byteAt(0);
}
}

View File

@ -0,0 +1,133 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.netty.handler.codec;
import io.netty.handler.codec.DefaultHeaders.HeaderDateFormat;
import io.netty.util.internal.PlatformDependent;
import java.text.ParseException;
/**
* Converts to/from native types, general {@link Object}, and {@link CharSequence}s.
*/
public class CharSequenceValueConverter implements ValueConverter<CharSequence> {
public static final CharSequenceValueConverter INSTANCE = new CharSequenceValueConverter();
@Override
public CharSequence convertObject(Object value) {
if (value instanceof CharSequence) {
return (CharSequence) value;
}
return value.toString();
}
@Override
public CharSequence convertInt(int value) {
return String.valueOf(value);
}
@Override
public CharSequence convertLong(long value) {
return String.valueOf(value);
}
@Override
public CharSequence convertDouble(double value) {
return String.valueOf(value);
}
@Override
public CharSequence convertChar(char value) {
return String.valueOf(value);
}
@Override
public CharSequence convertBoolean(boolean value) {
return String.valueOf(value);
}
@Override
public CharSequence convertFloat(float value) {
return String.valueOf(value);
}
@Override
public boolean convertToBoolean(CharSequence value) {
return Boolean.parseBoolean(value.toString());
}
@Override
public CharSequence convertByte(byte value) {
return String.valueOf(value);
}
@Override
public byte convertToByte(CharSequence value) {
return Byte.valueOf(value.toString());
}
@Override
public char convertToChar(CharSequence value) {
if (value.length() == 0) {
throw new IllegalArgumentException("'value' is empty.");
}
return value.charAt(0);
}
@Override
public CharSequence convertShort(short value) {
return String.valueOf(value);
}
@Override
public short convertToShort(CharSequence value) {
return Short.valueOf(value.toString());
}
@Override
public int convertToInt(CharSequence value) {
return Integer.parseInt(value.toString());
}
@Override
public long convertToLong(CharSequence value) {
return Long.parseLong(value.toString());
}
@Override
public CharSequence convertTimeMillis(long value) {
return String.valueOf(value);
}
@Override
public long convertToTimeMillis(CharSequence value) {
try {
return HeaderDateFormat.get().parse(value.toString());
} catch (ParseException e) {
PlatformDependent.throwException(e);
}
return 0;
}
@Override
public float convertToFloat(CharSequence value) {
return Float.valueOf(value.toString());
}
@Override
public double convertToDouble(CharSequence value) {
return Double.valueOf(value.toString());
}
}

View File

@ -1,105 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.netty.handler.codec;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Extension to the {@link Headers} interface to provide methods which convert the
* native {@code UnconvertedType} to the not-native {@code ConvertedType}
*/
public interface ConvertibleHeaders<UnconvertedType, ConvertedType> extends Headers<UnconvertedType> {
/**
* Interface to do conversions to and from the two generic type parameters
*/
interface TypeConverter<UnconvertedType, ConvertedType> {
/**
* Convert a native value
* @param value The value to be converted
* @return The conversion results
*/
ConvertedType toConvertedType(UnconvertedType value);
/**
* Undo a conversion and restore the original native type
* @param value The converted value
* @return The original native type
*/
UnconvertedType toUnconvertedType(ConvertedType value);
}
/**
* Invokes {@link Headers#get(Object)} and does a conversion on the results if not {@code null}
* @param name The name of entry to get
* @return The value corresponding to {@code name} and then converted
*/
ConvertedType getAndConvert(UnconvertedType name);
/**
* Invokes {@link Headers#get(Object, Object)} and does a conversion on the results if not {@code null}
* @param name The name of entry to get
* @return The value corresponding to {@code name} and then converted
*/
ConvertedType getAndConvert(UnconvertedType name, ConvertedType defaultValue);
/**
* Invokes {@link Headers#getAndRemove(Object)} and does a conversion on the results if not {@code null}
* @param name The name of entry to get
* @return The value corresponding to {@code name} and then converted
*/
ConvertedType getAndRemoveAndConvert(UnconvertedType name);
/**
* Invokes {@link Headers#getAndRemove(Object, Object)} and does
* a conversion on the results if not {@code null}
* @param name The name of entry to get
* @return The value corresponding to {@code name} and then converted
*/
ConvertedType getAndRemoveAndConvert(UnconvertedType name, ConvertedType defaultValue);
/**
* Invokes {@link Headers#getAll(Object)} and does a conversion on the results if not {@code null}
* @param name The name of entry to get
* @return The values corresponding to {@code name} and then converted
*/
List<ConvertedType> getAllAndConvert(UnconvertedType name);
/**
* Invokes {@link Headers#getAllAndRemove(Object)} and does a conversion on the results if not {@code null}
* @param name The name of entry to get
* @return The values corresponding to {@code name} and then converted
*/
List<ConvertedType> getAllAndRemoveAndConvert(UnconvertedType name);
/**
* Invokes {@link Headers#iterator()} and lazily does a conversion on the results as they are accessed
*
* @return Iterator which will provide converted values corresponding to {@code name}
*/
Iterator<Entry<ConvertedType, ConvertedType>> iteratorConverted();
/**
* Invokes {@link Headers#names()} and does a conversion on the results
*
* @return The values corresponding to {@code name} and then converted
*/
Set<ConvertedType> namesAndConvert(Comparator<ConvertedType> comparator);
}

View File

@ -1,361 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.netty.handler.codec;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.PlatformDependent;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
public class DefaultBinaryHeaders extends DefaultHeaders<ByteString> implements BinaryHeaders {
private static final NameValidator<ByteString> NO_NAME_VALIDATOR = DefaultHeaders.NoNameValidator.instance();
public DefaultBinaryHeaders() {
this(new TreeMap<ByteString, Object>(ByteString.DEFAULT_COMPARATOR));
}
public DefaultBinaryHeaders(Map<ByteString, Object> map) {
this(map, NO_NAME_VALIDATOR);
}
public DefaultBinaryHeaders(Map<ByteString, Object> map, NameValidator<ByteString> nameValidator) {
super(map, nameValidator, ByteStringConverter.INSTANCE);
}
@Override
public BinaryHeaders add(ByteString name, ByteString value) {
super.add(name, value);
return this;
}
@Override
public BinaryHeaders add(ByteString name, Iterable<? extends ByteString> values) {
super.add(name, values);
return this;
}
@Override
public BinaryHeaders add(ByteString name, ByteString... values) {
super.add(name, values);
return this;
}
@Override
public BinaryHeaders addObject(ByteString name, Object value) {
super.addObject(name, value);
return this;
}
@Override
public BinaryHeaders addObject(ByteString name, Iterable<?> values) {
super.addObject(name, values);
return this;
}
@Override
public BinaryHeaders addObject(ByteString name, Object... values) {
super.addObject(name, values);
return this;
}
@Override
public BinaryHeaders addBoolean(ByteString name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public BinaryHeaders addChar(ByteString name, char value) {
super.addChar(name, value);
return this;
}
@Override
public BinaryHeaders addByte(ByteString name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public BinaryHeaders addShort(ByteString name, short value) {
super.addShort(name, value);
return this;
}
@Override
public BinaryHeaders addInt(ByteString name, int value) {
super.addInt(name, value);
return this;
}
@Override
public BinaryHeaders addLong(ByteString name, long value) {
super.addLong(name, value);
return this;
}
@Override
public BinaryHeaders addFloat(ByteString name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public BinaryHeaders addDouble(ByteString name, double value) {
super.addDouble(name, value);
return this;
}
@Override
public BinaryHeaders addTimeMillis(ByteString name, long value) {
super.addTimeMillis(name, value);
return this;
}
@Override
public BinaryHeaders add(BinaryHeaders headers) {
super.add(headers);
return this;
}
@Override
public BinaryHeaders set(ByteString name, ByteString value) {
super.set(name, value);
return this;
}
@Override
public BinaryHeaders set(ByteString name, Iterable<? extends ByteString> values) {
super.set(name, values);
return this;
}
@Override
public BinaryHeaders set(ByteString name, ByteString... values) {
super.set(name, values);
return this;
}
@Override
public BinaryHeaders setObject(ByteString name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public BinaryHeaders setObject(ByteString name, Iterable<?> values) {
super.setObject(name, values);
return this;
}
@Override
public BinaryHeaders setObject(ByteString name, Object... values) {
super.setObject(name, values);
return this;
}
@Override
public BinaryHeaders setBoolean(ByteString name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public BinaryHeaders setChar(ByteString name, char value) {
super.setChar(name, value);
return this;
}
@Override
public BinaryHeaders setByte(ByteString name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public BinaryHeaders setShort(ByteString name, short value) {
super.setShort(name, value);
return this;
}
@Override
public BinaryHeaders setInt(ByteString name, int value) {
super.setInt(name, value);
return this;
}
@Override
public BinaryHeaders setLong(ByteString name, long value) {
super.setLong(name, value);
return this;
}
@Override
public BinaryHeaders setFloat(ByteString name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public BinaryHeaders setDouble(ByteString name, double value) {
super.setDouble(name, value);
return this;
}
@Override
public BinaryHeaders setTimeMillis(ByteString name, long value) {
super.setTimeMillis(name, value);
return this;
}
@Override
public BinaryHeaders set(BinaryHeaders headers) {
super.set(headers);
return this;
}
@Override
public BinaryHeaders setAll(BinaryHeaders headers) {
super.setAll(headers);
return this;
}
@Override
public BinaryHeaders clear() {
super.clear();
return this;
}
public static final class ByteStringConverter implements ValueConverter<ByteString> {
public static final ByteStringConverter INSTANCE = new ByteStringConverter();
private static final Charset DEFAULT_CHARSET = CharsetUtil.UTF_8;
private ByteStringConverter() {
}
@Override
public ByteString convertObject(Object value) {
if (value instanceof ByteString) {
return (ByteString) value;
}
if (value instanceof CharSequence) {
return new ByteString((CharSequence) value, DEFAULT_CHARSET);
}
return new ByteString(value.toString(), DEFAULT_CHARSET);
}
@Override
public ByteString convertInt(int value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public ByteString convertLong(long value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public ByteString convertDouble(double value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public ByteString convertChar(char value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public ByteString convertBoolean(boolean value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public ByteString convertFloat(float value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public int convertToInt(ByteString value) {
return value.parseAsciiInt();
}
@Override
public long convertToLong(ByteString value) {
return value.parseAsciiLong();
}
@Override
public ByteString convertTimeMillis(long value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public long convertToTimeMillis(ByteString value) {
try {
return HeaderDateFormat.get().parse(value.toString());
} catch (ParseException e) {
PlatformDependent.throwException(e);
}
return 0;
}
@Override
public double convertToDouble(ByteString value) {
return value.parseAsciiDouble();
}
@Override
public char convertToChar(ByteString value) {
return value.parseChar();
}
@Override
public boolean convertToBoolean(ByteString value) {
return value.byteAt(0) != 0;
}
@Override
public float convertToFloat(ByteString value) {
return value.parseAsciiFloat();
}
@Override
public ByteString convertShort(short value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public short convertToShort(ByteString value) {
return value.parseAsciiShort();
}
@Override
public ByteString convertByte(byte value) {
return new ByteString(String.valueOf(value), DEFAULT_CHARSET);
}
@Override
public byte convertToByte(ByteString value) {
return value.byteAt(0);
}
}
}

View File

@ -1,160 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.netty.handler.codec;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
public class DefaultConvertibleHeaders<UnconvertedType, ConvertedType> extends DefaultHeaders<UnconvertedType>
implements ConvertibleHeaders<UnconvertedType, ConvertedType> {
private final TypeConverter<UnconvertedType, ConvertedType> typeConverter;
public DefaultConvertibleHeaders(Map<UnconvertedType, Object> map,
NameValidator<UnconvertedType> nameValidator,
ValueConverter<UnconvertedType> valueConverter,
TypeConverter<UnconvertedType, ConvertedType> typeConverter) {
super(map, nameValidator, valueConverter);
this.typeConverter = typeConverter;
}
@Override
public ConvertedType getAndConvert(UnconvertedType name) {
return getAndConvert(name, null);
}
@Override
public ConvertedType getAndConvert(UnconvertedType name, ConvertedType defaultValue) {
UnconvertedType v = get(name);
if (v == null) {
return defaultValue;
}
return typeConverter.toConvertedType(v);
}
@Override
public ConvertedType getAndRemoveAndConvert(UnconvertedType name) {
return getAndRemoveAndConvert(name, null);
}
@Override
public ConvertedType getAndRemoveAndConvert(UnconvertedType name, ConvertedType defaultValue) {
UnconvertedType v = getAndRemove(name);
if (v == null) {
return defaultValue;
}
return typeConverter.toConvertedType(v);
}
@Override
public List<ConvertedType> getAllAndConvert(UnconvertedType name) {
List<UnconvertedType> all = getAll(name);
List<ConvertedType> allConverted = new ArrayList<ConvertedType>(all.size());
for (int i = 0; i < all.size(); ++i) {
allConverted.add(typeConverter.toConvertedType(all.get(i)));
}
return allConverted;
}
@Override
public List<ConvertedType> getAllAndRemoveAndConvert(UnconvertedType name) {
List<UnconvertedType> all = getAllAndRemove(name);
List<ConvertedType> allConverted = new ArrayList<ConvertedType>(all.size());
for (int i = 0; i < all.size(); ++i) {
allConverted.add(typeConverter.toConvertedType(all.get(i)));
}
return allConverted;
}
@Override
public Iterator<Entry<ConvertedType, ConvertedType>> iteratorConverted() {
return new ConvertedIterator();
}
@Override
public Set<ConvertedType> namesAndConvert(Comparator<ConvertedType> comparator) {
Set<UnconvertedType> names = names();
Set<ConvertedType> namesConverted = new TreeSet<ConvertedType>(comparator);
for (UnconvertedType unconverted : names) {
namesConverted.add(typeConverter.toConvertedType(unconverted));
}
return namesConverted;
}
private final class ConvertedIterator implements Iterator<Entry<ConvertedType, ConvertedType>> {
private final Iterator<Entry<UnconvertedType, UnconvertedType>> iter = iterator();
@Override
public boolean hasNext() {
return iter.hasNext();
}
@Override
public Entry<ConvertedType, ConvertedType> next() {
Entry<UnconvertedType, UnconvertedType> next = iter.next();
return new ConvertedEntry(next);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
private final class ConvertedEntry implements Entry<ConvertedType, ConvertedType> {
private final Entry<UnconvertedType, UnconvertedType> entry;
private ConvertedType name;
private ConvertedType value;
ConvertedEntry(Entry<UnconvertedType, UnconvertedType> entry) {
this.entry = entry;
}
@Override
public ConvertedType getKey() {
if (name == null) {
name = typeConverter.toConvertedType(entry.getKey());
}
return name;
}
@Override
public ConvertedType getValue() {
if (value == null) {
value = typeConverter.toConvertedType(entry.getValue());
}
return value;
}
@Override
public ConvertedType setValue(ConvertedType value) {
ConvertedType old = getValue();
entry.setValue(typeConverter.toUnconvertedType(value));
return old;
}
@Override
public String toString() {
return entry.toString();
}
}
}

View File

@ -1,616 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec;
import static io.netty.util.AsciiString.CHARSEQUENCE_CASE_INSENSITIVE_ORDER;
import static io.netty.util.AsciiString.CHARSEQUENCE_CASE_SENSITIVE_ORDER;
import static io.netty.util.internal.StringUtil.COMMA;
import io.netty.util.AsciiString;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;
import java.text.ParseException;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
public class DefaultTextHeaders extends DefaultConvertibleHeaders<CharSequence, String> implements TextHeaders {
public static final NameValidator<CharSequence> NO_NAME_VALIDATOR = NoNameValidator.instance();
private final ValuesComposer valuesComposer;
public DefaultTextHeaders() {
this(true);
}
public DefaultTextHeaders(boolean singleHeaderFields) {
this(new TreeMap<CharSequence, Object>(CHARSEQUENCE_CASE_INSENSITIVE_ORDER), NO_NAME_VALIDATOR,
CharSequenceConverter.INSTANCE, singleHeaderFields);
}
public DefaultTextHeaders(Map<CharSequence, Object> map,
NameValidator<CharSequence> nameValidator,
ValueConverter<CharSequence> valueConverter,
boolean singleHeaderFields) {
super(map, nameValidator, valueConverter, CharSequenceToStringConverter.INSTANCE);
valuesComposer = singleHeaderFields ? new SingleHeaderValuesComposer() : new MultipleFieldsValueComposer();
}
@Override
public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) {
return contains(name, value,
ignoreCase ? CHARSEQUENCE_CASE_INSENSITIVE_ORDER : CHARSEQUENCE_CASE_SENSITIVE_ORDER);
}
@Override
public TextHeaders add(CharSequence name, CharSequence value) {
return valuesComposer.add(name, value);
}
@Override
public TextHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
return valuesComposer.add(name, values);
}
@Override
public TextHeaders add(CharSequence name, CharSequence... values) {
return valuesComposer.add(name, values);
}
@Override
public TextHeaders addObject(CharSequence name, Object value) {
return valuesComposer.addObject(name, value);
}
@Override
public TextHeaders addObject(CharSequence name, Iterable<?> values) {
return valuesComposer.addObject(name, values);
}
@Override
public TextHeaders addObject(CharSequence name, Object... values) {
return valuesComposer.addObject(name, values);
}
@Override
public TextHeaders addBoolean(CharSequence name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public TextHeaders addChar(CharSequence name, char value) {
super.addChar(name, value);
return this;
}
@Override
public TextHeaders addByte(CharSequence name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public TextHeaders addShort(CharSequence name, short value) {
super.addShort(name, value);
return this;
}
@Override
public TextHeaders addInt(CharSequence name, int value) {
super.addInt(name, value);
return this;
}
@Override
public TextHeaders addLong(CharSequence name, long value) {
super.addLong(name, value);
return this;
}
@Override
public TextHeaders addFloat(CharSequence name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public TextHeaders addDouble(CharSequence name, double value) {
super.addDouble(name, value);
return this;
}
@Override
public TextHeaders addTimeMillis(CharSequence name, long value) {
super.addTimeMillis(name, value);
return this;
}
@Override
public TextHeaders add(TextHeaders headers) {
super.add(headers);
return this;
}
@Override
public TextHeaders set(CharSequence name, CharSequence value) {
super.set(name, value);
return this;
}
@Override
public TextHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
return valuesComposer.set(name, values);
}
@Override
public TextHeaders set(CharSequence name, CharSequence... values) {
return valuesComposer.set(name, values);
}
@Override
public TextHeaders setObject(CharSequence name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public TextHeaders setObject(CharSequence name, Iterable<?> values) {
return valuesComposer.setObject(name, values);
}
@Override
public TextHeaders setObject(CharSequence name, Object... values) {
return valuesComposer.setObject(name, values);
}
@Override
public TextHeaders setBoolean(CharSequence name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public TextHeaders setChar(CharSequence name, char value) {
super.setChar(name, value);
return this;
}
@Override
public TextHeaders setByte(CharSequence name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public TextHeaders setShort(CharSequence name, short value) {
super.setShort(name, value);
return this;
}
@Override
public TextHeaders setInt(CharSequence name, int value) {
super.setInt(name, value);
return this;
}
@Override
public TextHeaders setLong(CharSequence name, long value) {
super.setLong(name, value);
return this;
}
@Override
public TextHeaders setFloat(CharSequence name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public TextHeaders setDouble(CharSequence name, double value) {
super.setDouble(name, value);
return this;
}
@Override
public TextHeaders setTimeMillis(CharSequence name, long value) {
super.setTimeMillis(name, value);
return this;
}
@Override
public TextHeaders set(TextHeaders headers) {
super.set(headers);
return this;
}
@Override
public TextHeaders setAll(TextHeaders headers) {
super.setAll(headers);
return this;
}
@Override
public TextHeaders clear() {
super.clear();
return this;
}
/*
* This interface enables different implementations for adding/setting header values.
* Concrete implementations can control how values are added, for example to add all
* values for a header as a comma separated string instead of adding them as multiple
* headers with a single value.
*/
private interface ValuesComposer {
TextHeaders add(CharSequence name, CharSequence value);
TextHeaders add(CharSequence name, CharSequence... values);
TextHeaders add(CharSequence name, Iterable<? extends CharSequence> values);
TextHeaders addObject(CharSequence name, Object value);
TextHeaders addObject(CharSequence name, Iterable<?> values);
TextHeaders addObject(CharSequence name, Object... values);
TextHeaders set(CharSequence name, CharSequence... values);
TextHeaders set(CharSequence name, Iterable<? extends CharSequence> values);
TextHeaders setObject(CharSequence name, Object... values);
TextHeaders setObject(CharSequence name, Iterable<?> values);
}
/*
* Will add multiple values for the same header as multiple separate headers.
*/
private final class MultipleFieldsValueComposer implements ValuesComposer {
@Override
public TextHeaders add(CharSequence name, CharSequence value) {
DefaultTextHeaders.super.add(name, value);
return DefaultTextHeaders.this;
}
@Override
public TextHeaders add(CharSequence name, CharSequence... values) {
DefaultTextHeaders.super.add(name, values);
return DefaultTextHeaders.this;
}
@Override
public TextHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
DefaultTextHeaders.super.add(name, values);
return DefaultTextHeaders.this;
}
@Override
public TextHeaders addObject(CharSequence name, Object value) {
DefaultTextHeaders.super.addObject(name, value);
return DefaultTextHeaders.this;
}
@Override
public TextHeaders addObject(CharSequence name, Iterable<?> values) {
DefaultTextHeaders.super.addObject(name, values);
return DefaultTextHeaders.this;
}
@Override
public TextHeaders addObject(CharSequence name, Object... values) {
DefaultTextHeaders.super.addObject(name, values);
return DefaultTextHeaders.this;
}
@Override
public TextHeaders set(CharSequence name, CharSequence... values) {
DefaultTextHeaders.super.set(name, values);
return DefaultTextHeaders.this;
}
@Override
public TextHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
DefaultTextHeaders.super.set(name, values);
return DefaultTextHeaders.this;
}
@Override
public TextHeaders setObject(CharSequence name, Object... values) {
DefaultTextHeaders.super.setObject(name, values);
return DefaultTextHeaders.this;
}
@Override
public TextHeaders setObject(CharSequence name, Iterable<?> values) {
DefaultTextHeaders.super.setObject(name, values);
return DefaultTextHeaders.this;
}
}
/**
* Will add multiple values for the same header as single header with a comma separated list of values.
*
* Please refer to section <a href="https://tools.ietf.org/html/rfc7230#section-3.2.2">3.2.2 Field Order</a>
* of RFC-7230 for details.
*/
private final class SingleHeaderValuesComposer implements ValuesComposer {
private final ValueConverter<CharSequence> valueConverter = valueConverter();
private CsvValueEscaper<Object> objectEscaper;
private CsvValueEscaper<CharSequence> charSequenceEscaper;
private CsvValueEscaper<Object> objectEscaper() {
if (objectEscaper == null) {
objectEscaper = new CsvValueEscaper<Object>() {
@Override
public CharSequence escape(Object value) {
return StringUtil.escapeCsv(valueConverter.convertObject(value));
}
};
}
return objectEscaper;
}
private CsvValueEscaper<CharSequence> charSequenceEscaper() {
if (charSequenceEscaper == null) {
charSequenceEscaper = new CsvValueEscaper<CharSequence>() {
@Override
public CharSequence escape(CharSequence value) {
return StringUtil.escapeCsv(value);
}
};
}
return charSequenceEscaper;
}
@Override
public TextHeaders add(CharSequence name, CharSequence value) {
return addEscapedValue(name, StringUtil.escapeCsv(value));
}
@Override
public TextHeaders add(CharSequence name, CharSequence... values) {
return addEscapedValue(name, commaSeparate(charSequenceEscaper(), values));
}
@Override
public TextHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
return addEscapedValue(name, commaSeparate(charSequenceEscaper(), values));
}
@Override
public TextHeaders addObject(CharSequence name, Object value) {
return addEscapedValue(name, objectEscaper().escape(value));
}
@Override
public TextHeaders addObject(CharSequence name, Iterable<?> values) {
return addEscapedValue(name, commaSeparate(objectEscaper(), values));
}
@Override
public TextHeaders addObject(CharSequence name, Object... values) {
return addEscapedValue(name, commaSeparate(objectEscaper(), values));
}
@Override
public TextHeaders set(CharSequence name, CharSequence... values) {
DefaultTextHeaders.super.set(name, commaSeparate(charSequenceEscaper(), values));
return DefaultTextHeaders.this;
}
@Override
public TextHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
DefaultTextHeaders.super.set(name, commaSeparate(charSequenceEscaper(), values));
return DefaultTextHeaders.this;
}
@Override
public TextHeaders setObject(CharSequence name, Object... values) {
DefaultTextHeaders.super.set(name, commaSeparate(objectEscaper(), values));
return DefaultTextHeaders.this;
}
@Override
public TextHeaders setObject(CharSequence name, Iterable<?> values) {
DefaultTextHeaders.super.set(name, commaSeparate(objectEscaper(), values));
return DefaultTextHeaders.this;
}
private TextHeaders addEscapedValue(CharSequence name, CharSequence escapedValue) {
CharSequence currentValue = DefaultTextHeaders.super.get(name);
if (currentValue == null) {
DefaultTextHeaders.super.add(name, escapedValue);
} else {
DefaultTextHeaders.super.set(name, commaSeparateEscapedValues(currentValue, escapedValue));
}
return DefaultTextHeaders.this;
}
private <T> CharSequence commaSeparate(CsvValueEscaper<T> escaper, T... values) {
final int lengthEstimate = 10;
StringBuilder sb = new StringBuilder(values.length * lengthEstimate);
if (values.length > 0) {
int end = values.length - 1;
for (int i = 0; i < end; i++) {
sb.append(escaper.escape(values[i])).append(COMMA);
}
sb.append(escaper.escape(values[end]));
}
return sb;
}
private <T> CharSequence commaSeparate(CsvValueEscaper<T> escaper, Iterable<? extends T> values) {
StringBuilder sb = new StringBuilder();
Iterator<? extends T> iterator = values.iterator();
if (iterator.hasNext()) {
T next = iterator.next();
while (iterator.hasNext()) {
sb.append(escaper.escape(next)).append(COMMA);
next = iterator.next();
}
sb.append(escaper.escape(next));
}
return sb;
}
private CharSequence commaSeparateEscapedValues(CharSequence currentValue, CharSequence value) {
return new StringBuilder(currentValue.length() + 1 + value.length())
.append(currentValue)
.append(COMMA)
.append(value);
}
}
/**
* Escapes comma separated values (CSV).
*
* @param <T> The type that a concrete implementation handles
*/
private interface CsvValueEscaper<T> {
/**
* Appends the value to the specified {@link StringBuilder}, escaping if necessary.
*
* @param value the value to be appended, escaped if necessary
*/
CharSequence escape(T value);
}
private static final class CharSequenceToStringConverter implements TypeConverter<CharSequence, String> {
private static final CharSequenceToStringConverter INSTANCE = new CharSequenceToStringConverter();
@Override
public String toConvertedType(CharSequence value) {
return value.toString();
}
@Override
public CharSequence toUnconvertedType(String value) {
return value;
}
}
public static class CharSequenceConverter implements ValueConverter<CharSequence> {
public static final CharSequenceConverter INSTANCE = new CharSequenceConverter();
@Override
public CharSequence convertObject(Object value) {
if (value instanceof CharSequence) {
return (CharSequence) value;
}
return value.toString();
}
@Override
public CharSequence convertInt(int value) {
return String.valueOf(value);
}
@Override
public CharSequence convertLong(long value) {
return String.valueOf(value);
}
@Override
public CharSequence convertDouble(double value) {
return String.valueOf(value);
}
@Override
public CharSequence convertChar(char value) {
return String.valueOf(value);
}
@Override
public CharSequence convertBoolean(boolean value) {
return String.valueOf(value);
}
@Override
public CharSequence convertFloat(float value) {
return String.valueOf(value);
}
@Override
public boolean convertToBoolean(CharSequence value) {
return Boolean.parseBoolean(value.toString());
}
@Override
public CharSequence convertByte(byte value) {
return String.valueOf(value);
}
@Override
public byte convertToByte(CharSequence value) {
return Byte.valueOf(value.toString());
}
@Override
public char convertToChar(CharSequence value) {
if (value.length() == 0) {
throw new IllegalArgumentException("'value' is empty.");
}
return value.charAt(0);
}
@Override
public CharSequence convertShort(short value) {
return String.valueOf(value);
}
@Override
public short convertToShort(CharSequence value) {
return Short.valueOf(value.toString());
}
@Override
public int convertToInt(CharSequence value) {
return Integer.parseInt(value.toString());
}
@Override
public long convertToLong(CharSequence value) {
return Long.parseLong(value.toString());
}
@Override
public CharSequence convertTimeMillis(long value) {
return String.valueOf(value);
}
@Override
public long convertToTimeMillis(CharSequence value) {
try {
return HeaderDateFormat.get().parse(value.toString());
} catch (ParseException e) {
PlatformDependent.throwException(e);
}
return 0;
}
@Override
public float convertToFloat(CharSequence value) {
return Float.valueOf(value.toString());
}
@Override
public double convertToDouble(CharSequence value) {
return Double.valueOf(value.toString());
}
}
}

View File

@ -1,228 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec;
import io.netty.util.ByteString;
public class EmptyBinaryHeaders extends EmptyHeaders<ByteString> implements BinaryHeaders {
protected EmptyBinaryHeaders() {
}
@Override
public BinaryHeaders add(ByteString name, ByteString value) {
super.add(name, value);
return this;
}
@Override
public BinaryHeaders add(ByteString name, Iterable<? extends ByteString> values) {
super.add(name, values);
return this;
}
@Override
public BinaryHeaders add(ByteString name, ByteString... values) {
super.add(name, values);
return this;
}
@Override
public BinaryHeaders addObject(ByteString name, Object value) {
super.addObject(name, value);
return this;
}
@Override
public BinaryHeaders addObject(ByteString name, Iterable<?> values) {
super.addObject(name, values);
return this;
}
@Override
public BinaryHeaders addObject(ByteString name, Object... values) {
super.addObject(name, values);
return this;
}
@Override
public BinaryHeaders addBoolean(ByteString name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public BinaryHeaders addChar(ByteString name, char value) {
super.addChar(name, value);
return this;
}
@Override
public BinaryHeaders addByte(ByteString name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public BinaryHeaders addShort(ByteString name, short value) {
super.addShort(name, value);
return this;
}
@Override
public BinaryHeaders addInt(ByteString name, int value) {
super.addInt(name, value);
return this;
}
@Override
public BinaryHeaders addLong(ByteString name, long value) {
super.addLong(name, value);
return this;
}
@Override
public BinaryHeaders addFloat(ByteString name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public BinaryHeaders addDouble(ByteString name, double value) {
super.addDouble(name, value);
return this;
}
@Override
public BinaryHeaders addTimeMillis(ByteString name, long value) {
super.addTimeMillis(name, value);
return this;
}
@Override
public BinaryHeaders add(BinaryHeaders headers) {
super.add(headers);
return this;
}
@Override
public BinaryHeaders set(ByteString name, ByteString value) {
super.set(name, value);
return this;
}
@Override
public BinaryHeaders set(ByteString name, Iterable<? extends ByteString> values) {
super.set(name, values);
return this;
}
@Override
public BinaryHeaders set(ByteString name, ByteString... values) {
super.set(name, values);
return this;
}
@Override
public BinaryHeaders setObject(ByteString name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public BinaryHeaders setObject(ByteString name, Iterable<?> values) {
super.setObject(name, values);
return this;
}
@Override
public BinaryHeaders setObject(ByteString name, Object... values) {
super.setObject(name, values);
return this;
}
@Override
public BinaryHeaders setBoolean(ByteString name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public BinaryHeaders setChar(ByteString name, char value) {
super.setChar(name, value);
return this;
}
@Override
public BinaryHeaders setByte(ByteString name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public BinaryHeaders setShort(ByteString name, short value) {
super.setShort(name, value);
return this;
}
@Override
public BinaryHeaders setInt(ByteString name, int value) {
super.setInt(name, value);
return this;
}
@Override
public BinaryHeaders setLong(ByteString name, long value) {
super.setLong(name, value);
return this;
}
@Override
public BinaryHeaders setFloat(ByteString name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public BinaryHeaders setDouble(ByteString name, double value) {
super.setDouble(name, value);
return this;
}
@Override
public BinaryHeaders setTimeMillis(ByteString name, long value) {
super.setTimeMillis(name, value);
return this;
}
@Override
public BinaryHeaders set(BinaryHeaders headers) {
super.set(headers);
return this;
}
@Override
public BinaryHeaders setAll(BinaryHeaders headers) {
super.setAll(headers);
return this;
}
@Override
public BinaryHeaders clear() {
super.clear();
return this;
}
}

View File

@ -1,67 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.netty.handler.codec;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
public class EmptyConvertibleHeaders<UnconvertedType, ConvertedType> extends
EmptyHeaders<UnconvertedType> implements ConvertibleHeaders<UnconvertedType, ConvertedType> {
@Override
public ConvertedType getAndConvert(UnconvertedType name) {
return null;
}
@Override
public ConvertedType getAndConvert(UnconvertedType name, ConvertedType defaultValue) {
return defaultValue;
}
@Override
public ConvertedType getAndRemoveAndConvert(UnconvertedType name) {
return null;
}
@Override
public ConvertedType getAndRemoveAndConvert(UnconvertedType name, ConvertedType defaultValue) {
return defaultValue;
}
@Override
public List<ConvertedType> getAllAndConvert(UnconvertedType name) {
return Collections.emptyList();
}
@Override
public List<ConvertedType> getAllAndRemoveAndConvert(UnconvertedType name) {
return Collections.emptyList();
}
@Override
public Iterator<Entry<ConvertedType, ConvertedType>> iteratorConverted() {
List<Entry<ConvertedType, ConvertedType>> empty = Collections.emptyList();
return empty.iterator();
}
@Override
public Set<ConvertedType> namesAndConvert(Comparator<ConvertedType> comparator) {
return Collections.emptySet();
}
}

View File

@ -15,7 +15,6 @@
package io.netty.handler.codec;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
@ -292,11 +291,6 @@ public class EmptyHeaders<T> implements Headers<T> {
return false;
}
@Override
public boolean contains(T name, T value, Comparator<? super T> valueComparator) {
return false;
}
@Override
public int size() {
return 0;

View File

@ -1,231 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec;
public class EmptyTextHeaders extends EmptyConvertibleHeaders<CharSequence, String> implements TextHeaders {
protected EmptyTextHeaders() {
}
@Override
public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) {
return false;
}
@Override
public TextHeaders add(CharSequence name, CharSequence value) {
super.add(name, value);
return this;
}
@Override
public TextHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
super.add(name, values);
return this;
}
@Override
public TextHeaders add(CharSequence name, CharSequence... values) {
super.add(name, values);
return this;
}
@Override
public TextHeaders addObject(CharSequence name, Object value) {
super.addObject(name, value);
return this;
}
@Override
public TextHeaders addObject(CharSequence name, Iterable<?> values) {
super.addObject(name, values);
return this;
}
@Override
public TextHeaders addObject(CharSequence name, Object... values) {
super.addObject(name, values);
return this;
}
@Override
public TextHeaders addBoolean(CharSequence name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public TextHeaders addChar(CharSequence name, char value) {
super.addChar(name, value);
return this;
}
@Override
public TextHeaders addByte(CharSequence name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public TextHeaders addShort(CharSequence name, short value) {
super.addShort(name, value);
return this;
}
@Override
public TextHeaders addInt(CharSequence name, int value) {
super.addInt(name, value);
return this;
}
@Override
public TextHeaders addLong(CharSequence name, long value) {
super.addLong(name, value);
return this;
}
@Override
public TextHeaders addFloat(CharSequence name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public TextHeaders addDouble(CharSequence name, double value) {
super.addDouble(name, value);
return this;
}
@Override
public TextHeaders addTimeMillis(CharSequence name, long value) {
super.addTimeMillis(name, value);
return this;
}
@Override
public TextHeaders add(TextHeaders headers) {
super.add(headers);
return this;
}
@Override
public TextHeaders set(CharSequence name, CharSequence value) {
super.set(name, value);
return this;
}
@Override
public TextHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
super.set(name, values);
return this;
}
@Override
public TextHeaders set(CharSequence name, CharSequence... values) {
super.set(name, values);
return this;
}
@Override
public TextHeaders setObject(CharSequence name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public TextHeaders setObject(CharSequence name, Iterable<?> values) {
super.setObject(name, values);
return this;
}
@Override
public TextHeaders setObject(CharSequence name, Object... values) {
super.setObject(name, values);
return this;
}
@Override
public TextHeaders setBoolean(CharSequence name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public TextHeaders setChar(CharSequence name, char value) {
super.setChar(name, value);
return this;
}
@Override
public TextHeaders setByte(CharSequence name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public TextHeaders setShort(CharSequence name, short value) {
super.setShort(name, value);
return this;
}
@Override
public TextHeaders setInt(CharSequence name, int value) {
super.setInt(name, value);
return this;
}
@Override
public TextHeaders setLong(CharSequence name, long value) {
super.setLong(name, value);
return this;
}
@Override
public TextHeaders setFloat(CharSequence name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public TextHeaders setDouble(CharSequence name, double value) {
super.setDouble(name, value);
return this;
}
@Override
public TextHeaders setTimeMillis(CharSequence name, long value) {
super.setTimeMillis(name, value);
return this;
}
@Override
public TextHeaders set(TextHeaders headers) {
super.set(headers);
return this;
}
@Override
public TextHeaders setAll(TextHeaders headers) {
super.setAll(headers);
return this;
}
@Override
public TextHeaders clear() {
super.clear();
return this;
}
}

View File

@ -14,57 +14,12 @@
*/
package io.netty.handler.codec;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
public interface Headers<T> extends Iterable<Entry<T, T>> {
/**
* Converts to/from a generic object to the type of the headers.
*/
interface ValueConverter<T> {
T convertObject(Object value);
T convertBoolean(boolean value);
boolean convertToBoolean(T value);
T convertByte(byte value);
byte convertToByte(T value);
T convertChar(char value);
char convertToChar(T value);
T convertShort(short value);
short convertToShort(T value);
T convertInt(int value);
int convertToInt(T value);
T convertLong(long value);
long convertToLong(T value);
T convertTimeMillis(long value);
long convertToTimeMillis(T value);
T convertFloat(float value);
float convertToFloat(T value);
T convertDouble(double value);
double convertToDouble(T value);
}
/**
* Returns the value of a header with the specified name. If there is more than one value for the specified name,
* the first value in insertion order is returned.
@ -313,7 +268,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the {@code boolean} value of a header with the specified {@code name} and removes the header from this
* object. If there is more than one value for the specified name, the first value in insertion order is returned.
* In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to retrieve
* @return the {@code boolean} value of the first value in insertion order or {@code null} if there is no
* such value or it can't be converted to {@code boolean}.
@ -324,7 +281,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the {@code boolean} value of a header with the specified {@code name} and removes the header from this
* object. If there is more than one value for the specified name, the first value in insertion order is returned.
* In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to search
* @param defaultValue the default value
* @return the {@code boolean} value of the first value in insertion order or {@code defaultValue} if there is no
@ -336,7 +295,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the {@code byte} value of a header with the specified {@code name} and removes the header from this
* object. If there is more than one value for the specified name, the first value in insertion order is returned.
* In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to search
* @return the {@code byte} value of the first value in insertion order or {@code null} if there is no
* such value or it can't be converted to {@code byte}.
@ -347,7 +308,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the {@code byte} value of a header with the specified {@code name} and removes the header from this
* object. If there is more than one value for the specified name, the first value in insertion order is returned.
* In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to search
* @param defaultValue the default value
* @return the {@code byte} value of the first value in insertion order or {@code defaultValue} if there is no
@ -359,7 +322,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the {@code char} value of a header with the specified {@code name} and removes the header from this
* object. If there is more than one value for the specified name, the first value in insertion order is returned.
* In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to search
* @return the {@code char} value of the first value in insertion order or {@code null} if there is no
* such value or it can't be converted to {@code char}.
@ -370,7 +335,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the {@code char} value of a header with the specified {@code name} and removes the header from this
* object. If there is more than one value for the specified name, the first value in insertion order is returned.
* In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to search
* @param defaultValue the default value
* @return the {@code char} value of the first value in insertion order or {@code defaultValue} if there is no
@ -382,7 +349,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the {@code short} value of a header with the specified {@code name} and removes the header from this
* object. If there is more than one value for the specified name, the first value in insertion order is returned.
* In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to search
* @return the {@code short} value of the first value in insertion order or {@code null} if there is no
* such value or it can't be converted to {@code short}.
@ -393,7 +362,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the {@code short} value of a header with the specified {@code name} and removes the header from this
* object. If there is more than one value for the specified name, the first value in insertion order is returned.
* In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to search
* @param defaultValue the default value
* @return the {@code short} value of the first value in insertion order or {@code defaultValue} if there is no
@ -405,7 +376,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the {@code int} value of a header with the specified {@code name} and removes the header from this
* object. If there is more than one value for the specified name, the first value in insertion order is returned.
* In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to search
* @return the {@code int} value of the first value in insertion order or {@code null} if there is no
* such value or it can't be converted to {@code int}.
@ -416,7 +389,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the {@code int} value of a header with the specified {@code name} and removes the header from this
* object. If there is more than one value for the specified name, the first value in insertion order is returned.
* In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to search
* @param defaultValue the default value
* @return the {@code int} value of the first value in insertion order or {@code defaultValue} if there is no
@ -428,7 +403,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the {@code long} value of a header with the specified {@code name} and removes the header from this
* object. If there is more than one value for the specified name, the first value in insertion order is returned.
* In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to search
* @return the {@code long} value of the first value in insertion order or {@code null} if there is no
* such value or it can't be converted to {@code long}.
@ -439,7 +416,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the {@code long} value of a header with the specified {@code name} and removes the header from this
* object. If there is more than one value for the specified name, the first value in insertion order is returned.
* In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to search
* @param defaultValue the default value
* @return the {@code long} value of the first value in insertion order or {@code defaultValue} if there is no
@ -451,7 +430,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the {@code float} value of a header with the specified {@code name} and removes the header from this
* object. If there is more than one value for the specified name, the first value in insertion order is returned.
* In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to search
* @return the {@code float} value of the first value in insertion order or {@code null} if there is no
* such value or it can't be converted to {@code float}.
@ -462,7 +443,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the {@code float} value of a header with the specified {@code name} and removes the header from this
* object. If there is more than one value for the specified name, the first value in insertion order is returned.
* In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to search
* @param defaultValue the default value
* @return the {@code float} value of the first value in insertion order or {@code defaultValue} if there is no
@ -474,7 +457,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the {@code double} value of a header with the specified {@code name} and removes the header from this
* object. If there is more than one value for the specified name, the first value in insertion order is returned.
* In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to search
* @return the {@code double} value of the first value in insertion order or {@code null} if there is no
* such value or it can't be converted to {@code double}.
@ -485,7 +470,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the {@code double} value of a header with the specified {@code name} and removes the header from this
* object. If there is more than one value for the specified name, the first value in insertion order is returned.
* In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to search
* @param defaultValue the default value
* @return the {@code double} value of the first value in insertion order or {@code defaultValue} if there is no
@ -497,7 +484,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the value of a header with the specified {@code name} in milliseconds and removes the header from this
* object. If there is more than one value for the specified {@code name}, the first value in insertion order is
* returned. In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to retrieve
* @return the milliseconds value of the first value in insertion order or {@code null} if there is no such
* value or it can't be converted to milliseconds.
@ -508,7 +497,9 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* Returns the value of a header with the specified {@code name} in milliseconds and removes the header from this
* object. If there is more than one value for the specified {@code name}, the first value in insertion order is
* returned. In any case all values for {@code name} are removed.
*
* <p>
* If an exception occurs during the translation from type {@code T} all entries with {@code name} may still
* be removed.
* @param name the name of the header to retrieve
* @param defaultValue the default value
* @return the milliseconds value of the first value in insertion order or {@code defaultValue} if there is no such
@ -529,6 +520,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* The {@link Object#equals(Object)} method is used to test for equality of {@code value}.
* </p>
* @param name the header name
* @param value the header value of the header to find
*/
boolean contains(T name, T value);
@ -622,16 +614,6 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
*/
boolean containsTimeMillis(T name, long value);
/**
* Returns {@code true} if a header with the {@code name} and {@code value} exists.
*
* @param name the header name
* @param value the header value
* @param valueComparator The comparator to use when comparing {@code value} to entries in this map
* @return {@code true} if it contains it {@code false} otherwise
*/
boolean contains(T name, T value, Comparator<? super T> valueComparator);
/**
* Returns the number of headers in this object.
*/
@ -687,8 +669,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
Headers<T> add(T name, T... values);
/**
* Adds a new header. Before the {@code value} is add, it's converted to type {@code T} by a call to
* {@link ValueConverter#convertObject(java.lang.Object)}.
* Adds a new header. Before the {@code value} is added, it's converted to type {@code T}.
*
* @param name the header name
* @param value the value of the header
@ -858,7 +839,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
/**
* Sets a new header. Any existing headers with this name are removed. Before the {@code value} is add, it's
* converted to type {@code T} by a call to {@link ValueConverter#convertObject(java.lang.Object)}.
* converted to type {@code T}.
*
* @param name the header name
* @param value the value of the header

View File

@ -0,0 +1,282 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec;
import java.util.AbstractList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/**
* Provides utility methods related to {@link Headers}.
*/
public final class HeadersUtils {
private HeadersUtils() {
}
/**
* {@link Headers#get(Object)} and convert each element of {@link List} to a {@link String}.
* @param name the name of the header to retrieve
* @return a {@link List} of header values or an empty {@link List} if no values are found.
*/
public static <T> List<String> getAllAsString(Headers<T> headers, T name) {
final List<T> allNames = headers.getAll(name);
return new AbstractList<String>() {
@Override
public String get(int index) {
T value = allNames.get(index);
return value != null ? value.toString() : null;
}
@Override
public int size() {
return allNames.size();
}
};
}
/**
* {@link Headers#get(Object)} and convert the result to a {@link String}.
* @param headers the headers to get the {@code name} from
* @param name the name of the header to retrieve
* @return the first header value if the header is found. {@code null} if there's no such entry.
*/
public static <T> String getAsString(Headers<T> headers, T name) {
T orig = headers.get(name);
return orig != null ? orig.toString() : null;
}
/**
* {@link Headers#iterator()} which converts each {@link Entry}'s key and value to a {@link String}.
*/
public static Iterator<Entry<String, String>> iteratorAsString(
Iterable<Entry<CharSequence, CharSequence>> headers) {
return new StringEntryIterator(headers.iterator());
}
/**
* {@link Headers#names()} and convert each element of {@link Set} to a {@link String}.
* @param headers the headers to get the names from
* @return a {@link Set} of header values or an empty {@link Set} if no values are found.
*/
public static Set<String> namesAsString(Headers<CharSequence> headers) {
return new CharSequenceDelegatingStringSet(headers.names());
}
private static final class StringEntryIterator implements Iterator<Entry<String, String>> {
private final Iterator<Entry<CharSequence, CharSequence>> iter;
public StringEntryIterator(Iterator<Entry<CharSequence, CharSequence>> iter) {
this.iter = iter;
}
@Override
public boolean hasNext() {
return iter.hasNext();
}
@Override
public Entry<String, String> next() {
return new StringEntry(iter.next());
}
@Override
public void remove() {
iter.remove();
}
}
private static final class StringEntry implements Entry<String, String> {
private final Entry<CharSequence, CharSequence> entry;
private String name;
private String value;
StringEntry(Entry<CharSequence, CharSequence> entry) {
this.entry = entry;
}
@Override
public String getKey() {
if (name == null) {
name = entry.getKey().toString();
}
return name;
}
@Override
public String getValue() {
if (value == null && entry.getValue() != null) {
value = entry.getValue().toString();
}
return value;
}
@Override
public String setValue(String value) {
String old = getValue();
entry.setValue(value);
return old;
}
@Override
public String toString() {
return entry.toString();
}
}
private static final class StringIterator<T> implements Iterator<String> {
private final Iterator<T> iter;
public StringIterator(Iterator<T> iter) {
this.iter = iter;
}
@Override
public boolean hasNext() {
return iter.hasNext();
}
@Override
public String next() {
T next = iter.next();
return next != null ? next.toString() : null;
}
@Override
public void remove() {
iter.remove();
}
}
private static final class CharSequenceDelegatingStringSet extends DelegatingStringSet<CharSequence> {
public CharSequenceDelegatingStringSet(Set<CharSequence> allNames) {
super(allNames);
}
@Override
public boolean add(String e) {
return allNames.add(e);
}
@Override
public boolean addAll(Collection<? extends String> c) {
return allNames.addAll(c);
}
}
private abstract static class DelegatingStringSet<T> implements Set<String> {
protected final Set<T> allNames;
public DelegatingStringSet(Set<T> allNames) {
this.allNames = checkNotNull(allNames, "allNames");
}
@Override
public int size() {
return allNames.size();
}
@Override
public boolean isEmpty() {
return allNames.isEmpty();
}
@Override
public boolean contains(Object o) {
return allNames.contains(o.toString());
}
@Override
public Iterator<String> iterator() {
return new StringIterator<T>(allNames.iterator());
}
@Override
public Object[] toArray() {
Object[] arr = new String[size()];
fillArray(arr);
return arr;
}
@SuppressWarnings("unchecked")
@Override
public <X> X[] toArray(X[] a) {
if (a == null || a.length < size()) {
X[] arr = (X[]) new Object[size()];
fillArray(arr);
return arr;
}
fillArray(a);
return a;
}
private void fillArray(Object[] arr) {
Iterator<T> itr = allNames.iterator();
for (int i = 0; i < size(); ++i) {
arr[i] = itr.next();
}
}
@Override
public boolean remove(Object o) {
return allNames.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
for (Object o : c) {
if (!contains(o)) {
return false;
}
}
return true;
}
@Override
public boolean removeAll(Collection<?> c) {
boolean modified = false;
for (Object o : c) {
if (remove(o)) {
modified = true;
}
}
return modified;
}
@Override
public boolean retainAll(Collection<?> c) {
boolean modified = false;
Iterator<String> it = iterator();
while (it.hasNext()) {
if (!c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
@Override
public void clear() {
allNames.clear();
}
}
}

View File

@ -1,144 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec;
/**
* A typical string multimap used by text protocols such as HTTP for the representation of arbitrary key-value data. One
* thing to note is that it uses {@link CharSequence} as its primary key and value type rather than {@link String}. When
* you invoke the operations that produce {@link String}s such as {@link #get(Object)}, a {@link CharSequence} is
* implicitly converted to a {@link String}. This is particularly useful for speed optimization because this multimap
* can hold a special {@link CharSequence} implementation that a codec can treat specially, such as {@link CharSequence}
* .
*/
public interface TextHeaders extends ConvertibleHeaders<CharSequence, String> {
/**
* Returns {@code true} if a header with the name and value exists.
* @param name the header name
* @param value the header value
* @return {@code true} if it contains it {@code false} otherwise
*/
boolean contains(CharSequence name, CharSequence value, boolean ignoreCase);
@Override
TextHeaders add(CharSequence name, CharSequence value);
@Override
TextHeaders add(CharSequence name, Iterable<? extends CharSequence> values);
@Override
TextHeaders add(CharSequence name, CharSequence... values);
@Override
TextHeaders addObject(CharSequence name, Object value);
@Override
TextHeaders addObject(CharSequence name, Iterable<?> values);
@Override
TextHeaders addObject(CharSequence name, Object... values);
@Override
TextHeaders addBoolean(CharSequence name, boolean value);
@Override
TextHeaders addByte(CharSequence name, byte value);
@Override
TextHeaders addChar(CharSequence name, char value);
@Override
TextHeaders addShort(CharSequence name, short value);
@Override
TextHeaders addInt(CharSequence name, int value);
@Override
TextHeaders addLong(CharSequence name, long value);
@Override
TextHeaders addFloat(CharSequence name, float value);
@Override
TextHeaders addDouble(CharSequence name, double value);
@Override
TextHeaders addTimeMillis(CharSequence name, long value);
/**
* See {@link Headers#add(Headers)}
*/
TextHeaders add(TextHeaders headers);
@Override
TextHeaders set(CharSequence name, CharSequence value);
@Override
TextHeaders set(CharSequence name, Iterable<? extends CharSequence> values);
@Override
TextHeaders set(CharSequence name, CharSequence... values);
@Override
TextHeaders setObject(CharSequence name, Object value);
@Override
TextHeaders setObject(CharSequence name, Iterable<?> values);
@Override
TextHeaders setObject(CharSequence name, Object... values);
@Override
TextHeaders setBoolean(CharSequence name, boolean value);
@Override
TextHeaders setByte(CharSequence name, byte value);
@Override
TextHeaders setChar(CharSequence name, char value);
@Override
TextHeaders setShort(CharSequence name, short value);
@Override
TextHeaders setInt(CharSequence name, int value);
@Override
TextHeaders setLong(CharSequence name, long value);
@Override
TextHeaders setFloat(CharSequence name, float value);
@Override
TextHeaders setDouble(CharSequence name, double value);
@Override
TextHeaders setTimeMillis(CharSequence name, long value);
/**
* See {@link Headers#set(Headers)}
*/
TextHeaders set(TextHeaders headers);
/**
* See {@link Headers#setAll(Headers)}
*/
TextHeaders setAll(TextHeaders headers);
@Override
TextHeaders clear();
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/package io.netty.handler.codec;
/**
* Converts to/from a generic object to the type.
*/
public interface ValueConverter<T> {
T convertObject(Object value);
T convertBoolean(boolean value);
boolean convertToBoolean(T value);
T convertByte(byte value);
byte convertToByte(T value);
T convertChar(char value);
char convertToChar(T value);
T convertShort(short value);
short convertToShort(T value);
T convertInt(int value);
int convertToInt(T value);
T convertLong(long value);
long convertToLong(T value);
T convertTimeMillis(long value);
long convertToTimeMillis(T value);
T convertFloat(float value);
float convertToFloat(T value);
T convertDouble(double value);
double convertToDouble(T value);
}

View File

@ -21,28 +21,30 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import io.netty.util.ByteString;
import java.text.ParsePosition;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import io.netty.util.CharsetUtil;
import org.junit.Test;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil;
/**
* Tests for {@link DefaultBinaryHeaders}.
* Tests for {@link DefaultHeaders}.
*/
public class DefaultBinaryHeadersTest {
public class DefaultHeadersTest {
private Headers<ByteString> newInstance() {
return new DefaultHeaders<ByteString>(ByteStringValueConverter.INSTANCE);
}
@Test
public void addShouldIncreaseAndRemoveShouldDecreaseTheSize() {
DefaultBinaryHeaders headers = new DefaultBinaryHeaders();
Headers<ByteString> headers = newInstance();
assertEquals(0, headers.size());
headers.add(bs("name1"), bs("value1"), bs("value2"));
assertEquals(2, headers.size());
@ -62,7 +64,7 @@ public class DefaultBinaryHeadersTest {
@Test
public void afterClearHeadersShouldBeEmpty() {
DefaultBinaryHeaders headers = new DefaultBinaryHeaders();
Headers<ByteString> headers = newInstance();
headers.add(bs("name1"), bs("value1"));
headers.add(bs("name2"), bs("value2"));
assertEquals(2, headers.size());
@ -75,7 +77,7 @@ public class DefaultBinaryHeadersTest {
@Test
public void removingANameForASecondTimeShouldReturnFalse() {
DefaultBinaryHeaders headers = new DefaultBinaryHeaders();
Headers<ByteString> headers = newInstance();
headers.add(bs("name1"), bs("value1"));
headers.add(bs("name2"), bs("value2"));
assertTrue(headers.remove(bs("name2")));
@ -84,7 +86,7 @@ public class DefaultBinaryHeadersTest {
@Test
public void multipleValuesPerNameShouldBeAllowed() {
DefaultBinaryHeaders headers = new DefaultBinaryHeaders();
Headers<ByteString> headers = newInstance();
headers.add(bs("name"), bs("value1"));
headers.add(bs("name"), bs("value2"));
headers.add(bs("name"), bs("value3"));
@ -97,7 +99,7 @@ public class DefaultBinaryHeadersTest {
@Test
public void testContains() {
DefaultBinaryHeaders headers = new DefaultBinaryHeaders();
Headers<ByteString> headers = newInstance();
headers.addBoolean(bs("boolean"), true);
assertTrue(headers.containsBoolean(bs("boolean"), true));
assertFalse(headers.containsBoolean(bs("boolean"), false));
@ -147,7 +149,7 @@ public class DefaultBinaryHeadersTest {
@Test
public void canMixConvertedAndNormalValues() {
DefaultBinaryHeaders headers = new DefaultBinaryHeaders();
Headers<ByteString> headers = newInstance();
headers.add(bs("name"), bs("value"));
headers.addInt(bs("name"), 100);
headers.addBoolean(bs("name"), false);
@ -161,7 +163,7 @@ public class DefaultBinaryHeadersTest {
@Test
public void testGetAndRemove() {
DefaultBinaryHeaders headers = new DefaultBinaryHeaders();
Headers<ByteString> headers = newInstance();
headers.add(bs("name1"), bs("value1"));
headers.add(bs("name2"), bs("value2"), bs("value3"));
headers.add(bs("name3"), bs("value4"), bs("value5"), bs("value6"));
@ -177,14 +179,14 @@ public class DefaultBinaryHeadersTest {
@Test
public void whenNameContainsMultipleValuesGetShouldReturnTheFirst() {
DefaultBinaryHeaders headers = new DefaultBinaryHeaders();
Headers<ByteString> headers = newInstance();
headers.add(bs("name1"), bs("value1"), bs("value2"));
assertEquals(bs("value1"), headers.get(bs("name1")));
}
@Test
public void getWithDefaultValueWorks() {
DefaultBinaryHeaders headers = new DefaultBinaryHeaders();
Headers<ByteString> headers = newInstance();
headers.add(bs("name1"), bs("value1"));
assertEquals(bs("value1"), headers.get(bs("name1"), bs("defaultvalue")));
@ -193,7 +195,7 @@ public class DefaultBinaryHeadersTest {
@Test
public void setShouldOverWritePreviousValue() {
DefaultBinaryHeaders headers = new DefaultBinaryHeaders();
Headers<ByteString> headers = newInstance();
headers.set(bs("name"), bs("value1"));
headers.set(bs("name"), bs("value2"));
assertEquals(1, headers.size());
@ -204,19 +206,19 @@ public class DefaultBinaryHeadersTest {
@Test
public void setAllShouldOverwriteSomeAndLeaveOthersUntouched() {
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
Headers<ByteString> h1 = newInstance();
h1.add(bs("name1"), bs("value1"));
h1.add(bs("name2"), bs("value2"));
h1.add(bs("name2"), bs("value3"));
h1.add(bs("name3"), bs("value4"));
DefaultBinaryHeaders h2 = new DefaultBinaryHeaders();
Headers<ByteString> h2 = newInstance();
h2.add(bs("name1"), bs("value5"));
h2.add(bs("name2"), bs("value6"));
h2.add(bs("name1"), bs("value7"));
DefaultBinaryHeaders expected = new DefaultBinaryHeaders();
Headers<ByteString> expected = newInstance();
expected.add(bs("name1"), bs("value5"));
expected.add(bs("name2"), bs("value6"));
expected.add(bs("name1"), bs("value7"));
@ -229,12 +231,12 @@ public class DefaultBinaryHeadersTest {
@Test
public void headersWithSameNamesAndValuesShouldBeEquivalent() {
DefaultBinaryHeaders headers1 = new DefaultBinaryHeaders();
Headers<ByteString> headers1 = newInstance();
headers1.add(bs("name1"), bs("value1"));
headers1.add(bs("name2"), bs("value2"));
headers1.add(bs("name2"), bs("value3"));
DefaultBinaryHeaders headers2 = new DefaultBinaryHeaders();
Headers<ByteString> headers2 = newInstance();
headers2.add(bs("name1"), bs("value1"));
headers2.add(bs("name2"), bs("value2"));
headers2.add(bs("name2"), bs("value3"));
@ -250,8 +252,8 @@ public class DefaultBinaryHeadersTest {
@Test
public void emptyHeadersShouldBeEqual() {
DefaultBinaryHeaders headers1 = new DefaultBinaryHeaders();
DefaultBinaryHeaders headers2 = new DefaultBinaryHeaders();
Headers<ByteString> headers1 = newInstance();
Headers<ByteString> headers2 = newInstance();
assertNotSame(headers1, headers2);
assertEquals(headers1, headers2);
assertEquals(headers1.hashCode(), headers2.hashCode());
@ -259,28 +261,28 @@ public class DefaultBinaryHeadersTest {
@Test
public void headersWithSameNamesButDifferentValuesShouldNotBeEquivalent() {
DefaultBinaryHeaders headers1 = new DefaultBinaryHeaders();
Headers<ByteString> headers1 = newInstance();
headers1.add(bs("name1"), bs("value1"));
DefaultBinaryHeaders headers2 = new DefaultBinaryHeaders();
Headers<ByteString> headers2 = newInstance();
headers1.add(bs("name1"), bs("value2"));
assertNotEquals(headers1, headers2);
}
@Test
public void subsetOfHeadersShouldNotBeEquivalent() {
DefaultBinaryHeaders headers1 = new DefaultBinaryHeaders();
Headers<ByteString> headers1 = newInstance();
headers1.add(bs("name1"), bs("value1"));
headers1.add(bs("name2"), bs("value2"));
DefaultBinaryHeaders headers2 = new DefaultBinaryHeaders();
Headers<ByteString> headers2 = newInstance();
headers1.add(bs("name1"), bs("value1"));
assertNotEquals(headers1, headers2);
}
@Test
public void headersWithDifferentNamesAndValuesShouldNotBeEquivalent() {
DefaultBinaryHeaders h1 = new DefaultBinaryHeaders();
Headers<ByteString> h1 = newInstance();
h1.set(bs("name1"), bs("value1"));
DefaultBinaryHeaders h2 = new DefaultBinaryHeaders();
Headers<ByteString> h2 = newInstance();
h2.set(bs("name2"), bs("value2"));
assertNotEquals(h1, h2);
assertNotEquals(h2, h1);
@ -290,23 +292,22 @@ public class DefaultBinaryHeadersTest {
@Test(expected = NoSuchElementException.class)
public void iterateEmptyHeadersShouldThrow() {
Iterator<Map.Entry<ByteString, ByteString>> iterator = new DefaultBinaryHeaders().iterator();
Iterator<Map.Entry<ByteString, ByteString>> iterator = newInstance().iterator();
assertFalse(iterator.hasNext());
iterator.next();
}
@Test
public void iteratorShouldReturnAllNameValuePairs() {
DefaultBinaryHeaders headers1 = new DefaultBinaryHeaders();
Headers<ByteString> headers1 = newInstance();
headers1.add(bs("name1"), bs("value1"), bs("value2"));
headers1.add(bs("name2"), bs("value3"));
headers1.add(bs("name3"), bs("value4"), bs("value5"), bs("value6"));
headers1.add(bs("name1"), bs("value7"), bs("value8"));
assertEquals(8, headers1.size());
DefaultBinaryHeaders headers2 = new DefaultBinaryHeaders();
Headers<ByteString> headers2 = newInstance();
for (Entry<ByteString, ByteString> entry : headers1) {
Object v = entry.getValue();
headers2.add(entry.getKey(), entry.getValue());
}
@ -315,7 +316,7 @@ public class DefaultBinaryHeadersTest {
@Test
public void iteratorSetValueShouldChangeHeaderValue() {
DefaultBinaryHeaders headers = new DefaultBinaryHeaders();
Headers<ByteString> headers = newInstance();
headers.add(bs("name1"), bs("value1"), bs("value2"), bs("value3"));
headers.add(bs("name2"), bs("value4"));
assertEquals(4, headers.size());
@ -342,65 +343,16 @@ public class DefaultBinaryHeadersTest {
@Test
public void getAllReturnsEmptyListForUnknownName() {
DefaultBinaryHeaders headers = new DefaultBinaryHeaders();
Headers<ByteString> headers = newInstance();
assertEquals(0, headers.getAll(bs("noname")).size());
}
@Test
public void canNotModifyTheListReturnedByGetAll() {
DefaultBinaryHeaders headers = new DefaultBinaryHeaders();
headers.add(bs("name1"), bs("value1"));
headers.add(bs("name2"), bs("value2"), bs("value3"));
// Test for single value names.
try {
headers.getAll(bs("name1")).add(bs("value"));
fail();
} catch (UnsupportedOperationException e) {
// for checkstyle
}
try {
headers.getAll(bs("name1")).remove(0);
fail();
} catch (UnsupportedOperationException e) {
// for checkstyle
}
// Test for multi value names.
try {
headers.getAll(bs("name2")).add(bs("value"));
fail();
} catch (UnsupportedOperationException e) {
// for checkstyle
}
try {
headers.getAll(bs("name2")).remove(0);
fail();
} catch (UnsupportedOperationException e) {
// for checkstyle
}
// Test for names that don't exist.
try {
headers.getAll(bs("name3")).add(bs("value"));
fail();
} catch (UnsupportedOperationException e) {
// for checkstyle
}
try {
headers.getAll(bs("name3")).remove(0);
fail();
} catch (UnsupportedOperationException e) {
// for checkstyle
}
}
@Test
public void setHeadersShouldClearAndOverwrite() {
DefaultBinaryHeaders headers1 = new DefaultBinaryHeaders();
Headers<ByteString> headers1 = newInstance();
headers1.add(bs("name"), bs("value"));
DefaultBinaryHeaders headers2 = new DefaultBinaryHeaders();
Headers<ByteString> headers2 = newInstance();
headers2.add(bs("name"), bs("newvalue"));
headers2.add(bs("name1"), bs("value1"));
@ -410,15 +362,15 @@ public class DefaultBinaryHeadersTest {
@Test
public void setAllHeadersShouldOnlyOverwriteHeaders() {
DefaultBinaryHeaders headers1 = new DefaultBinaryHeaders();
Headers<ByteString> headers1 = newInstance();
headers1.add(bs("name"), bs("value"));
headers1.add(bs("name1"), bs("value1"));
DefaultBinaryHeaders headers2 = new DefaultBinaryHeaders();
Headers<ByteString> headers2 = newInstance();
headers2.add(bs("name"), bs("newvalue"));
headers2.add(bs("name2"), bs("value2"));
DefaultBinaryHeaders expected = new DefaultBinaryHeaders();
Headers<ByteString> expected = newInstance();
expected.add(bs("name"), bs("newvalue"));
expected.add(bs("name1"), bs("value1"));
expected.add(bs("name2"), bs("value2"));

View File

@ -1,360 +0,0 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version
* 2.0 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package io.netty.handler.codec;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static io.netty.util.internal.StringUtil.COMMA;
import static io.netty.util.internal.StringUtil.DOUBLE_QUOTE;
import static org.junit.Assert.assertEquals;
public class DefaultTextHeadersTest {
private static final String HEADER_NAME = "testHeader";
@Test
public void addCharSequences() {
final TextHeaders headers = newDefaultTextHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asArray());
assertDefaultValues(headers, HeaderValue.THREE);
}
@Test
public void addCharSequencesCsv() {
final TextHeaders headers = newCsvTextHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asArray());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void addCharSequencesCsvWithExistingHeader() {
final TextHeaders headers = newCsvTextHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asArray());
headers.add(HEADER_NAME, HeaderValue.FIVE.subset(4));
assertCsvValues(headers, HeaderValue.FIVE);
}
@Test
public void addCharSequencesCsvWithValueContainingComma() {
final TextHeaders headers = newCsvTextHeaders();
headers.add(HEADER_NAME, HeaderValue.SIX_QUOTED.subset(4));
assertEquals(HeaderValue.SIX_QUOTED.subsetAsCsvString(4), headers.getAndConvert(HEADER_NAME));
assertEquals(HeaderValue.SIX_QUOTED.subsetAsCsvString(4), headers.getAllAndConvert(HEADER_NAME).get(0));
}
@Test
public void addCharSequencesCsvWithValueContainingCommas() {
final TextHeaders headers = newCsvTextHeaders();
headers.add(HEADER_NAME, HeaderValue.EIGHT.subset(6));
assertEquals(HeaderValue.EIGHT.subsetAsCsvString(6), headers.getAndConvert(HEADER_NAME));
assertEquals(HeaderValue.EIGHT.subsetAsCsvString(6), headers.getAllAndConvert(HEADER_NAME).get(0));
}
@Test (expected = NullPointerException.class)
public void addCharSequencesCsvNullValue() {
final TextHeaders headers = newCsvTextHeaders();
final String value = null;
headers.add(HEADER_NAME, value);
}
@Test
public void addCharSequencesCsvMultipleTimes() {
final TextHeaders headers = newCsvTextHeaders();
for (int i = 0; i < 5; ++i) {
headers.add(HEADER_NAME, "value");
}
assertEquals("value,value,value,value,value", headers.getAndConvert(HEADER_NAME));
}
@Test
public void addCharSequenceCsv() {
final TextHeaders headers = newCsvTextHeaders();
addValues(headers, HeaderValue.ONE, HeaderValue.TWO, HeaderValue.THREE);
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void addCharSequenceCsvSingleValue() {
final TextHeaders headers = newCsvTextHeaders();
addValues(headers, HeaderValue.ONE);
assertCsvValue(headers, HeaderValue.ONE);
}
@Test
public void addIterable() {
final TextHeaders headers = newDefaultTextHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asList());
assertDefaultValues(headers, HeaderValue.THREE);
}
@Test
public void addIterableCsv() {
final TextHeaders headers = newCsvTextHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asList());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void addIterableCsvWithExistingHeader() {
final TextHeaders headers = newCsvTextHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asArray());
headers.add(HEADER_NAME, HeaderValue.FIVE.subset(4));
assertCsvValues(headers, HeaderValue.FIVE);
}
@Test
public void addIterableCsvSingleValue() {
final TextHeaders headers = newCsvTextHeaders();
headers.add(HEADER_NAME, HeaderValue.ONE.asList());
assertCsvValue(headers, HeaderValue.ONE);
}
@Test
public void addIterableCsvEmtpy() {
final TextHeaders headers = newCsvTextHeaders();
headers.add(HEADER_NAME, Collections.<CharSequence>emptyList());
assertEquals("", headers.getAllAndConvert(HEADER_NAME).get(0));
}
@Test
public void addObjectCsv() {
final TextHeaders headers = newCsvTextHeaders();
addObjectValues(headers, HeaderValue.ONE, HeaderValue.TWO, HeaderValue.THREE);
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void addObjects() {
final TextHeaders headers = newDefaultTextHeaders();
headers.addObject(HEADER_NAME, HeaderValue.THREE.asArray());
assertDefaultValues(headers, HeaderValue.THREE);
}
@Test
public void addObjectsCsv() {
final TextHeaders headers = newCsvTextHeaders();
headers.addObject(HEADER_NAME, HeaderValue.THREE.asArray());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void addObjectsIterableCsv() {
final TextHeaders headers = newCsvTextHeaders();
headers.addObject(HEADER_NAME, HeaderValue.THREE.asList());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void addObjectsCsvWithExistingHeader() {
final TextHeaders headers = newCsvTextHeaders();
headers.addObject(HEADER_NAME, HeaderValue.THREE.asArray());
headers.addObject(HEADER_NAME, HeaderValue.FIVE.subset(4));
assertCsvValues(headers, HeaderValue.FIVE);
}
@Test
public void setCharSequences() {
final TextHeaders headers = newDefaultTextHeaders();
headers.set(HEADER_NAME, HeaderValue.THREE.asArray());
assertDefaultValues(headers, HeaderValue.THREE);
}
@Test
public void setCharSequenceCsv() {
final TextHeaders headers = newCsvTextHeaders();
headers.set(HEADER_NAME, HeaderValue.THREE.asArray());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void setIterable() {
final TextHeaders headers = newDefaultTextHeaders();
headers.set(HEADER_NAME, HeaderValue.THREE.asList());
assertDefaultValues(headers, HeaderValue.THREE);
}
@Test
public void setIterableCsv() {
final TextHeaders headers = newCsvTextHeaders();
headers.set(HEADER_NAME, HeaderValue.THREE.asList());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void setObjectObjects() {
final TextHeaders headers = newDefaultTextHeaders();
headers.setObject(HEADER_NAME, HeaderValue.THREE.asArray());
assertDefaultValues(headers, HeaderValue.THREE);
}
@Test
public void setObjectObjectsCsv() {
final TextHeaders headers = newCsvTextHeaders();
headers.setObject(HEADER_NAME, HeaderValue.THREE.asArray());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void setObjectIterable() {
final TextHeaders headers = newDefaultTextHeaders();
headers.setObject(HEADER_NAME, HeaderValue.THREE.asList());
assertDefaultValues(headers, HeaderValue.THREE);
}
@Test
public void setObjectIterableCsv() {
final TextHeaders headers = newCsvTextHeaders();
headers.setObject(HEADER_NAME, HeaderValue.THREE.asList());
assertCsvValues(headers, HeaderValue.THREE);
}
private static void assertDefaultValues(final TextHeaders headers, final HeaderValue headerValue) {
assertEquals(headerValue.asArray()[0], headers.get(HEADER_NAME));
assertEquals(headerValue.asList(), headers.getAll(HEADER_NAME));
}
private static void assertCsvValues(final TextHeaders headers, final HeaderValue headerValue) {
assertEquals(headerValue.asCsv(), headers.getAndConvert(HEADER_NAME));
assertEquals(headerValue.asCsv(), headers.getAllAndConvert(HEADER_NAME).get(0));
}
private static void assertCsvValue(final TextHeaders headers, final HeaderValue headerValue) {
assertEquals(headerValue.toString(), headers.getAndConvert(HEADER_NAME));
assertEquals(headerValue.toString(), headers.getAllAndConvert(HEADER_NAME).get(0));
}
private static TextHeaders newDefaultTextHeaders() {
return new DefaultTextHeaders(false);
}
private static TextHeaders newCsvTextHeaders() {
return new DefaultTextHeaders(true);
}
private static void addValues(final TextHeaders headers, HeaderValue... headerValues) {
for (HeaderValue v: headerValues) {
headers.add(HEADER_NAME, v.toString());
}
}
private static void addObjectValues(final TextHeaders headers, HeaderValue... headerValues) {
for (HeaderValue v: headerValues) {
headers.addObject(HEADER_NAME, v.toString());
}
}
private enum HeaderValue {
UNKNOWN("unknown", 0),
ONE("one", 1),
TWO("two", 2),
THREE("three", 3),
FOUR("four", 4),
FIVE("five", 5),
SIX_QUOTED("six,", 6),
SEVEN_QUOTED("seven; , GMT", 7),
EIGHT("eight", 8);
private final int nr;
private final String value;
private String[] array;
private static final String DOUBLE_QUOTE_STRING = String.valueOf(DOUBLE_QUOTE);
HeaderValue(final String value, final int nr) {
this.nr = nr;
this.value = value;
}
@Override
public String toString() {
return value;
}
public String[] asArray() {
if (array == null) {
final String[] arr = new String[nr];
for (int i = 1, y = 0; i <= nr; i++, y++) {
arr[y] = of(i).toString();
}
array = arr;
}
return array;
}
public String[] subset(final int from) {
final int size = from - 1;
final String[] arr = new String[nr - size];
System.arraycopy(asArray(), size, arr, 0, arr.length);
return arr;
}
public String subsetAsCsvString(final int from) {
final String[] subset = subset(from);
return asCsv(subset);
}
public List<CharSequence> asList() {
return Arrays.<CharSequence>asList(asArray());
}
public String asCsv(final String[] arr) {
final StringBuilder sb = new StringBuilder();
int end = arr.length - 1;
for (int i = 0; i < end; i++) {
final String value = arr[i];
quoted(sb, value).append(COMMA);
}
quoted(sb, arr[end]);
return sb.toString();
}
public String asCsv() {
return asCsv(asArray());
}
private static StringBuilder quoted(final StringBuilder sb, final String value) {
if (value.contains(String.valueOf(COMMA)) && !value.contains(DOUBLE_QUOTE_STRING)) {
return sb.append(DOUBLE_QUOTE).append(value).append(DOUBLE_QUOTE);
}
return sb.append(value);
}
public static String quoted(final String value) {
return quoted(new StringBuilder(), value).toString();
}
private static final Map<Integer, HeaderValue> MAP;
static {
final Map<Integer, HeaderValue> map = new HashMap<Integer, HeaderValue>();
for (HeaderValue v : values()) {
final int nr = v.nr;
map.put(Integer.valueOf(nr), v);
}
MAP = map;
}
public static HeaderValue of(final int nr) {
final HeaderValue v = MAP.get(Integer.valueOf(nr));
return v == null ? UNKNOWN : v;
}
}
}

View File

@ -15,8 +15,6 @@
*/
package io.netty.util;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import static io.netty.util.internal.StringUtil.UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET;
import io.netty.util.ByteProcessor.IndexOfProcessor;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.PlatformDependent;
@ -24,11 +22,12 @@ import io.netty.util.internal.PlatformDependent;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/**
* A string which has been encoded into a character encoding whose character always takes a single byte, similarly to
* ASCII. It internally keeps its content in a byte array unlike {@link String}, which uses a character array, for
@ -40,124 +39,28 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
private static final char MAX_CHAR_VALUE = 255;
public static final AsciiString EMPTY_STRING = new AsciiString("");
public static final Comparator<AsciiString> CASE_INSENSITIVE_ORDER = new Comparator<AsciiString>() {
public static final HashingStrategy<CharSequence> CASE_INSENSITIVE_HASHER =
new HashingStrategy<CharSequence>() {
@Override
public int compare(AsciiString o1, AsciiString o2) {
return CHARSEQUENCE_CASE_INSENSITIVE_ORDER.compare(o1, o2);
public int hashCode(CharSequence o) {
return AsciiString.caseInsensitiveHashCode(o);
}
@Override
public boolean equals(CharSequence a, CharSequence b) {
return AsciiString.contentEqualsIgnoreCase(a, b);
}
};
public static final Comparator<AsciiString> CASE_SENSITIVE_ORDER = new Comparator<AsciiString>() {
public static final HashingStrategy<CharSequence> CASE_SENSITIVE_HASHER =
new HashingStrategy<CharSequence>() {
@Override
public int compare(AsciiString o1, AsciiString o2) {
return CHARSEQUENCE_CASE_SENSITIVE_ORDER.compare(o1, o2);
public int hashCode(CharSequence o) {
return AsciiString.hashCode(o);
}
};
public static final Comparator<CharSequence> CHARSEQUENCE_CASE_INSENSITIVE_ORDER = new Comparator<CharSequence>() {
@Override
public int compare(CharSequence o1, CharSequence o2) {
int len1 = o1.length();
int delta = len1 - o2.length();
if (delta != 0) {
return delta;
}
if (o1.getClass().equals(AsciiString.class) && o2.getClass().equals(AsciiString.class)) {
AsciiString a1 = (AsciiString) o1;
AsciiString a2 = (AsciiString) o2;
final int a1Len = a1.length() + a1.arrayOffset();
for (int i = a1.arrayOffset(), j = a2.arrayOffset(); i < a1Len; i++, j++) {
byte c1 = a1.value[i];
byte c2 = a2.value[j];
if (c1 != c2) {
if (c1 >= 'A' && c1 <= 'Z') {
c1 += 32;
}
if (c2 >= 'A' && c2 <= 'Z') {
c2 += 32;
}
delta = c1 - c2;
if (delta != 0) {
return delta;
}
}
}
} else {
for (int i = len1 - 1; i >= 0; i --) {
char c1 = o1.charAt(i);
char c2 = o2.charAt(i);
if (c1 != c2) {
if (c1 >= 'A' && c1 <= 'Z') {
c1 += 32;
}
if (c2 >= 'A' && c2 <= 'Z') {
c2 += 32;
}
delta = c1 - c2;
if (delta != 0) {
return delta;
}
}
}
}
return 0;
}
};
public static final Comparator<CharSequence> CHARSEQUENCE_CASE_SENSITIVE_ORDER = new Comparator<CharSequence>() {
@Override
public int compare(CharSequence o1, CharSequence o2) {
if (o1 == o2) {
return 0;
}
AsciiString a1 = o1 instanceof AsciiString ? (AsciiString) o1 : null;
AsciiString a2 = o2 instanceof AsciiString ? (AsciiString) o2 : null;
int result;
int length1 = o1.length();
int length2 = o2.length();
int minLength = Math.min(length1, length2);
if (a1 != null && a2 != null) {
final int a1Len = minLength + a1.arrayOffset();
for (int i = a1.arrayOffset(), j = a2.arrayOffset(); i < a1Len; i++, j++) {
byte v1 = a1.value[i];
byte v2 = a2.value[j];
result = v1 - v2;
if (result != 0) {
return result;
}
}
} else if (a1 != null) {
for (int i = a1.arrayOffset(), j = 0; j < minLength; i++, j++) {
int c1 = a1.value[i];
int c2 = o2.charAt(j);
result = c1 - c2;
if (result != 0) {
return result;
}
}
} else if (a2 != null) {
for (int i = 0, j = a2.arrayOffset(); i < minLength; i++, j++) {
int c1 = o1.charAt(i);
int c2 = a2.value[j];
result = c1 - c2;
if (result != 0) {
return result;
}
}
} else {
for (int i = 0; i < minLength; i++) {
int c1 = o1.charAt(i);
int c2 = o2.charAt(i);
result = c1 - c2;
if (result != 0) {
return result;
}
}
}
return length1 - length2;
public boolean equals(CharSequence a, CharSequence b) {
return AsciiString.contentEquals(a, b);
}
};
@ -171,94 +74,8 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
}
};
/**
* Returns the case-insensitive hash code of the specified string. Note that this method uses the same hashing
* algorithm with {@link #hashCode()} so that you can put both {@link AsciiString}s and arbitrary
* {@link CharSequence}s into the same {@link TextHeaders}.
*/
public static int caseInsensitiveHashCode(CharSequence value) {
if (value instanceof AsciiString) {
try {
ByteProcessor processor = new ByteProcessor() {
private int hash;
@Override
public boolean process(byte value) throws Exception {
hash = hash * HASH_CODE_PRIME ^ toLowerCase(value) & HASH_CODE_PRIME;
return true;
}
@Override
public int hashCode() {
return hash;
}
};
((AsciiString) value).forEachByte(processor);
return processor.hashCode();
} catch (Exception e) {
PlatformDependent.throwException(e);
}
}
int hash = 0;
final int end = value.length();
for (int i = 0; i < end; i++) {
hash = hash * HASH_CODE_PRIME ^ toLowerCase(value.charAt(i)) & HASH_CODE_PRIME;
}
return hash;
}
/**
* Returns {@code true} if both {@link CharSequence}'s are equals when ignore the case. This only supports 8-bit
* ASCII.
*/
public static boolean equalsIgnoreCase(CharSequence a, CharSequence b) {
if (a == b) {
return true;
}
if (a instanceof AsciiString) {
AsciiString aa = (AsciiString) a;
return aa.equalsIgnoreCase(b);
}
if (b instanceof AsciiString) {
AsciiString ab = (AsciiString) b;
return ab.equalsIgnoreCase(a);
}
if (a == null || b == null) {
return false;
}
return a.toString().equalsIgnoreCase(b.toString());
}
/**
* Returns {@code true} if both {@link CharSequence}'s are equals. This only supports 8-bit ASCII.
*/
public static boolean equals(CharSequence a, CharSequence b) {
if (a == b) {
return true;
}
if (a instanceof AsciiString) {
AsciiString aa = (AsciiString) a;
return aa.equals(b);
}
if (b instanceof AsciiString) {
AsciiString ab = (AsciiString) b;
return ab.equals(a);
}
if (a == null || b == null) {
return false;
}
return a.equals(b);
}
private String string;
private int caseInsensitiveHash;
/**
* Returns an {@link AsciiString} containing the given character sequence. If the given string is already a
@ -326,47 +143,16 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
@Override
public char charAt(int index) {
return b2c(byteAt(index));
return (char) (byteAt(index) & 0xFF);
}
@Override
public void arrayChanged() {
string = null;
caseInsensitiveHash = 0;
super.arrayChanged();
}
private static byte c2b(char c) {
if (c > MAX_CHAR_VALUE) {
return '?';
}
return (byte) c;
}
private static char b2c(byte b) {
return (char) (b & 0xFF);
}
private static byte toLowerCase(byte b) {
if ('A' <= b && b <= 'Z') {
return (byte) (b + UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET);
}
return b;
}
private static char toLowerCase(char c) {
if ('A' <= c && c <= 'Z') {
return (char) (c + UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET);
}
return c;
}
private static byte toUpperCase(byte b) {
if ('a' <= b && b <= 'z') {
return (byte) (b - UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET);
}
return b;
}
@Override
public String toString(Charset charset, int start, int end) {
if (start == 0 && end == length()) {
@ -403,7 +189,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
int length2 = string.length();
int minLength = Math.min(length1, length2);
for (int i = 0, j = arrayOffset(); i < minLength; i++, j++) {
result = b2c(value[j]) - string.charAt(i);
result = (char) (value[j] & 0xFF) - string.charAt(i);
if (result != 0) {
return result;
}
@ -412,24 +198,6 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return length1 - length2;
}
/**
* Compares the specified string to this string using the ASCII values of the characters, ignoring case differences.
* Returns 0 if the strings contain the same characters in the same order. Returns a negative integer if the first
* non-equal character in this string has an ASCII value which is less than the ASCII value of the character at the
* same position in the specified string, or if this string is a prefix of the specified string. Returns a positive
* integer if the first non-equal character in this string has an ASCII value which is greater than the ASCII value
* of the character at the same position in the specified string, or if the specified string is a prefix of this
* string.
*
* @param string the string to compare.
* @return 0 if the strings are equal, a negative integer if this string is before the specified string, or a
* positive integer if this string is after the specified string.
* @throws NullPointerException if {@code string} is {@code null}.
*/
public int compareToIgnoreCase(CharSequence string) {
return CHARSEQUENCE_CASE_INSENSITIVE_ORDER.compare(this, string);
}
/**
* Concatenates this string and the specified string.
*
@ -487,24 +255,26 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
* @param string the string to compare.
* @return {@code true} if the specified string is equal to this string, {@code false} otherwise.
*/
public boolean equalsIgnoreCase(CharSequence string) {
if (string == this) {
public boolean contentEqualsIgnoreCase(CharSequence string) {
if (string == null || string.length() != length()) {
return false;
}
if (string.getClass() == AsciiString.class) {
AsciiString rhs = (AsciiString) string;
for (int i = arrayOffset(), j = rhs.arrayOffset(); i < length(); ++i, ++j) {
byte c1 = value[i];
byte c2 = value[j];
if (c1 != c2 && toLowerCase(c1) != toLowerCase(c2)) {
return false;
}
}
return true;
}
if (string == null) {
return false;
}
final int thisLen = value.length;
final int thatLen = string.length();
if (thisLen != thatLen) {
return false;
}
for (int i = 0, j = arrayOffset(); i < thisLen; i++, j++) {
char c1 = b2c(value[j]);
char c2 = string.charAt(i);
for (int i = arrayOffset(), j = 0; i < length(); ++i, ++j) {
char c1 = (char) (value[i] & 0xFF);
char c2 = string.charAt(j);
if (c1 != c2 && toLowerCase(c1) != toLowerCase(c2)) {
return false;
}
@ -539,7 +309,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
final char[] buffer = new char[length];
for (int i = 0, j = start + arrayOffset(); i < length; i++, j++) {
buffer[i] = b2c(value[j]);
buffer[i] = (char) (value[j] & 0xFF);
}
return buffer;
}
@ -564,7 +334,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
final int dstEnd = dstIdx + length;
for (int i = dstIdx, j = srcIdx + arrayOffset(); i < dstEnd; i++, j++) {
dst[i] = b2c(value[j]);
dst[i] = (char) (value[j] & 0xFF);
}
}
@ -633,7 +403,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return -1; // handles subCount > count || start >= count
}
int o1 = i, o2 = 0;
while (++o2 < subCount && b2c(value[++o1 + arrayOffset()]) == subString.charAt(o2)) {
while (++o2 < subCount && (char) (value[++o1 + arrayOffset()] & 0xFF) == subString.charAt(o2)) {
// Intentionally empty
}
if (o2 == subCount) {
@ -698,7 +468,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return -1;
}
int o1 = i, o2 = 0;
while (++o2 < subCount && b2c(value[++o1 + arrayOffset()]) == subString.charAt(o2)) {
while (++o2 < subCount && (char) (value[++o1 + arrayOffset()] & 0xFF) == subString.charAt(o2)) {
// Intentionally empty
}
if (o2 == subCount) {
@ -743,7 +513,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
final int thatEnd = start + length;
for (int i = start, j = thisStart + arrayOffset(); i < thatEnd; i++, j++) {
if (b2c(value[j]) != string.charAt(i)) {
if ((char) (value[j] & 0xFF) != string.charAt(i)) {
return false;
}
}
@ -782,7 +552,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
thisStart += arrayOffset();
final int thisEnd = thisStart + length;
while (thisStart < thisEnd) {
char c1 = b2c(value[thisStart++]);
char c1 = (char) (value[thisStart++] & 0xFF);
char c2 = string.charAt(start++);
if (c1 != c2 && toLowerCase(c1) != toLowerCase(c2)) {
return false;
@ -936,23 +706,26 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
/**
* Compares a {@code CharSequence} to this {@code String} to determine if their contents are equal.
*
* @param cs the character sequence to compare to.
* @param a the character sequence to compare to.
* @return {@code true} if equal, otherwise {@code false}
*/
public boolean contentEquals(CharSequence cs) {
if (cs == null) {
throw new NullPointerException();
public boolean contentEquals(CharSequence a) {
if (this == a) {
return true;
}
if (a instanceof AsciiString) {
return equals(a);
}
int length1 = length();
int length2 = cs.length();
if (length1 != length2) {
if (a.length() != length()) {
return false;
} else if (length1 == 0) {
return true; // since both are empty strings
}
return regionMatches(0, cs, 0, length2);
for (int i = arrayOffset(), j = 0; j < a.length(); ++i, ++j) {
if ((char) (value[i] & 0xFF) != a.charAt(j)) {
return false;
}
}
return true;
}
/**
@ -982,12 +755,11 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return toAsciiStringArray(Pattern.compile(expr).split(this, max));
}
private static AsciiString[] toAsciiStringArray(String[] jdkResult) {
AsciiString[] res = new AsciiString[jdkResult.length];
for (int i = 0; i < jdkResult.length; i++) {
res[i] = new AsciiString(jdkResult[i]);
private static byte c2b(char c) {
if (c > MAX_CHAR_VALUE) {
return '?';
}
return res;
return (byte) c;
}
/**
@ -1030,6 +802,130 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return res.toArray(new AsciiString[res.size()]);
}
/**
* Generate a hash code that will be consistent regardless of ASCII character casing.
* <p>
* NOTE: This must be compatible with {@link #caseInsensitiveHashCode(CharSequence)}.
*/
public int hashCodeCaseInsensitive() {
int h = caseInsensitiveHash;
if (h == 0) {
final int end = arrayOffset() + length();
for (int i = arrayOffset(); i < end; ++i) {
h = h * HASH_CODE_PRIME + toLowerCase((char) (value[i] & 0xFF));
}
caseInsensitiveHash = h;
}
return caseInsensitiveHash;
}
/**
* Returns the case-insensitive hash code of the specified string. Note that this method uses the same hashing
* algorithm with {@link #hashCode()} so that you can put both {@link AsciiString}s and arbitrary
* {@link CharSequence}s into the same {@link TextHeaders}.
*/
public static int caseInsensitiveHashCode(CharSequence value) {
if (value.getClass() == AsciiString.class) {
return ((AsciiString) value).hashCodeCaseInsensitive();
}
int hash = 0;
for (int i = 0; i < value.length(); ++i) {
hash = hash * HASH_CODE_PRIME + toLowerCase(value.charAt(i));
}
return hash;
}
/**
* A case-sensitive version of {@link caseInsensitiveHashCode(CharSequence)}.
* @param value
* @return
*/
public static int hashCode(CharSequence value) {
if (value.getClass() == AsciiString.class) {
return ((AsciiString) value).hashCode();
}
int hash = 0;
for (int i = 0; i < value.length(); ++i) {
hash = hash * HASH_CODE_PRIME + value.charAt(i);
}
return hash;
}
/**
* Returns {@code true} if both {@link CharSequence}'s are equals when ignore the case. This only supports 8-bit
* ASCII.
*/
public static boolean contentEqualsIgnoreCase(CharSequence a, CharSequence b) {
if (a == b) {
return true;
}
if (a == null || b == null) {
return false;
}
if (a.getClass() == AsciiString.class) {
return ((AsciiString) a).contentEqualsIgnoreCase(b);
}
if (b.getClass() == AsciiString.class) {
return ((AsciiString) b).contentEqualsIgnoreCase(a);
}
if (a.length() != b.length()) {
return false;
}
for (int i = 0, j = 0; i < a.length(); ++i, ++j) {
char c1 = a.charAt(i);
char c2 = b.charAt(j);
if (c1 != c2 && toLowerCase(c1) != toLowerCase(c2)) {
return false;
}
}
return true;
}
/**
* Returns {@code true} if the content of both {@link CharSequence}'s are equals. This only supports 8-bit ASCII.
*/
public static boolean contentEquals(CharSequence a, CharSequence b) {
if (a == b) {
return true;
}
if (a == null || b == null) {
return false;
}
if (a.getClass() == AsciiString.class) {
return ((AsciiString) a).contentEquals(b);
}
if (b.getClass() == AsciiString.class) {
return ((AsciiString) b).contentEquals(a);
}
if (a.length() != b.length()) {
return false;
}
for (int i = 0; i < a.length(); ++i) {
if (a.charAt(i) != b.charAt(i)) {
return false;
}
}
return true;
}
private static AsciiString[] toAsciiStringArray(String[] jdkResult) {
AsciiString[] res = new AsciiString[jdkResult.length];
for (int i = 0; i < jdkResult.length; i++) {
res[i] = new AsciiString(jdkResult[i]);
}
return res;
}
/**
* Determines if this {@code String} contains the sequence of characters in the {@code CharSequence} passed.
*
@ -1042,4 +938,25 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
}
return indexOf(cs) >= 0;
}
private static byte toLowerCase(byte b) {
if ('A' <= b && b <= 'Z') {
return (byte) (b + 32);
}
return b;
}
private static char toLowerCase(char c) {
if ('A' <= c && c <= 'Z') {
return (char) (c + 32);
}
return c;
}
private static byte toUpperCase(byte b) {
if ('a' <= b && b <= 'z') {
return (byte) (b - 32);
}
return b;
}
}

View File

@ -16,15 +16,14 @@ package io.netty.util;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Arrays;
import java.util.Comparator;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;
/**
* The primary use case for this class is to function as an immutable array of bytes. For performance reasons this
@ -33,31 +32,6 @@ import java.util.Comparator;
* this object is immutable.
*/
public class ByteString {
/**
* A byte wise comparator between two {@link ByteString} objects.
*/
public static final Comparator<ByteString> DEFAULT_COMPARATOR = new Comparator<ByteString>() {
@Override
public int compare(ByteString o1, ByteString o2) {
if (o1 == o2) {
return 0;
}
int result;
int length1 = o1.length();
int length2 = o2.length();
int minLength = Math.min(length1, length2);
for (int i = o1.offset, j = o2.offset; i < minLength; i++, j++) {
result = o1.value[i] - o2.value[j];
if (result != 0) {
return result;
}
}
return length1 - length2;
}
};
/**
* Allows sub classes to take advantage of {@link ByteString} operations which need to generate new
* ByteString objects.
@ -77,7 +51,7 @@ public class ByteString {
};
public static final ByteString EMPTY_STRING = new ByteString(0);
protected static final int HASH_CODE_PRIME = 31;;
protected static final int HASH_CODE_PRIME = 31;
/**
* If this value is modified outside the constructor then call {@link #arrayChanged()}.
@ -127,16 +101,15 @@ public class ByteString {
* will be shared.
*/
public ByteString(byte[] value, int start, int length, boolean copy) {
if (start < 0 || start > checkNotNull(value, "value").length - length) {
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
+ ") <= " + "value.length(" + value.length + ')');
}
if (copy) {
this.value = Arrays.copyOfRange(value, start, start + length);
offset = 0;
this.offset = 0;
this.length = length;
} else {
if (start < 0 || start > value.length - length) {
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" +
length + ") <= " + "value.length(" + value.length + ')');
}
this.value = value;
this.offset = start;
this.length = length;
@ -268,6 +241,14 @@ public class ByteString {
this.length = this.value.length;
}
/**
* Create a new {@link ByteString} assuming ASCII encoding of {@code value}.
* @param value value to translate assuming ASCII encoding.
*/
public static final ByteString fromAscii(CharSequence value) {
return new ByteString(value, CharsetUtil.US_ASCII);
}
/**
* Iterates over the readable bytes of this buffer with the specified {@code processor} in ascending order.
*
@ -275,7 +256,7 @@ public class ByteString {
* The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
*/
public final int forEachByte(ByteProcessor visitor) throws Exception {
return forEachByte(0, length(), visitor);
return forEachByte0(0, length(), visitor);
}
/**
@ -291,6 +272,10 @@ public class ByteString {
+ ") <= " + "length(" + length() + ')');
}
return forEachByte0(index, length, visitor);
}
private int forEachByte0(int index, int length, ByteProcessor visitor) throws Exception {
final int len = offset + length;
for (int i = offset + index; i < len; ++i) {
if (!visitor.process(value[i])) {
@ -307,7 +292,7 @@ public class ByteString {
* The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
*/
public final int forEachByteDesc(ByteProcessor visitor) throws Exception {
return forEachByteDesc(0, length(), visitor);
return forEachByteDesc0(0, length(), visitor);
}
/**
@ -323,6 +308,10 @@ public class ByteString {
+ ") <= " + "length(" + length() + ')');
}
return forEachByteDesc0(index, length, visitor);
}
private int forEachByteDesc0(int index, int length, ByteProcessor visitor) throws Exception {
final int end = offset + index;
for (int i = offset + index + length - 1; i >= end; --i) {
if (!visitor.process(value[i])) {
@ -423,7 +412,7 @@ public class ByteString {
if (h == 0) {
final int end = offset + length;
for (int i = offset; i < end; ++i) {
h = h * HASH_CODE_PRIME ^ value[i] & HASH_CODE_PRIME;
h = h * HASH_CODE_PRIME + value[i];
}
hash = h;

View File

@ -0,0 +1,75 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.util;
import io.netty.util.internal.ObjectUtil;
/**
* Abstraction for hash code generation and equality comparison.
*/
public interface HashingStrategy<T> {
/**
* Generate a hash code for {@code obj}.
* <p>
* This method must obey the same relationship that {@link java.lang.Object#hashCode()} has with
* {@link java.lang.Object#equals(Object)}:
* <ul>
* <li>Calling this method multiple times with the same {@code obj} should return the same result</li>
* <li>If {@link #equals(Object, Object)} with parameters {@code a} and {@code b} returns {@code true}
* then the return value for this method for parameters {@code a} and {@code b} must return the same result</li>
* <li>If {@link #equals(Object, Object)} with parameters {@code a} and {@code b} returns {@code false}
* then the return value for this method for parameters {@code a} and {@code b} does <strong>not</strong> have to
* return different results results. However this property is desirable.</li>
* <li>if {@code obj} is {@code null} then this method return {@code 0}</li>
* </ul>
*/
int hashCode(T obj);
/**
* Returns {@code true} if the arguments are equal to each other and {@code false} otherwise.
* This method has the following restrictions:
* <ul>
* <li><i>reflexive</i> - {@code equals(a, a)} should return true</li>
* <li><i>symmetric</i> - {@code equals(a, b)} returns {@code true} iff {@code equals(b, a)} returns
* {@code true}</li>
* <li><i>transitive</i> - if {@code equals(a, b)} returns {@code true} and {@code equals(a, c)} returns
* {@code true} then {@code equals(b, c)} should also return {@code true}</li>
* <li><i>consistent</i> - {@code equals(a, b)} should return the same result when called multiple times
* assuming {@code a} and {@code b} remain unchanged relative to the comparison criteria</li>
* <li>if {@code a} and {@code b} are both {@code null} then this method returns {@code true}</li>
* <li>if {@code a} is {@code null} and {@code b} is non-{@code null}, or {@code a} is non-{@code null} and
* {@code b} is {@code null} then this method returns {@code false}</li>
* </ul>
*/
boolean equals(T a, T b);
/**
* A {@link HashingStrategy} which delegates to java's {@link ObjectUtil#hashCode(Object)}
* and {@link ObjectUtil#equals(Object, Object)}.
*/
@SuppressWarnings("rawtypes")
HashingStrategy JAVA_HASHER = new HashingStrategy() {
@Override
public int hashCode(Object obj) {
return obj != null ? obj.hashCode() : 0;
}
@Override
public boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
};
}

View File

@ -37,7 +37,6 @@ public final class StringUtil {
public static final char CARRIAGE_RETURN = '\r';
public static final char TAB = '\t';
public static final byte UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET = 'a' - 'A';
private static final String[] BYTE2HEX_PAD = new String[256];
private static final String[] BYTE2HEX_NOPAD = new String[256];

View File

@ -15,14 +15,18 @@
*/
package io.netty.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertArrayEquals;
import java.nio.charset.Charset;
import org.junit.Assert;
import org.junit.Test;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.Random;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static io.netty.util.AsciiString.caseInsensitiveHashCode;
/**
* Test for the {@link AsciiString} class
*/
@ -87,6 +91,8 @@ public class AsciiStringTest {
final int end = init.length;
AsciiString sub1 = ascii.subSequence(start, end, false);
AsciiString sub2 = ascii.subSequence(start, end, true);
assertEquals(sub1.hashCode(), sub2.hashCode());
assertEquals(sub1.hashCodeCaseInsensitive(), sub2.hashCode());
assertEquals(sub1, sub2);
for (int i = start; i < end; ++i) {
assertEquals(init[i], sub1.byteAt(i - start));
@ -94,9 +100,77 @@ public class AsciiStringTest {
}
@Test
public void caseInsensativeHasher() {
String s1 = new String("TransfeR-EncodinG");
AsciiString s2 = new AsciiString("transfer-encoding");
assertEquals(AsciiString.caseInsensitiveHashCode(s1), AsciiString.caseInsensitiveHashCode(s2));
public void testCaseSensitivity() {
Random r = new Random();
int i = 0;
for (; i < 32; i++) {
doCaseSensitivity(r, i);
}
final int min = i;
final int max = 4000;
final int len = r.nextInt((max - min) + 1) + min;
doCaseSensitivity(r, len);
}
private static void doCaseSensitivity(Random r, int len) {
// Build an upper case and lower case string
final int upperA = 'A';
final int upperZ = 'Z';
final int upperToLower = (int) 'a' - upperA;
byte[] lowerCaseBytes = new byte[len];
StringBuilder upperCaseBuilder = new StringBuilder(len);
for (int i = 0; i < len; ++i) {
char upper = (char) (r.nextInt((upperZ - upperA) + 1) + upperA);
upperCaseBuilder.append(upper);
lowerCaseBytes[i] = (byte) (upper + upperToLower);
}
String upperCaseString = upperCaseBuilder.toString();
String lowerCaseString = new String(lowerCaseBytes);
AsciiString lowerCaseAscii = new AsciiString(lowerCaseBytes, false);
AsciiString upperCaseAscii = new AsciiString(upperCaseString);
ByteString lowerCaseByteString = new ByteString(lowerCaseBytes);
ByteString upperCaseByteString = new ByteString(upperCaseString, CharsetUtil.US_ASCII);
final String errorString = "len: " + len;
// Test upper case hash codes are equal
final int upperCaseExpected = upperCaseAscii.hashCode();
assertEquals(errorString, upperCaseExpected, AsciiString.hashCode(upperCaseBuilder));
assertEquals(errorString, upperCaseExpected, AsciiString.hashCode(upperCaseString));
assertEquals(errorString, upperCaseExpected, upperCaseAscii.hashCode());
assertEquals(errorString, upperCaseExpected, upperCaseByteString.hashCode());
// Test lower case hash codes are equal
final int lowerCaseExpected = lowerCaseAscii.hashCode();
assertEquals(errorString, lowerCaseExpected, AsciiString.hashCode(lowerCaseAscii));
assertEquals(errorString, lowerCaseExpected, AsciiString.hashCode(lowerCaseString));
assertEquals(errorString, lowerCaseExpected, lowerCaseAscii.hashCode());
assertEquals(errorString, lowerCaseExpected, lowerCaseByteString.hashCode());
// Test case insensitive hash codes are equal
final int expectedCaseInsensative = lowerCaseAscii.hashCodeCaseInsensitive();
assertEquals(errorString, expectedCaseInsensative, caseInsensitiveHashCode(upperCaseBuilder));
assertEquals(errorString, expectedCaseInsensative, caseInsensitiveHashCode(upperCaseString));
assertEquals(errorString, expectedCaseInsensative, caseInsensitiveHashCode(lowerCaseString));
assertEquals(errorString, expectedCaseInsensative, caseInsensitiveHashCode(lowerCaseAscii));
assertEquals(errorString, expectedCaseInsensative, caseInsensitiveHashCode(upperCaseAscii));
assertEquals(errorString, expectedCaseInsensative, lowerCaseAscii.hashCodeCaseInsensitive());
assertEquals(errorString, expectedCaseInsensative, upperCaseAscii.hashCodeCaseInsensitive());
// Test that opposite cases are not equal
if (len != 0) {
assertNotEquals(errorString, lowerCaseAscii.hashCode(), AsciiString.hashCode(upperCaseString));
assertNotEquals(errorString, upperCaseAscii.hashCode(), AsciiString.hashCode(lowerCaseString));
}
}
@Test
public void caseInsensitiveHasherCharBuffer() {
String s1 = new String("TRANSFER-ENCODING");
char[] array = new char[128];
final int offset = 100;
for (int i = 0; i < s1.length(); ++i) {
array[offset + i] = s1.charAt(i);
}
CharBuffer buffer = CharBuffer.wrap(array, offset, s1.length());
assertEquals(caseInsensitiveHashCode(s1), caseInsensitiveHashCode(buffer));
}
}

View File

@ -17,7 +17,6 @@ package io.netty.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;
@ -36,8 +35,6 @@ public class ByteStringTest {
private int length = 100;
private ByteString aByteString;
private ByteString bByteString;
private ByteString greaterThanAByteString;
private ByteString lessThanAByteString;
private Random r = new Random();
@Before
@ -52,66 +49,6 @@ public class ByteStringTest {
System.arraycopy(a, aOffset, b, bOffset, length);
aByteString = new ByteString(a, aOffset, length, false);
bByteString = new ByteString(b, bOffset, length, false);
int i;
final int end = aOffset + length;
// Find an element that can be decremented
for (i = aOffset + 1; i < end; ++i) {
if (a[i] > Byte.MIN_VALUE) {
--a[i];
break;
}
}
if (i == end) {
throw new IllegalStateException("Couldn't find an index to decrement, all random numbers Byte.MIN_VALUE");
}
lessThanAByteString = new ByteString(a, aOffset, length, true);
++a[i]; // Restore the a array to the original value
// Find an element that can be incremented
for (i = aOffset + 1; i < end; ++i) {
if (a[i] < Byte.MAX_VALUE) {
++a[i];
break;
}
}
if (i == end) {
throw new IllegalStateException("Couldn't find an index to increment, all random numbers Byte.MAX_VALUE");
}
greaterThanAByteString = new ByteString(a, aOffset, length, true);
--a[i]; // Restore the a array to the original value
}
@Test
public void testEqualsComparareSelf() {
assertTrue(ByteString.DEFAULT_COMPARATOR.compare(aByteString, aByteString) == 0);
assertEquals(bByteString, bByteString);
}
@Test
public void testEqualsComparatorAgainstAnother1() {
assertTrue(ByteString.DEFAULT_COMPARATOR.compare(bByteString, aByteString) == 0);
assertEquals(bByteString, aByteString);
assertEquals(bByteString.hashCode(), aByteString.hashCode());
}
@Test
public void testEqualsComparatorAgainstAnother2() {
assertTrue(ByteString.DEFAULT_COMPARATOR.compare(aByteString, bByteString) == 0);
assertEquals(aByteString, bByteString);
assertEquals(aByteString.hashCode(), bByteString.hashCode());
}
@Test
public void testLessThan() {
assertTrue(ByteString.DEFAULT_COMPARATOR.compare(lessThanAByteString, aByteString) < 0);
assertTrue(ByteString.DEFAULT_COMPARATOR.compare(aByteString, lessThanAByteString) > 0);
}
@Test
public void testGreaterThan() {
assertTrue(ByteString.DEFAULT_COMPARATOR.compare(greaterThanAByteString, aByteString) > 0);
assertTrue(ByteString.DEFAULT_COMPARATOR.compare(aByteString, greaterThanAByteString) < 0);
}
@Test

View File

@ -264,7 +264,7 @@ public final class HttpUploadClient {
*/
private static void formpostmultipart(
Bootstrap bootstrap, String host, int port, URI uriFile, HttpDataFactory factory,
List<Entry<String, String>> headers, List<InterfaceHttpData> bodylist) throws Exception {
Iterable<Entry<String, String>> headers, List<InterfaceHttpData> bodylist) throws Exception {
// XXX /formpostmultipart
// Start the connection attempt.
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));

View File

@ -21,7 +21,6 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

View File

@ -62,7 +62,7 @@ public class StompClientHandler extends SimpleChannelInboundHandler<StompFrame>
ctx.writeAndFlush(subscribeFrame);
break;
case RECEIPT:
String receiptHeader = frame.headers().getAndConvert(StompHeaders.RECEIPT_ID);
String receiptHeader = frame.headers().getAsString(StompHeaders.RECEIPT_ID);
if (state == ClientState.AUTHENTICATED && receiptHeader.equals(subscrReceiptId)) {
StompFrame msgFrame = new DefaultStompFrame(StompCommand.SEND);
msgFrame.headers().set(StompHeaders.DESTINATION, StompClient.TOPIC);

View File

@ -80,7 +80,7 @@ public final class ExampleHeaders {
header.put("accept-encoding", "gzip, deflate, sdch");
header.put("accept-language", "en-US,en;q=0.8");
header.put("cache-control", "max-age=0");
header.put("cookie:", "noneofyourbusiness");
header.put("cookie", "noneofyourbusiness");
header.put("user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)");
EXAMPLES.put(HeaderExample.ELEVEN, header);

View File

@ -83,6 +83,14 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
}
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public void httpRemove(Blackhole bh) {
for (AsciiString name : httpNames) {
bh.consume(httpHeaders.remove(name));
}
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public void httpGet(Blackhole bh) {
@ -110,6 +118,14 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
}
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public void http2Remove(Blackhole bh) {
for (ByteString name : http2Names) {
bh.consume(http2Headers.remove(name));
}
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public void http2Get(Blackhole bh) {