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-07-22 15:14:03 -07:00
parent 36d682b6cf
commit 1a43923aa8
102 changed files with 2860 additions and 4112 deletions

View File

@ -0,0 +1,173 @@
/*
* 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.internal.StringUtil;
import java.util.Collection;
import java.util.Iterator;
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 {
/**
* 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;
public CombinedHttpHeaders(boolean validate) {
super(validate);
}
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 CombinedHttpHeaders add(CharSequence name, CharSequence value) {
return addEscapedValue(name, StringUtil.escapeCsv(value));
}
@Override
public CombinedHttpHeaders add(CharSequence name, CharSequence... values) {
return addEscapedValue(name, commaSeparate(charSequenceEscaper(), values));
}
@Override
public CombinedHttpHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
return addEscapedValue(name, commaSeparate(charSequenceEscaper(), values));
}
@Override
public CombinedHttpHeaders addObject(CharSequence name, Iterable<?> values) {
return addEscapedValue(name, commaSeparate(objectEscaper(), values));
}
@Override
public CombinedHttpHeaders addObject(CharSequence name, Object... values) {
return addEscapedValue(name, commaSeparate(objectEscaper(), values));
}
@Override
public CombinedHttpHeaders set(CharSequence name, CharSequence... values) {
super.set(name, commaSeparate(charSequenceEscaper(), values));
return this;
}
@Override
public CombinedHttpHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
super.set(name, commaSeparate(charSequenceEscaper(), values));
return this;
}
@Override
public CombinedHttpHeaders setObject(CharSequence name, Object... values) {
super.set(name, commaSeparate(objectEscaper(), values));
return this;
}
@Override
public CombinedHttpHeaders setObject(CharSequence name, Iterable<?> values) {
super.set(name, commaSeparate(objectEscaper(), values));
return this;
}
private CombinedHttpHeaders 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; package io.netty.handler.codec.http;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
/** /**
* Default implementation of a {@link FullHttpResponse}. * 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) { 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) { 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, public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status,
ByteBuf content, boolean singleFieldHeaders) { ByteBuf content, boolean validateHeaders) {
this(version, status, content, true, singleFieldHeaders); this(version, status, content, validateHeaders, false);
} }
public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status, public DefaultFullHttpResponse(HttpVersion version, HttpResponseStatus status,
ByteBuf content, boolean validateHeaders, boolean singleFieldHeaders) { ByteBuf content, boolean validateHeaders, boolean singleFieldHeaders) {
super(version, status, validateHeaders, singleFieldHeaders); super(version, status, validateHeaders, singleFieldHeaders);
if (content == null) { this.content = checkNotNull(content, "content");
throw new NullPointerException("content"); this.trailingHeaders = singleFieldHeaders ? new CombinedHttpHeaders(validateHeaders)
} : new DefaultHttpHeaders(validateHeaders);
this.content = content;
trailingHeaders = new DefaultHttpHeaders(validateHeaders, singleFieldHeaders);
this.validateHeaders = validateHeaders; this.validateHeaders = validateHeaders;
} }

View File

@ -15,60 +15,65 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.handler.codec.CharSequenceValueConverter;
import io.netty.handler.codec.DefaultHeaders; import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.DefaultTextHeaders; import io.netty.handler.codec.Headers;
import io.netty.handler.codec.TextHeaders; import io.netty.handler.codec.HeadersUtils;
import io.netty.handler.codec.ValueConverter;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.ByteProcessor;
import io.netty.util.internal.PlatformDependent;
import java.util.Calendar; import java.util.Calendar;
import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.TreeMap; import java.util.Map.Entry;
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; import static io.netty.util.internal.ObjectUtil.checkNotNull;
public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeaders { public class DefaultHttpHeaders extends DefaultHeaders<CharSequence> implements HttpHeaders {
private static final int HIGHEST_INVALID_NAME_CHAR_MASK = ~63;
private static final int HIGHEST_INVALID_VALUE_CHAR_MASK = ~15; private static final int HIGHEST_INVALID_VALUE_CHAR_MASK = ~15;
private static final ByteProcessor HEADER_NAME_VALIDATOR = new ByteProcessor() {
/** @Override
* A look-up table used for checking if a character in a header name is prohibited. public boolean process(byte value) throws Exception {
*/ validateChar((char) (value & 0xFF));
private static final byte[] LOOKUP_TABLE = new byte[~HIGHEST_INVALID_NAME_CHAR_MASK + 1]; return true;
}
static { };
LOOKUP_TABLE['\t'] = -1; static final NameValidator<CharSequence> HttpNameValidator = new NameValidator<CharSequence>() {
LOOKUP_TABLE['\n'] = -1; @Override
LOOKUP_TABLE[0x0b] = -1; public void validateName(CharSequence name) {
LOOKUP_TABLE['\f'] = -1; if (name instanceof AsciiString) {
LOOKUP_TABLE[' '] = -1; try {
LOOKUP_TABLE[','] = -1; ((AsciiString) name).forEachByte(HEADER_NAME_VALIDATOR);
LOOKUP_TABLE[':'] = -1; } catch (Exception e) {
LOOKUP_TABLE[';'] = -1; PlatformDependent.throwException(e);
LOOKUP_TABLE['='] = -1; }
} } else {
checkNotNull(name, "name");
// Go through each character in the name
for (int index = 0; index < name.length(); ++index) {
validateChar(name.charAt(index));
}
}
}
};
public DefaultHttpHeaders() { public DefaultHttpHeaders() {
this(true); this(true);
} }
@SuppressWarnings("unchecked")
public DefaultHttpHeaders(boolean validate) { public DefaultHttpHeaders(boolean validate) {
this(validate, false); super(CASE_INSENSITIVE_HASHER, valueConverter(validate),
validate ? HttpNameValidator : NameValidator.NOT_NULL);
} }
protected DefaultHttpHeaders(boolean validate, boolean singleHeaderFields) { protected DefaultHttpHeaders(boolean validateValue, NameValidator<CharSequence> nameValidator) {
this(true, validate ? HeaderNameValidator.INSTANCE : NO_NAME_VALIDATOR, singleHeaderFields); super(CASE_INSENSITIVE_HASHER, valueConverter(validateValue), nameValidator);
}
protected DefaultHttpHeaders(boolean validate,
DefaultHeaders.NameValidator<CharSequence> nameValidator,
boolean singleHeaderFields) {
super(new TreeMap<CharSequence, Object>(AsciiString.CHARSEQUENCE_CASE_INSENSITIVE_ORDER),
nameValidator,
validate ? HeaderValueConverterAndValidator.INSTANCE : HeaderValueConverter.INSTANCE,
singleHeaderFields);
} }
@Override @Override
@ -162,7 +167,7 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
} }
@Override @Override
public HttpHeaders add(TextHeaders headers) { public HttpHeaders add(Headers<? extends CharSequence> headers) {
super.add(headers); super.add(headers);
return this; return this;
} }
@ -258,13 +263,13 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
} }
@Override @Override
public HttpHeaders set(TextHeaders headers) { public HttpHeaders set(Headers<? extends CharSequence> headers) {
super.set(headers); super.set(headers);
return this; return this;
} }
@Override @Override
public HttpHeaders setAll(TextHeaders headers) { public HttpHeaders setAll(Headers<? extends CharSequence> headers) {
super.setAll(headers); super.setAll(headers);
return this; return this;
} }
@ -276,73 +281,96 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader
} }
@Override @Override
public int hashCode() { public boolean equals(Object o) {
return size(); if (!(o instanceof HttpHeaders)) {
return false;
}
return equals((HttpHeaders) o, CASE_SENSITIVE_HASHER);
} }
@Override @Override
public boolean equals(Object other) { public int hashCode() {
if (!(other instanceof HttpHeaders)) { return hashCode(CASE_SENSITIVE_HASHER);
return false;
}
HttpHeaders headers = (HttpHeaders) other;
return DefaultHeaders.comparatorEquals(this, headers, AsciiString.CHARSEQUENCE_CASE_SENSITIVE_ORDER);
} }
static final class HeaderNameValidator implements DefaultHeaders.NameValidator<CharSequence> { @Override
public String getAsString(CharSequence name) {
return HeadersUtils.getAsString(this, name);
}
public static final HeaderNameValidator INSTANCE = new HeaderNameValidator(); @Override
public List<String> getAllAsString(CharSequence name) {
return HeadersUtils.getAllAsString(this, name);
}
private HeaderNameValidator() { @Override
} public Iterator<Entry<String, String>> iteratorAsString() {
return HeadersUtils.iteratorAsString(this);
}
@Override @Override
public void validate(CharSequence name) { public boolean contains(CharSequence name, CharSequence value) {
// Go through each character in the name return contains(name, value, false);
for (int index = 0; index < name.length(); index++) { }
char character = name.charAt(index);
// Check to see if the character is not an ASCII character @Override
if (character > 127) { public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) {
throw new IllegalArgumentException("a header name cannot contain non-ASCII characters: " + name); return contains(name, value,
} ignoreCase ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER);
}
// Check for prohibited characters. private static void validateChar(char character) {
if ((character & HIGHEST_INVALID_NAME_CHAR_MASK) == 0 && LOOKUP_TABLE[character] != 0) { switch (character) {
throw new IllegalArgumentException( case '\t':
"a header name cannot contain the following prohibited characters: =,;: \\t\\r\\n\\v\\f: " + case '\n':
name); 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 { private static ValueConverter<CharSequence> valueConverter(boolean validate) {
return validate ? HeaderValueConverterAndValidator.INSTANCE : HeaderValueConverter.INSTANCE;
}
public static final HeaderValueConverter INSTANCE = new HeaderValueConverter(); private static class HeaderValueConverter extends CharSequenceValueConverter {
static final HeaderValueConverter INSTANCE = new HeaderValueConverter();
@Override @Override
public CharSequence convertObject(Object value) { public CharSequence convertObject(Object value) {
checkNotNull(value, "value"); checkNotNull(value, "value");
CharSequence seq;
if (value instanceof CharSequence) { if (value instanceof CharSequence) {
seq = (CharSequence) value; return (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 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 { private static final class HeaderValueConverterAndValidator extends HeaderValueConverter {
static final HeaderValueConverterAndValidator INSTANCE = new HeaderValueConverterAndValidator();
public static final HeaderValueConverterAndValidator INSTANCE = new HeaderValueConverterAndValidator();
@Override @Override
public CharSequence convertObject(Object value) { public CharSequence convertObject(Object value) {

View File

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

View File

@ -15,6 +15,8 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/** /**
* The default {@link HttpRequest} implementation. * 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) { public DefaultHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, boolean validateHeaders) {
super(httpVersion, validateHeaders, false); super(httpVersion, validateHeaders, false);
if (method == null) { this.method = checkNotNull(method, "method");
throw new NullPointerException("method"); this.uri = checkNotNull(uri, "uri");
}
if (uri == null) {
throw new NullPointerException("uri");
}
this.method = method;
this.uri = uri;
} }
@Override @Override

View File

@ -49,12 +49,15 @@ public class DefaultHttpResponse extends DefaultHttpMessage implements HttpRespo
* @param version the HTTP version of this response * @param version the HTTP version of this response
* @param status the getStatus 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 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 * @param singleFieldHeaders {@code true} to check and enforce that headers with the same name are appended
* field or as multiple header fields. * 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, public DefaultHttpResponse(HttpVersion version, HttpResponseStatus status, boolean validateHeaders,
boolean singleHeaderFields) { boolean singleFieldHeaders) {
super(version, validateHeaders, singleHeaderFields); super(version, validateHeaders, singleFieldHeaders);
if (status == null) { if (status == null) {
throw new NullPointerException("status"); throw new NullPointerException("status");
} }

View File

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

View File

@ -16,12 +16,35 @@
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.handler.codec.EmptyTextHeaders; import java.util.Collections;
import io.netty.handler.codec.TextHeaders; import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Map.Entry;
public class EmptyHttpHeaders extends EmptyTextHeaders implements HttpHeaders { import io.netty.handler.codec.EmptyHeaders;
import io.netty.handler.codec.Headers;
public class EmptyHttpHeaders extends EmptyHeaders<CharSequence> implements HttpHeaders {
public static final EmptyHttpHeaders INSTANCE = new EmptyHttpHeaders(); public static final EmptyHttpHeaders INSTANCE = new EmptyHttpHeaders();
private static final Iterator<Entry<String, String>> EMPTY_STRING_ITERATOR =
new Iterator<Entry<String, String>>() {
@Override
public boolean hasNext() {
return false;
}
@Override
public Entry<String, String> next() {
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException("remove");
}
};
protected EmptyHttpHeaders() { protected EmptyHttpHeaders() {
} }
@ -117,7 +140,7 @@ public class EmptyHttpHeaders extends EmptyTextHeaders implements HttpHeaders {
} }
@Override @Override
public HttpHeaders add(TextHeaders headers) { public HttpHeaders add(Headers<? extends CharSequence> headers) {
super.add(headers); super.add(headers);
return this; return this;
} }
@ -213,13 +236,13 @@ public class EmptyHttpHeaders extends EmptyTextHeaders implements HttpHeaders {
} }
@Override @Override
public HttpHeaders set(TextHeaders headers) { public HttpHeaders set(Headers<? extends CharSequence> headers) {
super.set(headers); super.set(headers);
return this; return this;
} }
@Override @Override
public HttpHeaders setAll(TextHeaders headers) { public HttpHeaders setAll(Headers<? extends CharSequence> headers) {
super.setAll(headers); super.setAll(headers);
return this; return this;
} }
@ -229,4 +252,24 @@ public class EmptyHttpHeaders extends EmptyTextHeaders implements HttpHeaders {
super.clear(); super.clear();
return this; return this;
} }
@Override
public String getAsString(CharSequence name) {
return null;
}
@Override
public List<String> getAllAsString(CharSequence name) {
return Collections.emptyList();
}
@Override
public Iterator<Entry<String, String>> iteratorAsString() {
return EMPTY_STRING_ITERATOR;
}
@Override
public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) {
return false;
}
} }

View File

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

View File

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

View File

@ -77,7 +77,7 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<HttpObj
final HttpHeaders headers = message.headers(); final HttpHeaders headers = message.headers();
// Determine the content encoding. // Determine the content encoding.
String contentEncoding = headers.getAndConvert(HttpHeaderNames.CONTENT_ENCODING); String contentEncoding = headers.getAsString(HttpHeaderNames.CONTENT_ENCODING);
if (contentEncoding != null) { if (contentEncoding != null) {
contentEncoding = contentEncoding.trim(); contentEncoding = contentEncoding.trim();
} else { } else {

View File

@ -50,12 +50,12 @@ public class HttpContentDecompressor extends HttpContentDecoder {
@Override @Override
protected EmbeddedChannel newContentDecoder(String contentEncoding) throws Exception { protected EmbeddedChannel newContentDecoder(String contentEncoding) throws Exception {
if (GZIP.equalsIgnoreCase(contentEncoding) || if (GZIP.contentEqualsIgnoreCase(contentEncoding) ||
X_GZIP.equalsIgnoreCase(contentEncoding)) { X_GZIP.contentEqualsIgnoreCase(contentEncoding)) {
return new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP)); return new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP));
} }
if (DEFLATE.equalsIgnoreCase(contentEncoding) || if (DEFLATE.contentEqualsIgnoreCase(contentEncoding) ||
X_DEFLATE.equalsIgnoreCase(contentEncoding)) { X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) {
final ZlibWrapper wrapper = strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE; final ZlibWrapper wrapper = strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE;
// To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly. // To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly.
return new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(wrapper)); return new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(wrapper));

View File

@ -18,7 +18,6 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -32,14 +31,14 @@ public final class HttpHeaderUtil {
*/ */
public static boolean isKeepAlive(HttpMessage message) { public static boolean isKeepAlive(HttpMessage message) {
CharSequence connection = message.headers().get(HttpHeaderNames.CONNECTION); CharSequence connection = message.headers().get(HttpHeaderNames.CONNECTION);
if (connection != null && HttpHeaderValues.CLOSE.equalsIgnoreCase(connection)) { if (connection != null && HttpHeaderValues.CLOSE.contentEqualsIgnoreCase(connection)) {
return false; return false;
} }
if (message.protocolVersion().isKeepAliveDefault()) { if (message.protocolVersion().isKeepAliveDefault()) {
return !HttpHeaderValues.CLOSE.equalsIgnoreCase(connection); return !HttpHeaderValues.CLOSE.contentEqualsIgnoreCase(connection);
} else { } else {
return HttpHeaderValues.KEEP_ALIVE.equalsIgnoreCase(connection); return HttpHeaderValues.KEEP_ALIVE.contentEqualsIgnoreCase(connection);
} }
} }
@ -193,7 +192,7 @@ public final class HttpHeaderUtil {
if (value == null) { if (value == null) {
return false; return false;
} }
if (HttpHeaderValues.CONTINUE.equalsIgnoreCase(value)) { if (HttpHeaderValues.CONTINUE.contentEqualsIgnoreCase(value)) {
return true; return true;
} }
@ -231,16 +230,14 @@ public final class HttpHeaderUtil {
m.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED); m.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED);
m.headers().remove(HttpHeaderNames.CONTENT_LENGTH); m.headers().remove(HttpHeaderNames.CONTENT_LENGTH);
} else { } else {
// Make a copy to be able to modify values while iterating List<CharSequence> values = m.headers().getAll(HttpHeaderNames.TRANSFER_ENCODING);
List<CharSequence> values =
new ArrayList<CharSequence>(m.headers().getAll(HttpHeaderNames.TRANSFER_ENCODING));
if (values.isEmpty()) { if (values.isEmpty()) {
return; return;
} }
Iterator<CharSequence> valuesIt = values.iterator(); Iterator<CharSequence> valuesIt = values.iterator();
while (valuesIt.hasNext()) { while (valuesIt.hasNext()) {
CharSequence value = valuesIt.next(); CharSequence value = valuesIt.next();
if (HttpHeaderValues.CHUNKED.equalsIgnoreCase(value)) { if (HttpHeaderValues.CHUNKED.contentEqualsIgnoreCase(value)) {
valuesIt.remove(); valuesIt.remove();
} }
} }

View File

@ -15,13 +15,17 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.handler.codec.TextHeaders; import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import io.netty.handler.codec.Headers;
/** /**
* Provides the constants for the standard HTTP header names and values and * Provides the constants for the standard HTTP header names and values and
* commonly used utility methods that accesses an {@link HttpMessage}. * commonly used utility methods that accesses an {@link HttpMessage}.
*/ */
public interface HttpHeaders extends TextHeaders { public interface HttpHeaders extends Headers<CharSequence> {
@Override @Override
HttpHeaders add(CharSequence name, CharSequence value); HttpHeaders add(CharSequence name, CharSequence value);
@ -68,7 +72,7 @@ public interface HttpHeaders extends TextHeaders {
HttpHeaders addTimeMillis(CharSequence name, long value); HttpHeaders addTimeMillis(CharSequence name, long value);
@Override @Override
HttpHeaders add(TextHeaders headers); HttpHeaders add(Headers<? extends CharSequence> headers);
@Override @Override
HttpHeaders set(CharSequence name, CharSequence value); HttpHeaders set(CharSequence name, CharSequence value);
@ -116,11 +120,41 @@ public interface HttpHeaders extends TextHeaders {
HttpHeaders setTimeMillis(CharSequence name, long value); HttpHeaders setTimeMillis(CharSequence name, long value);
@Override @Override
HttpHeaders set(TextHeaders headers); HttpHeaders set(Headers<? extends CharSequence> headers);
@Override @Override
HttpHeaders setAll(TextHeaders headers); HttpHeaders setAll(Headers<? extends CharSequence> headers);
@Override @Override
HttpHeaders clear(); HttpHeaders 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,7 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import java.util.HashMap; import java.util.HashMap;
@ -85,8 +86,7 @@ public class HttpMethod implements Comparable<HttpMethod> {
*/ */
public static final HttpMethod CONNECT = new HttpMethod("CONNECT"); public static final HttpMethod CONNECT = new HttpMethod("CONNECT");
private static final Map<String, HttpMethod> methodMap = private static final Map<String, HttpMethod> methodMap = new HashMap<String, HttpMethod>();
new HashMap<String, HttpMethod>();
static { static {
methodMap.put(OPTIONS.toString(), OPTIONS); methodMap.put(OPTIONS.toString(), OPTIONS);
@ -106,21 +106,8 @@ public class HttpMethod implements Comparable<HttpMethod> {
* will be returned. Otherwise, a new instance will be returned. * will be returned. Otherwise, a new instance will be returned.
*/ */
public static HttpMethod valueOf(String name) { 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); HttpMethod result = methodMap.get(name);
if (result != null) { return result != null ? result : new HttpMethod(name);
return result;
} else {
return new HttpMethod(name);
}
} }
private final AsciiString name; private final AsciiString name;
@ -134,11 +121,7 @@ public class HttpMethod implements Comparable<HttpMethod> {
* <a href="http://en.wikipedia.org/wiki/Internet_Content_Adaptation_Protocol">ICAP</a> * <a href="http://en.wikipedia.org/wiki/Internet_Content_Adaptation_Protocol">ICAP</a>
*/ */
public HttpMethod(String name) { public HttpMethod(String name) {
if (name == null) { name = checkNotNull(name, "name").trim();
throw new NullPointerException("name");
}
name = name.trim();
if (name.isEmpty()) { if (name.isEmpty()) {
throw new IllegalArgumentException("empty name"); throw new IllegalArgumentException("empty name");
} }

View File

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

View File

@ -183,7 +183,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
this.factory = factory; this.factory = factory;
// Fill default values // Fill default values
setMultipart(this.request.headers().getAndConvert(HttpHeaderNames.CONTENT_TYPE)); setMultipart(this.request.headers().getAsString(HttpHeaderNames.CONTENT_TYPE));
if (request instanceof HttpContent) { if (request instanceof HttpContent) {
// Offer automatically if the given request is als type of HttpContent // Offer automatically if the given request is als type of HttpContent
// See #1089 // See #1089
@ -688,13 +688,13 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
return null; return null;
} }
String[] contents = splitMultipartHeader(newline); String[] contents = splitMultipartHeader(newline);
if (HttpHeaderNames.CONTENT_DISPOSITION.equalsIgnoreCase(contents[0])) { if (HttpHeaderNames.CONTENT_DISPOSITION.contentEqualsIgnoreCase(contents[0])) {
boolean checkSecondArg; boolean checkSecondArg;
if (currentStatus == MultiPartStatus.DISPOSITION) { if (currentStatus == MultiPartStatus.DISPOSITION) {
checkSecondArg = HttpHeaderValues.FORM_DATA.equalsIgnoreCase(contents[1]); checkSecondArg = HttpHeaderValues.FORM_DATA.contentEqualsIgnoreCase(contents[1]);
} else { } else {
checkSecondArg = HttpHeaderValues.ATTACHMENT.equalsIgnoreCase(contents[1]) checkSecondArg = HttpHeaderValues.ATTACHMENT.contentEqualsIgnoreCase(contents[1])
|| HttpHeaderValues.FILE.equalsIgnoreCase(contents[1]); || HttpHeaderValues.FILE.contentEqualsIgnoreCase(contents[1]);
} }
if (checkSecondArg) { if (checkSecondArg) {
// read next values and store them in the map as Attribute // read next values and store them in the map as Attribute
@ -722,7 +722,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
currentFieldAttributes.put(attribute.getName(), attribute); 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; Attribute attribute;
try { try {
attribute = factory.createAttribute(request, HttpHeaderNames.CONTENT_TRANSFER_ENCODING.toString(), attribute = factory.createAttribute(request, HttpHeaderNames.CONTENT_TRANSFER_ENCODING.toString(),
@ -733,7 +733,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
throw new ErrorDataDecoderException(e); throw new ErrorDataDecoderException(e);
} }
currentFieldAttributes.put(HttpHeaderNames.CONTENT_TRANSFER_ENCODING.toString(), attribute); currentFieldAttributes.put(HttpHeaderNames.CONTENT_TRANSFER_ENCODING.toString(), attribute);
} else if (HttpHeaderNames.CONTENT_LENGTH.equalsIgnoreCase(contents[0])) { } else if (HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(contents[0])) {
Attribute attribute; Attribute attribute;
try { try {
attribute = factory.createAttribute(request, HttpHeaderNames.CONTENT_LENGTH.toString(), attribute = factory.createAttribute(request, HttpHeaderNames.CONTENT_LENGTH.toString(),
@ -744,9 +744,9 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
throw new ErrorDataDecoderException(e); throw new ErrorDataDecoderException(e);
} }
currentFieldAttributes.put(HttpHeaderNames.CONTENT_LENGTH.toString(), attribute); currentFieldAttributes.put(HttpHeaderNames.CONTENT_LENGTH.toString(), attribute);
} else if (HttpHeaderNames.CONTENT_TYPE.equalsIgnoreCase(contents[0])) { } else if (HttpHeaderNames.CONTENT_TYPE.contentEqualsIgnoreCase(contents[0])) {
// Take care of possible "multipart/mixed" // Take care of possible "multipart/mixed"
if (HttpHeaderValues.MULTIPART_MIXED.equalsIgnoreCase(contents[1])) { if (HttpHeaderValues.MULTIPART_MIXED.contentEqualsIgnoreCase(contents[1])) {
if (currentStatus == MultiPartStatus.DISPOSITION) { if (currentStatus == MultiPartStatus.DISPOSITION) {
String values = StringUtil.substringAfter(contents[2], '='); String values = StringUtil.substringAfter(contents[2], '=');
multipartMixedBoundary = "--" + values; multipartMixedBoundary = "--" + values;

View File

@ -141,7 +141,7 @@ public class HttpPostRequestDecoder implements InterfaceHttpPostRequestDecoder {
*/ */
public static boolean isMultipart(HttpRequest request) { public static boolean isMultipart(HttpRequest request) {
if (request.headers().contains(HttpHeaderNames.CONTENT_TYPE)) { if (request.headers().contains(HttpHeaderNames.CONTENT_TYPE)) {
return getMultipartDataBoundary(request.headers().getAndConvert(HttpHeaderNames.CONTENT_TYPE)) != null; return getMultipartDataBoundary(request.headers().getAsString(HttpHeaderNames.CONTENT_TYPE)) != null;
} else { } else {
return false; return false;
} }

View File

@ -717,7 +717,7 @@ public class HttpPostRequestEncoder implements ChunkedInput<HttpContent> {
} }
HttpHeaders headers = request.headers(); HttpHeaders headers = request.headers();
List<String> contentTypes = headers.getAllAndConvert(HttpHeaderNames.CONTENT_TYPE); List<String> contentTypes = headers.getAllAsString(HttpHeaderNames.CONTENT_TYPE);
List<CharSequence> transferEncoding = headers.getAll(HttpHeaderNames.TRANSFER_ENCODING); List<CharSequence> transferEncoding = headers.getAll(HttpHeaderNames.TRANSFER_ENCODING);
if (contentTypes != null) { if (contentTypes != null) {
headers.remove(HttpHeaderNames.CONTENT_TYPE); headers.remove(HttpHeaderNames.CONTENT_TYPE);
@ -754,7 +754,7 @@ public class HttpPostRequestEncoder implements ChunkedInput<HttpContent> {
if (transferEncoding != null) { if (transferEncoding != null) {
headers.remove(HttpHeaderNames.TRANSFER_ENCODING); headers.remove(HttpHeaderNames.TRANSFER_ENCODING);
for (CharSequence v : transferEncoding) { for (CharSequence v : transferEncoding) {
if (HttpHeaderValues.CHUNKED.equalsIgnoreCase(v)) { if (HttpHeaderValues.CHUNKED.contentEqualsIgnoreCase(v)) {
// ignore // ignore
} else { } else {
headers.add(HttpHeaderNames.TRANSFER_ENCODING, v); headers.add(HttpHeaderNames.TRANSFER_ENCODING, v);

View File

@ -215,7 +215,7 @@ public abstract class WebSocketClientHandshaker {
// Verify the subprotocol that we received from the server. // Verify the subprotocol that we received from the server.
// This must be one of our expected subprotocols - or null/empty if we didn't want to speak a subprotocol // This must be one of our expected subprotocols - or null/empty if we didn't want to speak a subprotocol
String receivedProtocol = response.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL); String receivedProtocol = response.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL);
receivedProtocol = receivedProtocol != null ? receivedProtocol.trim() : null; receivedProtocol = receivedProtocol != null ? receivedProtocol.trim() : null;
String expectedProtocol = expectedSubprotocol != null ? expectedSubprotocol : ""; String expectedProtocol = expectedSubprotocol != null ? expectedSubprotocol : "";
boolean protocolValid = false; boolean protocolValid = false;

View File

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

View File

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

View File

@ -109,8 +109,8 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
protected FullHttpResponse newHandshakeResponse(FullHttpRequest req, HttpHeaders headers) { protected FullHttpResponse newHandshakeResponse(FullHttpRequest req, HttpHeaders headers) {
// Serve the WebSocket handshake request. // Serve the WebSocket handshake request.
if (!HttpHeaderValues.UPGRADE.equalsIgnoreCase(req.headers().get(HttpHeaderNames.CONNECTION)) if (!HttpHeaderValues.UPGRADE.contentEqualsIgnoreCase(req.headers().get(HttpHeaderNames.CONNECTION))
|| !WEBSOCKET.equalsIgnoreCase(req.headers().get(HttpHeaderNames.UPGRADE))) { || !WEBSOCKET.contentEqualsIgnoreCase(req.headers().get(HttpHeaderNames.UPGRADE))) {
throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade"); throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade");
} }
@ -133,7 +133,7 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
// New handshake getMethod with a challenge: // New handshake getMethod with a challenge:
res.headers().add(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, req.headers().get(HttpHeaderNames.ORIGIN)); res.headers().add(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, req.headers().get(HttpHeaderNames.ORIGIN));
res.headers().add(HttpHeaderNames.SEC_WEBSOCKET_LOCATION, uri()); res.headers().add(HttpHeaderNames.SEC_WEBSOCKET_LOCATION, uri());
String subprotocols = req.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL); String subprotocols = req.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL);
if (subprotocols != null) { if (subprotocols != null) {
String selectedSubprotocol = selectSubprotocol(subprotocols); String selectedSubprotocol = selectSubprotocol(subprotocols);
if (selectedSubprotocol == null) { if (selectedSubprotocol == null) {
@ -146,8 +146,8 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
} }
// Calculate the answer of the challenge. // Calculate the answer of the challenge.
String key1 = req.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_KEY1); String key1 = req.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_KEY1);
String key2 = req.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_KEY2); String key2 = req.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_KEY2);
int a = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key1).replaceAll("")) / int a = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key1).replaceAll("")) /
BEGINNING_SPACE.matcher(key1).replaceAll("").length()); BEGINNING_SPACE.matcher(key1).replaceAll("").length());
int b = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key2).replaceAll("")) / int b = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key2).replaceAll("")) /
@ -162,7 +162,7 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
// Old Hixie 75 handshake getMethod with no challenge: // Old Hixie 75 handshake getMethod with no challenge:
res.headers().add(HttpHeaderNames.WEBSOCKET_ORIGIN, req.headers().get(HttpHeaderNames.ORIGIN)); res.headers().add(HttpHeaderNames.WEBSOCKET_ORIGIN, req.headers().get(HttpHeaderNames.ORIGIN));
res.headers().add(HttpHeaderNames.WEBSOCKET_LOCATION, uri()); res.headers().add(HttpHeaderNames.WEBSOCKET_LOCATION, uri());
String protocol = req.headers().getAndConvert(HttpHeaderNames.WEBSOCKET_PROTOCOL); String protocol = req.headers().getAsString(HttpHeaderNames.WEBSOCKET_PROTOCOL);
if (protocol != null) { if (protocol != null) {
res.headers().add(HttpHeaderNames.WEBSOCKET_PROTOCOL, selectSubprotocol(protocol)); res.headers().add(HttpHeaderNames.WEBSOCKET_PROTOCOL, selectSubprotocol(protocol));
} }

View File

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

View File

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

View File

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

View File

@ -63,7 +63,7 @@ public class WebSocketClientExtensionHandler extends ChannelHandlerAdapter {
public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (msg instanceof HttpRequest && WebSocketExtensionUtil.isWebsocketUpgrade((HttpRequest) msg)) { if (msg instanceof HttpRequest && WebSocketExtensionUtil.isWebsocketUpgrade((HttpRequest) msg)) {
HttpRequest request = (HttpRequest) msg; HttpRequest request = (HttpRequest) msg;
String headerValue = request.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS); String headerValue = request.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS);
for (WebSocketClientExtensionHandshaker extentionHandshaker : extensionHandshakers) { for (WebSocketClientExtensionHandshaker extentionHandshaker : extensionHandshakers) {
WebSocketExtensionData extensionData = extentionHandshaker.newRequestData(); WebSocketExtensionData extensionData = extentionHandshaker.newRequestData();
@ -84,7 +84,7 @@ public class WebSocketClientExtensionHandler extends ChannelHandlerAdapter {
HttpResponse response = (HttpResponse) msg; HttpResponse response = (HttpResponse) msg;
if (WebSocketExtensionUtil.isWebsocketUpgrade(response)) { if (WebSocketExtensionUtil.isWebsocketUpgrade(response)) {
String extensionsHeader = response.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS); String extensionsHeader = response.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS);
if (extensionsHeader != null) { if (extensionsHeader != null) {
List<WebSocketExtensionData> extensions = List<WebSocketExtensionData> extensions =

View File

@ -69,7 +69,7 @@ public class WebSocketServerExtensionHandler extends ChannelHandlerAdapter {
HttpRequest request = (HttpRequest) msg; HttpRequest request = (HttpRequest) msg;
if (WebSocketExtensionUtil.isWebsocketUpgrade(request)) { if (WebSocketExtensionUtil.isWebsocketUpgrade(request)) {
String extensionsHeader = request.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS); String extensionsHeader = request.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS);
if (extensionsHeader != null) { if (extensionsHeader != null) {
List<WebSocketExtensionData> extensions = List<WebSocketExtensionData> extensions =
@ -107,7 +107,7 @@ public class WebSocketServerExtensionHandler extends ChannelHandlerAdapter {
if (msg instanceof HttpResponse && if (msg instanceof HttpResponse &&
WebSocketExtensionUtil.isWebsocketUpgrade((HttpResponse) msg) && validExtensions != null) { WebSocketExtensionUtil.isWebsocketUpgrade((HttpResponse) msg) && validExtensions != null) {
HttpResponse response = (HttpResponse) msg; HttpResponse response = (HttpResponse) msg;
String headerValue = response.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS); String headerValue = response.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS);
for (WebSocketServerExtension extension : validExtensions) { for (WebSocketServerExtension extension : validExtensions) {
WebSocketExtensionData extensionData = extension.newReponseData(); WebSocketExtensionData extensionData = extension.newReponseData();

View File

@ -15,18 +15,28 @@
*/ */
package io.netty.handler.codec.spdy; package io.netty.handler.codec.spdy;
import io.netty.handler.codec.DefaultTextHeaders; import io.netty.handler.codec.CharSequenceValueConverter;
import io.netty.handler.codec.TextHeaders; 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() { public DefaultSpdyHeaders() {
super(new LinkedHashMap<CharSequence, Object>(), super(CASE_INSENSITIVE_HASHER, HeaderValueConverterAndValidator.INSTANCE, SpydNameValidator);
HeaderNameValidator.INSTANCE,
HeaderValueConverterAndValidator.INSTANCE,
false);
} }
@Override @Override
@ -120,7 +130,7 @@ public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeader
} }
@Override @Override
public SpdyHeaders add(TextHeaders headers) { public SpdyHeaders add(Headers<? extends CharSequence> headers) {
super.add(headers); super.add(headers);
return this; return this;
} }
@ -216,13 +226,13 @@ public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeader
} }
@Override @Override
public SpdyHeaders set(TextHeaders headers) { public SpdyHeaders set(Headers<? extends CharSequence> headers) {
super.set(headers); super.set(headers);
return this; return this;
} }
@Override @Override
public SpdyHeaders setAll(TextHeaders headers) { public SpdyHeaders setAll(Headers<? extends CharSequence> headers) {
super.setAll(headers); super.setAll(headers);
return this; return this;
} }
@ -233,23 +243,38 @@ public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeader
return this; return this;
} }
private static class HeaderNameValidator implements NameValidator<CharSequence> { @Override
public String getAsString(CharSequence name) {
public static final HeaderNameValidator INSTANCE = new HeaderNameValidator(); return HeadersUtils.getAsString(this, name);
@Override
public void validate(CharSequence name) {
SpdyCodecUtil.validateHeaderName(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(); public static final HeaderValueConverterAndValidator INSTANCE = new HeaderValueConverterAndValidator();
@Override @Override
public CharSequence convertObject(Object value) { public CharSequence convertObject(Object value) {
CharSequence seq; final CharSequence seq;
if (value instanceof CharSequence) { if (value instanceof CharSequence) {
seq = (CharSequence) value; seq = (CharSequence) value;
} else { } else {

View File

@ -15,14 +15,18 @@
*/ */
package io.netty.handler.codec.spdy; package io.netty.handler.codec.spdy;
import io.netty.handler.codec.TextHeaders; import io.netty.handler.codec.Headers;
import io.netty.util.AsciiString; 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 * Provides the constants for the standard SPDY HTTP header names and commonly
* used utility methods that access a {@link SpdyHeadersFrame}. * used utility methods that access a {@link SpdyHeadersFrame}.
*/ */
public interface SpdyHeaders extends TextHeaders { public interface SpdyHeaders extends Headers<CharSequence> {
/** /**
* SPDY HTTP header names * SPDY HTTP header names
@ -102,7 +106,7 @@ public interface SpdyHeaders extends TextHeaders {
SpdyHeaders addTimeMillis(CharSequence name, long value); SpdyHeaders addTimeMillis(CharSequence name, long value);
@Override @Override
SpdyHeaders add(TextHeaders headers); SpdyHeaders add(Headers<? extends CharSequence> headers);
@Override @Override
SpdyHeaders set(CharSequence name, CharSequence value); SpdyHeaders set(CharSequence name, CharSequence value);
@ -150,11 +154,41 @@ public interface SpdyHeaders extends TextHeaders {
SpdyHeaders setObject(CharSequence name, Object... values); SpdyHeaders setObject(CharSequence name, Object... values);
@Override @Override
SpdyHeaders set(TextHeaders headers); SpdyHeaders set(Headers<? extends CharSequence> headers);
@Override @Override
SpdyHeaders setAll(TextHeaders headers); SpdyHeaders setAll(Headers<? extends CharSequence> headers);
@Override @Override
SpdyHeaders clear(); 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 { throws Exception {
// Create the first line of the request from the name/value pairs // Create the first line of the request from the name/value pairs
SpdyHeaders headers = requestFrame.headers(); SpdyHeaders headers = requestFrame.headers();
HttpMethod method = HttpMethod.valueOf(headers.getAndConvert(METHOD)); HttpMethod method = HttpMethod.valueOf(headers.getAsString(METHOD));
String url = headers.getAndConvert(PATH); String url = headers.getAsString(PATH);
HttpVersion httpVersion = HttpVersion.valueOf(headers.getAndConvert(VERSION)); HttpVersion httpVersion = HttpVersion.valueOf(headers.getAsString(VERSION));
headers.remove(METHOD); headers.remove(METHOD);
headers.remove(PATH); headers.remove(PATH);
headers.remove(VERSION); 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 // Create the first line of the response from the name/value pairs
SpdyHeaders headers = responseFrame.headers(); SpdyHeaders headers = responseFrame.headers();
HttpResponseStatus status = HttpResponseStatus.parseLine(headers.get(STATUS)); 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(STATUS);
headers.remove(VERSION); 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.asArray());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void addCharSequencesCsvWithExistingHeader() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.add(HEADER_NAME, HeaderValue.THREE.asArray());
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.asArray());
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.addObject(HEADER_NAME, HeaderValue.THREE.asArray());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void addObjectsIterableCsv() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.addObject(HEADER_NAME, HeaderValue.THREE.asList());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void addObjectsCsvWithExistingHeader() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.addObject(HEADER_NAME, HeaderValue.THREE.asArray());
headers.addObject(HEADER_NAME, HeaderValue.FIVE.subset(4));
assertCsvValues(headers, HeaderValue.FIVE);
}
@Test
public void setCharSequenceCsv() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.set(HEADER_NAME, HeaderValue.THREE.asArray());
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.setObject(HEADER_NAME, HeaderValue.THREE.asArray());
assertCsvValues(headers, HeaderValue.THREE);
}
@Test
public void setObjectIterableCsv() {
final CombinedHttpHeaders headers = newCombinedHttpHeaders();
headers.setObject(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.addObject(HEADER_NAME, v.toString());
}
}
}

View File

@ -15,14 +15,23 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.handler.codec.http.HttpHeadersTestUtils.HeaderValue;
import io.netty.util.AsciiString;
import org.junit.Test; import org.junit.Test;
import java.util.Iterator;
import java.util.List; import java.util.List;
import static io.netty.util.AsciiString.contentEquals;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals; 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 { public class DefaultHttpHeadersTest {
private static final String HEADER_NAME = "testHeader";
@Test @Test
public void keysShouldBeCaseInsensitive() { public void keysShouldBeCaseInsensitive() {
@ -56,4 +65,113 @@ public class DefaultHttpHeadersTest {
assertEquals(headers2, headers1); assertEquals(headers2, headers1);
assertEquals(headers1.hashCode(), headers2.hashCode()); 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<CharSequence> 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.asArray());
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.addObject(HEADER_NAME, HeaderValue.THREE.asArray());
assertDefaultValues(headers, HeaderValue.THREE);
}
@Test
public void setCharSequences() {
final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders();
headers.set(HEADER_NAME, HeaderValue.THREE.asArray());
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.setObject(HEADER_NAME, HeaderValue.THREE.asArray());
assertDefaultValues(headers, HeaderValue.THREE);
}
@Test
public void setObjectIterable() {
final DefaultHttpHeaders headers = newDefaultDefaultHttpHeaders();
headers.setObject(HEADER_NAME, HeaderValue.THREE.asList());
assertDefaultValues(headers, HeaderValue.THREE);
}
private static void assertDefaultValues(final DefaultHttpHeaders headers, final HeaderValue headerValue) {
assertTrue(contentEquals(headerValue.asArray()[0], headers.get(HEADER_NAME)));
List<CharSequence> expected = headerValue.asList();
List<CharSequence> actual = headers.getAll(HEADER_NAME);
assertEquals(expected.size(), actual.size());
Iterator<CharSequence> eItr = expected.iterator();
Iterator<CharSequence> aItr = actual.iterator();
while (eItr.hasNext()) {
assertTrue(contentEquals(eItr.next(), aItr.next()));
}
}
private static DefaultHttpHeaders newDefaultDefaultHttpHeaders() {
return new DefaultHttpHeaders(true);
}
} }

View File

@ -303,8 +303,8 @@ public class HttpContentCompressorTest {
HttpResponse res = (HttpResponse) o; HttpResponse res = (HttpResponse) o;
assertThat(res, is(not(instanceOf(HttpContent.class)))); assertThat(res, is(not(instanceOf(HttpContent.class))));
assertThat(res.headers().getAndConvert(HttpHeaderNames.TRANSFER_ENCODING), is("chunked")); assertThat(res.headers().getAsString(HttpHeaderNames.TRANSFER_ENCODING), is("chunked"));
assertThat(res.headers().get(HttpHeaderNames.CONTENT_LENGTH), is(nullValue())); assertThat(res.headers().get(HttpHeaderNames.CONTENT_LENGTH), is(nullValue()));
assertThat(res.headers().getAndConvert(HttpHeaderNames.CONTENT_ENCODING), is("gzip")); assertThat(res.headers().getAsString(HttpHeaderNames.CONTENT_ENCODING), is("gzip"));
} }
} }

View File

@ -333,7 +333,7 @@ public class HttpContentEncoderTest {
HttpResponse res = (HttpResponse) o; HttpResponse res = (HttpResponse) o;
assertThat(res, is(not(instanceOf(HttpContent.class)))); assertThat(res, is(not(instanceOf(HttpContent.class))));
assertThat(res.headers().getAndConvert(HttpHeaderNames.TRANSFER_ENCODING), is("chunked")); assertThat(res.headers().getAsString(HttpHeaderNames.TRANSFER_ENCODING), is("chunked"));
assertThat(res.headers().get(HttpHeaderNames.CONTENT_LENGTH), is(nullValue())); assertThat(res.headers().get(HttpHeaderNames.CONTENT_LENGTH), is(nullValue()));
HttpContent chunk = ch.readOutbound(); HttpContent chunk = ch.readOutbound();
@ -348,8 +348,8 @@ public class HttpContentEncoderTest {
HttpResponse res = (HttpResponse) o; HttpResponse res = (HttpResponse) o;
assertThat(res, is(not(instanceOf(HttpContent.class)))); assertThat(res, is(not(instanceOf(HttpContent.class))));
assertThat(res.headers().getAndConvert(HttpHeaderNames.TRANSFER_ENCODING), is("chunked")); assertThat(res.headers().getAsString(HttpHeaderNames.TRANSFER_ENCODING), is("chunked"));
assertThat(res.headers().get(HttpHeaderNames.CONTENT_LENGTH), is(nullValue())); assertThat(res.headers().get(HttpHeaderNames.CONTENT_LENGTH), is(nullValue()));
assertThat(res.headers().getAndConvert(HttpHeaderNames.CONTENT_ENCODING), is("test")); assertThat(res.headers().getAsString(HttpHeaderNames.CONTENT_ENCODING), is("test"));
} }
} }

View File

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

View File

@ -1,72 +0,0 @@
/*
* Copyright 2013 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 HttpHeadersTest {
@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<CharSequence> values = headers.getAll("Foo");
assertEquals(2, values.size());
assertEquals("1", values.get(0));
assertEquals("2", values.get(1));
}
@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));
}
@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);
}
}

View File

@ -0,0 +1,126 @@
/*
* 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.Arrays;
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 CharSequence[] array;
HeaderValue(final String value, final int nr) {
this.nr = nr;
this.value = value;
}
@Override
public String toString() {
return value;
}
public CharSequence[] 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 CharSequence[] arr) {
final StringBuilder sb = new StringBuilder();
int end = arr.length - 1;
for (int i = 0; i < end; i++) {
final CharSequence value = arr[i];
quoted(sb, value).append(COMMA);
}
quoted(sb, arr[end]);
return sb.toString();
}
public CharSequence asCsv() {
return asCsv(asArray());
}
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

@ -248,7 +248,7 @@ public class HttpResponseDecoderTest {
HttpResponse res = ch.readInbound(); HttpResponse res = ch.readInbound();
assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
assertThat(res.status(), is(HttpResponseStatus.OK)); assertThat(res.status(), is(HttpResponseStatus.OK));
assertThat(res.headers().getAndConvert(HttpHeaderNames.TRANSFER_ENCODING), is("chunked")); assertThat(res.headers().getAsString(HttpHeaderNames.TRANSFER_ENCODING), is("chunked"));
assertThat(ch.readInbound(), is(nullValue())); assertThat(ch.readInbound(), is(nullValue()));
// Close the connection without sending anything. // Close the connection without sending anything.
@ -269,7 +269,7 @@ public class HttpResponseDecoderTest {
HttpResponse res = ch.readInbound(); HttpResponse res = ch.readInbound();
assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
assertThat(res.status(), is(HttpResponseStatus.OK)); assertThat(res.status(), is(HttpResponseStatus.OK));
assertThat(res.headers().getAndConvert(HttpHeaderNames.TRANSFER_ENCODING), is("chunked")); assertThat(res.headers().getAsString(HttpHeaderNames.TRANSFER_ENCODING), is("chunked"));
// Read the partial content. // Read the partial content.
HttpContent content = ch.readInbound(); HttpContent content = ch.readInbound();
@ -339,7 +339,7 @@ public class HttpResponseDecoderTest {
HttpResponse res = ch.readInbound(); HttpResponse res = ch.readInbound();
assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1));
assertThat(res.status(), is(HttpResponseStatus.OK)); assertThat(res.status(), is(HttpResponseStatus.OK));
assertThat(res.headers().getAndConvert("X-Header"), is("h2=h2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT")); assertThat(res.headers().getAsString("X-Header"), is("h2=h2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT"));
assertThat(ch.readInbound(), is(nullValue())); assertThat(ch.readInbound(), is(nullValue()));
ch.writeInbound(Unpooled.wrappedBuffer(new byte[1024])); ch.writeInbound(Unpooled.wrappedBuffer(new byte[1024]));

View File

@ -91,7 +91,7 @@ public class HttpServerCodecTest {
// Ensure the aggregator generates a full request. // Ensure the aggregator generates a full request.
FullHttpRequest req = ch.readInbound(); FullHttpRequest req = ch.readInbound();
assertThat(req.headers().getAndConvert(HttpHeaderNames.CONTENT_LENGTH), is("1")); assertThat(req.headers().getAsString(HttpHeaderNames.CONTENT_LENGTH), is("1"));
assertThat(req.content().readableBytes(), is(1)); assertThat(req.content().readableBytes(), is(1));
assertThat(req.content().readByte(), is((byte) 42)); assertThat(req.content().readByte(), is((byte) 42));
req.release(); req.release();

View File

@ -95,20 +95,20 @@ public class CorsConfigTest {
@Test @Test
public void preflightResponseHeadersSingleValue() { public void preflightResponseHeadersSingleValue() {
final CorsConfig cors = withAnyOrigin().preflightResponseHeader("SingleValue", "value").build(); final CorsConfig cors = withAnyOrigin().preflightResponseHeader("SingleValue", "value").build();
assertThat(cors.preflightResponseHeaders().getAndConvert("SingleValue"), equalTo("value")); assertThat(cors.preflightResponseHeaders().getAsString("SingleValue"), equalTo("value"));
} }
@Test @Test
public void preflightResponseHeadersMultipleValues() { public void preflightResponseHeadersMultipleValues() {
final CorsConfig cors = withAnyOrigin().preflightResponseHeader("MultipleValues", "value1", "value2").build(); final CorsConfig cors = withAnyOrigin().preflightResponseHeader("MultipleValues", "value1", "value2").build();
assertThat(cors.preflightResponseHeaders().getAllAndConvert("MultipleValues"), hasItems("value1", "value2")); assertThat(cors.preflightResponseHeaders().getAllAsString("MultipleValues"), hasItems("value1", "value2"));
} }
@Test @Test
public void defaultPreflightResponseHeaders() { public void defaultPreflightResponseHeaders() {
final CorsConfig cors = withAnyOrigin().build(); final CorsConfig cors = withAnyOrigin().build();
assertThat(cors.preflightResponseHeaders().get(HttpHeaderNames.DATE), is(notNullValue())); assertThat(cors.preflightResponseHeaders().get(HttpHeaderNames.DATE), is(notNullValue()));
assertThat(cors.preflightResponseHeaders().getAndConvert(HttpHeaderNames.CONTENT_LENGTH), is("0")); assertThat(cors.preflightResponseHeaders().getAsString(HttpHeaderNames.CONTENT_LENGTH), is("0"));
} }
@Test @Test

View File

@ -47,14 +47,14 @@ public class CorsHandlerTest {
@Test @Test
public void simpleRequestWithAnyOrigin() { public void simpleRequestWithAnyOrigin() {
final HttpResponse response = simpleRequest(CorsConfig.withAnyOrigin().build(), "http://localhost:7777"); final HttpResponse response = simpleRequest(CorsConfig.withAnyOrigin().build(), "http://localhost:7777");
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is("*")); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_ORIGIN), is("*"));
} }
@Test @Test
public void simpleRequestWithOrigin() { public void simpleRequestWithOrigin() {
final String origin = "http://localhost:8888"; final String origin = "http://localhost:8888";
final HttpResponse response = simpleRequest(CorsConfig.withOrigin(origin).build(), origin); final HttpResponse response = simpleRequest(CorsConfig.withOrigin(origin).build(), origin);
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin)); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin));
} }
@Test @Test
@ -63,9 +63,9 @@ public class CorsHandlerTest {
final String origin2 = "https://localhost:8888"; final String origin2 = "https://localhost:8888";
final String[] origins = {origin1, origin2}; final String[] origins = {origin1, origin2};
final HttpResponse response1 = simpleRequest(CorsConfig.withOrigins(origins).build(), origin1); final HttpResponse response1 = simpleRequest(CorsConfig.withOrigins(origins).build(), origin1);
assertThat(response1.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin1)); assertThat(response1.headers().getAsString(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin1));
final HttpResponse response2 = simpleRequest(CorsConfig.withOrigins(origins).build(), origin2); final HttpResponse response2 = simpleRequest(CorsConfig.withOrigins(origins).build(), origin2);
assertThat(response2.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin2)); assertThat(response2.headers().getAsString(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin2));
} }
@Test @Test
@ -81,10 +81,10 @@ public class CorsHandlerTest {
.allowedRequestMethods(GET, DELETE) .allowedRequestMethods(GET, DELETE)
.build(); .build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888")); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888"));
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_METHODS), containsString("GET")); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_METHODS), containsString("GET"));
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_METHODS), containsString("DELETE")); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_METHODS), containsString("DELETE"));
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString())); assertThat(response.headers().getAsString(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -94,21 +94,21 @@ public class CorsHandlerTest {
.allowedRequestHeaders("content-type", "xheader1") .allowedRequestHeaders("content-type", "xheader1")
.build(); .build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888")); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888"));
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_METHODS), containsString("OPTIONS")); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_METHODS), containsString("OPTIONS"));
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_METHODS), containsString("GET")); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_METHODS), containsString("GET"));
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_HEADERS), containsString("content-type")); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_HEADERS), containsString("content-type"));
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_HEADERS), containsString("xheader1")); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_HEADERS), containsString("xheader1"));
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString())); assertThat(response.headers().getAsString(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
public void preflightRequestWithDefaultHeaders() { public void preflightRequestWithDefaultHeaders() {
final CorsConfig config = CorsConfig.withOrigin("http://localhost:8888").build(); final CorsConfig config = CorsConfig.withOrigin("http://localhost:8888").build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().getAndConvert(CONTENT_LENGTH), is("0")); assertThat(response.headers().getAsString(CONTENT_LENGTH), is("0"));
assertThat(response.headers().get(DATE), is(notNullValue())); assertThat(response.headers().get(DATE), is(notNullValue()));
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString())); assertThat(response.headers().getAsString(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -117,8 +117,8 @@ public class CorsHandlerTest {
.preflightResponseHeader("CustomHeader", "somevalue") .preflightResponseHeader("CustomHeader", "somevalue")
.build(); .build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().getAndConvert("CustomHeader"), equalTo("somevalue")); assertThat(response.headers().getAsString("CustomHeader"), equalTo("somevalue"));
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString())); assertThat(response.headers().getAsString(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -131,7 +131,7 @@ public class CorsHandlerTest {
.build(); .build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertValues(response, headerName, value1, value2); assertValues(response, headerName, value1, value2);
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString())); assertThat(response.headers().getAsString(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -144,7 +144,7 @@ public class CorsHandlerTest {
.build(); .build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertValues(response, headerName, value1, value2); assertValues(response, headerName, value1, value2);
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString())); assertThat(response.headers().getAsString(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -157,8 +157,8 @@ public class CorsHandlerTest {
} }
}).build(); }).build();
final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1");
assertThat(response.headers().getAndConvert("GenHeader"), equalTo("generatedValue")); assertThat(response.headers().getAsString("GenHeader"), equalTo("generatedValue"));
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString())); assertThat(response.headers().getAsString(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
@ -169,9 +169,9 @@ public class CorsHandlerTest {
.allowCredentials() .allowCredentials()
.build(); .build();
final HttpResponse response = preflightRequest(config, origin, "content-type, xheader1"); final HttpResponse response = preflightRequest(config, origin, "content-type, xheader1");
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is(equalTo("*"))); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_ORIGIN), is(equalTo("*")));
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is(equalTo("*"))); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_ORIGIN), is(equalTo("*")));
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_CREDENTIALS), is(nullValue())); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_CREDENTIALS), is(nullValue()));
} }
@Test @Test
@ -179,7 +179,7 @@ public class CorsHandlerTest {
final String origin = "null"; final String origin = "null";
final CorsConfig config = CorsConfig.withOrigin(origin).allowCredentials().build(); final CorsConfig config = CorsConfig.withOrigin(origin).allowCredentials().build();
final HttpResponse response = preflightRequest(config, origin, "content-type, xheader1"); final HttpResponse response = preflightRequest(config, origin, "content-type, xheader1");
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_CREDENTIALS), is(equalTo("true"))); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_CREDENTIALS), is(equalTo("true")));
} }
@Test @Test
@ -194,16 +194,16 @@ public class CorsHandlerTest {
public void simpleRequestCustomHeaders() { public void simpleRequestCustomHeaders() {
final CorsConfig config = CorsConfig.withAnyOrigin().exposeHeaders("custom1", "custom2").build(); final CorsConfig config = CorsConfig.withAnyOrigin().exposeHeaders("custom1", "custom2").build();
final HttpResponse response = simpleRequest(config, "http://localhost:7777"); final HttpResponse response = simpleRequest(config, "http://localhost:7777");
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("*")); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("*"));
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_EXPOSE_HEADERS), containsString("custom1")); assertThat(response.headers().getAsString(ACCESS_CONTROL_EXPOSE_HEADERS), containsString("custom1"));
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_EXPOSE_HEADERS), containsString("custom2")); assertThat(response.headers().getAsString(ACCESS_CONTROL_EXPOSE_HEADERS), containsString("custom2"));
} }
@Test @Test
public void simpleRequestAllowCredentials() { public void simpleRequestAllowCredentials() {
final CorsConfig config = CorsConfig.withAnyOrigin().allowCredentials().build(); final CorsConfig config = CorsConfig.withAnyOrigin().allowCredentials().build();
final HttpResponse response = simpleRequest(config, "http://localhost:7777"); final HttpResponse response = simpleRequest(config, "http://localhost:7777");
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true")); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true"));
} }
@Test @Test
@ -217,17 +217,17 @@ public class CorsHandlerTest {
public void anyOriginAndAllowCredentialsShouldEchoRequestOrigin() { public void anyOriginAndAllowCredentialsShouldEchoRequestOrigin() {
final CorsConfig config = CorsConfig.withAnyOrigin().allowCredentials().build(); final CorsConfig config = CorsConfig.withAnyOrigin().allowCredentials().build();
final HttpResponse response = simpleRequest(config, "http://localhost:7777"); final HttpResponse response = simpleRequest(config, "http://localhost:7777");
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true")); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true"));
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("http://localhost:7777")); assertThat(response.headers().getAsString(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("http://localhost:7777"));
assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString())); assertThat(response.headers().getAsString(VARY), equalTo(ORIGIN.toString()));
} }
@Test @Test
public void simpleRequestExposeHeaders() { public void simpleRequestExposeHeaders() {
final CorsConfig config = CorsConfig.withAnyOrigin().exposeHeaders("one", "two").build(); final CorsConfig config = CorsConfig.withAnyOrigin().exposeHeaders("one", "two").build();
final HttpResponse response = simpleRequest(config, "http://localhost:7777"); final HttpResponse response = simpleRequest(config, "http://localhost:7777");
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_EXPOSE_HEADERS), containsString("one")); assertThat(response.headers().getAsString(ACCESS_CONTROL_EXPOSE_HEADERS), containsString("one"));
assertThat(response.headers().getAndConvert(ACCESS_CONTROL_EXPOSE_HEADERS), containsString("two")); assertThat(response.headers().getAsString(ACCESS_CONTROL_EXPOSE_HEADERS), containsString("two"));
} }
@Test @Test
@ -328,7 +328,7 @@ public class CorsHandlerTest {
} }
private static void assertValues(final HttpResponse response, final String headerName, final String... values) { private static void assertValues(final HttpResponse response, final String headerName, final String... values) {
final String header = response.headers().getAndConvert(headerName); final String header = response.headers().getAsString(headerName);
for (String value : values) { for (String value : values) {
assertThat(header, containsString(value)); assertThat(header, containsString(value));
} }

View File

@ -67,14 +67,14 @@ public class WebSocketClientExtensionHandlerTest {
HttpRequest req2 = ch.readOutbound(); HttpRequest req2 = ch.readOutbound();
List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions(
req2.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); req2.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS));
HttpResponse res = newUpgradeResponse("main"); HttpResponse res = newUpgradeResponse("main");
ch.writeInbound(res); ch.writeInbound(res);
HttpResponse res2 = ch.readInbound(); HttpResponse res2 = ch.readInbound();
List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions(
res2.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS));
// test // test
assertEquals(2, reqExts.size()); assertEquals(2, reqExts.size());
@ -117,14 +117,14 @@ public class WebSocketClientExtensionHandlerTest {
HttpRequest req2 = ch.readOutbound(); HttpRequest req2 = ch.readOutbound();
List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions(
req2.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); req2.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS));
HttpResponse res = newUpgradeResponse("fallback"); HttpResponse res = newUpgradeResponse("fallback");
ch.writeInbound(res); ch.writeInbound(res);
HttpResponse res2 = ch.readInbound(); HttpResponse res2 = ch.readInbound();
List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions(
res2.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS));
// test // test
assertEquals(2, reqExts.size()); assertEquals(2, reqExts.size());
@ -180,14 +180,14 @@ public class WebSocketClientExtensionHandlerTest {
HttpRequest req2 = ch.readOutbound(); HttpRequest req2 = ch.readOutbound();
List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions(
req2.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); req2.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS));
HttpResponse res = newUpgradeResponse("main, fallback"); HttpResponse res = newUpgradeResponse("main, fallback");
ch.writeInbound(res); ch.writeInbound(res);
HttpResponse res2 = ch.readInbound(); HttpResponse res2 = ch.readInbound();
List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions(
res2.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS));
// test // test
assertEquals(2, reqExts.size()); assertEquals(2, reqExts.size());
@ -237,7 +237,7 @@ public class WebSocketClientExtensionHandlerTest {
HttpRequest req2 = ch.readOutbound(); HttpRequest req2 = ch.readOutbound();
List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> reqExts = WebSocketExtensionUtil.extractExtensions(
req2.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); req2.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS));
HttpResponse res = newUpgradeResponse("main, fallback"); HttpResponse res = newUpgradeResponse("main, fallback");
ch.writeInbound(res); ch.writeInbound(res);

View File

@ -76,7 +76,7 @@ public class WebSocketServerExtensionHandlerTest {
HttpResponse res2 = ch.readOutbound(); HttpResponse res2 = ch.readOutbound();
List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions(
res2.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS));
// test // test
assertEquals(1, resExts.size()); assertEquals(1, resExts.size());
@ -127,7 +127,7 @@ public class WebSocketServerExtensionHandlerTest {
HttpResponse res2 = ch.readOutbound(); HttpResponse res2 = ch.readOutbound();
List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> resExts = WebSocketExtensionUtil.extractExtensions(
res2.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS));
// test // test
assertEquals(2, resExts.size()); assertEquals(2, resExts.size());

View File

@ -45,7 +45,7 @@ public class WebSocketServerCompressionHandlerTest {
HttpResponse res2 = ch.readOutbound(); HttpResponse res2 = ch.readOutbound();
List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions(
res2.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS));
assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name());
assertTrue(exts.get(0).parameters().isEmpty()); assertTrue(exts.get(0).parameters().isEmpty());
@ -66,7 +66,7 @@ public class WebSocketServerCompressionHandlerTest {
HttpResponse res2 = ch.readOutbound(); HttpResponse res2 = ch.readOutbound();
List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions(
res2.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS));
assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name());
assertEquals("10", exts.get(0).parameters().get(CLIENT_MAX_WINDOW)); assertEquals("10", exts.get(0).parameters().get(CLIENT_MAX_WINDOW));
@ -87,7 +87,7 @@ public class WebSocketServerCompressionHandlerTest {
HttpResponse res2 = ch.readOutbound(); HttpResponse res2 = ch.readOutbound();
List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions(
res2.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS));
assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name());
assertTrue(exts.get(0).parameters().isEmpty()); assertTrue(exts.get(0).parameters().isEmpty());
@ -108,7 +108,7 @@ public class WebSocketServerCompressionHandlerTest {
HttpResponse res2 = ch.readOutbound(); HttpResponse res2 = ch.readOutbound();
List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions(
res2.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS));
assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name());
assertEquals("10", exts.get(0).parameters().get(SERVER_MAX_WINDOW)); assertEquals("10", exts.get(0).parameters().get(SERVER_MAX_WINDOW));
@ -163,7 +163,7 @@ public class WebSocketServerCompressionHandlerTest {
HttpResponse res2 = ch.readOutbound(); HttpResponse res2 = ch.readOutbound();
List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions(
res2.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS));
assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name());
assertTrue(exts.get(0).parameters().isEmpty()); assertTrue(exts.get(0).parameters().isEmpty());
@ -185,7 +185,7 @@ public class WebSocketServerCompressionHandlerTest {
HttpResponse res2 = ch.readOutbound(); HttpResponse res2 = ch.readOutbound();
List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions( List<WebSocketExtensionData> exts = WebSocketExtensionUtil.extractExtensions(
res2.headers().getAndConvert(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)); res2.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS));
assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name());
assertTrue(exts.get(0).parameters().isEmpty()); assertTrue(exts.get(0).parameters().isEmpty());

View File

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

View File

@ -14,19 +14,16 @@
*/ */
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.handler.codec.BinaryHeaders; import io.netty.handler.codec.ByteStringValueConverter;
import io.netty.handler.codec.DefaultBinaryHeaders;
import io.netty.handler.codec.DefaultHeaders; import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.Headers;
import io.netty.util.ByteString; 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() { public DefaultHttp2Headers() {
super(new TreeMap<ByteString, Object>(Http2HeaderNameComparator.INSTANCE)); super(ByteStringValueConverter.INSTANCE);
} }
@Override @Override
@ -120,7 +117,7 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He
} }
@Override @Override
public Http2Headers add(BinaryHeaders headers) { public Http2Headers add(Headers<? extends ByteString> headers) {
super.add(headers); super.add(headers);
return this; return this;
} }
@ -216,13 +213,13 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He
} }
@Override @Override
public Http2Headers set(BinaryHeaders headers) { public Http2Headers set(Headers<? extends ByteString> headers) {
super.set(headers); super.set(headers);
return this; return this;
} }
@Override @Override
public Http2Headers setAll(BinaryHeaders headers) { public Http2Headers setAll(Headers<? extends ByteString> headers) {
super.setAll(headers); super.setAll(headers);
return this; return this;
} }
@ -289,43 +286,37 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He
} }
@Override @Override
public int hashCode() { protected final HeaderEntry<ByteString> newHeaderEntry(int h, ByteString name, ByteString value,
return size(); HeaderEntry<ByteString> next) {
return new Http2HeaderEntry(h, name, value, next);
} }
@Override private final class Http2HeaderEntry extends HeaderEntry<ByteString> {
public boolean equals(Object other) { protected Http2HeaderEntry(int hash, ByteString key, ByteString value, HeaderEntry<ByteString> next) {
if (!(other instanceof Http2Headers)) { super(hash, key);
return false; this.value = value;
} this.next = next;
Http2Headers headers = (Http2Headers) other;
return DefaultHeaders.comparatorEquals(this, headers, ByteString.DEFAULT_COMPARATOR);
}
private static class Http2HeaderNameComparator implements Comparator<ByteString>, Serializable { // Make sure the pseudo headers fields are first in iteration order
if (!key.isEmpty() && key.byteAt(0) == ':') {
public static final Http2HeaderNameComparator INSTANCE = new Http2HeaderNameComparator(); after = firstNonPseudo;
private static final long serialVersionUID = 1109871697664666478L; before = firstNonPseudo.before();
} else {
@Override after = head;
public int compare(ByteString one, ByteString two) { before = head.before();
// Reserved header names come first. if (firstNonPseudo == head) {
final boolean isPseudoHeader1 = !one.isEmpty() && one.byteAt(0) == ':'; firstNonPseudo = this;
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);
} }
} }
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; package io.netty.handler.codec.http2;
import io.netty.handler.codec.BinaryHeaders; import io.netty.handler.codec.EmptyHeaders;
import io.netty.handler.codec.EmptyBinaryHeaders; import io.netty.handler.codec.Headers;
import io.netty.util.ByteString; 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(); public static final EmptyHttp2Headers INSTANCE = new EmptyHttp2Headers();
private EmptyHttp2Headers() { private EmptyHttp2Headers() {
@ -116,7 +116,7 @@ public final class EmptyHttp2Headers extends EmptyBinaryHeaders implements Http2
} }
@Override @Override
public Http2Headers add(BinaryHeaders headers) { public Http2Headers add(Headers<? extends ByteString> headers) {
super.add(headers); super.add(headers);
return this; return this;
} }
@ -212,13 +212,13 @@ public final class EmptyHttp2Headers extends EmptyBinaryHeaders implements Http2
} }
@Override @Override
public Http2Headers set(BinaryHeaders headers) { public Http2Headers set(Headers<? extends ByteString> headers) {
super.set(headers); super.set(headers);
return this; return this;
} }
@Override @Override
public Http2Headers setAll(BinaryHeaders headers) { public Http2Headers setAll(Headers<? extends ByteString> headers) {
super.setAll(headers); super.setAll(headers);
return this; return this;
} }

View File

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

View File

@ -274,7 +274,7 @@ public final class HttpUtil {
out.path(new AsciiString(request.uri())); out.path(new AsciiString(request.uri()));
out.method(new AsciiString(request.method().toString())); out.method(new AsciiString(request.method().toString()));
String value = inHeaders.getAndConvert(HttpHeaderNames.HOST); String value = inHeaders.getAsString(HttpHeaderNames.HOST);
if (value != null) { if (value != null) {
URI hostUri = URI.create(value); URI hostUri = URI.create(value);
// The authority MUST NOT include the deprecated "userinfo" subcomponent // The authority MUST NOT include the deprecated "userinfo" subcomponent
@ -321,8 +321,8 @@ public final class HttpUtil {
AsciiString aValue = AsciiString.of(entry.getValue()); AsciiString aValue = AsciiString.of(entry.getValue());
// https://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-8.1.2.2 // https://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-8.1.2.2
// makes a special exception for TE // makes a special exception for TE
if (!aName.equalsIgnoreCase(HttpHeaderNames.TE) || if (!aName.contentEqualsIgnoreCase(HttpHeaderNames.TE) ||
aValue.equalsIgnoreCase(HttpHeaderValues.TRAILERS)) { aValue.contentEqualsIgnoreCase(HttpHeaderValues.TRAILERS)) {
out.add(aName, aValue); out.add(aName, aValue);
} }
} }
@ -331,7 +331,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 { private static final class Http2ToHttpHeaderTranslator {
/** /**

View File

@ -14,11 +14,6 @@
*/ */
package io.netty.handler.codec.http2; 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.Map.Entry;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.FullHttpMessage; import io.netty.handler.codec.http.FullHttpMessage;
@ -26,7 +21,11 @@ import io.netty.handler.codec.http.HttpHeaders;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.collection.IntObjectHashMap; import io.netty.util.collection.IntObjectHashMap;
import io.netty.util.collection.IntObjectMap; import io.netty.util.collection.IntObjectMap;
import io.netty.util.internal.PlatformDependent;
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} * Translate header/data/priority HTTP/2 frame events into HTTP events. Just as {@link InboundHttp2ToHttpAdapter}
@ -135,12 +134,8 @@ public final class InboundHttp2ToHttpPriorityAdapter extends InboundHttp2ToHttpA
* @param http2Headers The target HTTP/2 headers * @param http2Headers The target HTTP/2 headers
*/ */
private static void addHttpHeadersToHttp2Headers(HttpHeaders httpHeaders, final Http2Headers http2Headers) { private static void addHttpHeadersToHttp2Headers(HttpHeaders httpHeaders, final Http2Headers http2Headers) {
try { for (Entry<CharSequence, CharSequence> entry : httpHeaders) {
for (Entry<CharSequence, CharSequence> entry : httpHeaders) { http2Headers.add(AsciiString.of(entry.getKey()), AsciiString.of(entry.getValue()));
http2Headers.add(AsciiString.of(entry.getKey()), AsciiString.of(entry.getValue()));
}
} catch (Exception ex) {
PlatformDependent.throwException(ex);
} }
} }

View File

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

View File

@ -15,9 +15,29 @@
package io.netty.handler.codec.http2; 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.DEFAULT_MAX_HEADER_SIZE;
import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_UNSIGNED_INT; 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 io.netty.handler.codec.http2.Http2TestUtil.randomString;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.Matchers.any; 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.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; 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}. * Integration tests for {@link DefaultHttp2FrameReader} and {@link DefaultHttp2FrameWriter}.
@ -365,8 +365,9 @@ public class DefaultHttp2FrameIOTest {
} }
private static Http2Headers dummyHeaders() { private static Http2Headers dummyHeaders() {
return new DefaultHttp2Headers().method(as("GET")).scheme(as("https")).authority(as("example.org")) return new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https"))
.path(as("/some/path")).add(as("accept"), as("*/*")); .authority(new AsciiString("example.org")).path(new AsciiString("/some/path"))
.add(new AsciiString("accept"), new AsciiString("*/*"));
} }
private static Http2Headers largeHeaders() { private static Http2Headers largeHeaders() {
@ -374,7 +375,7 @@ public class DefaultHttp2FrameIOTest {
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; ++i) {
String key = "this-is-a-test-header-key-" + i; String key = "this-is-a-test-header-key-" + i;
String value = "this-is-a-test-header-value-" + 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; return headers;
} }

View File

@ -15,12 +15,11 @@
package io.netty.handler.codec.http2; 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.Before;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertEquals;
/** /**
* Tests for {@link DefaultHttp2HeaderTableListSize}. * Tests for {@link DefaultHttp2HeaderTableListSize}.
*/ */

View File

@ -15,22 +15,21 @@
package io.netty.handler.codec.http2; 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.DEFAULT_MAX_HEADER_SIZE;
import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_HEADER_TABLE_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.handler.codec.http2.Http2TestUtil.randomBytes;
import static io.netty.util.CharsetUtil.UTF_8; import static io.netty.util.CharsetUtil.UTF_8;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail; 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}. * Tests for {@link DefaultHttp2HeadersDecoder}.
@ -51,7 +50,7 @@ public class DefaultHttp2HeadersDecoderTest {
Http2Headers headers = decoder.decodeHeaders(buf); Http2Headers headers = decoder.decodeHeaders(buf);
assertEquals(3, headers.size()); assertEquals(3, headers.size());
assertEquals("GET", headers.method().toString()); assertEquals("GET", headers.method().toString());
assertEquals("avalue", headers.get(as("akey")).toString()); assertEquals("avalue", headers.get(new AsciiString("akey")).toString());
} finally { } finally {
buf.release(); buf.release();
} }

View File

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

View File

@ -16,42 +16,83 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName;
import io.netty.util.ByteString; import io.netty.util.ByteString;
import io.netty.util.CharsetUtil;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList; import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry; 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.assertTrue;
import static org.junit.Assert.fail;
public class DefaultHttp2HeadersTest { public class DefaultHttp2HeadersTest {
@Test @Test
public void pseudoHeadersMustComeFirstWhenIterating() { public void pseudoHeadersMustComeFirstWhenIterating() {
DefaultHttp2Headers headers = new DefaultHttp2Headers(); Http2Headers headers = newHeaders();
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"));
Iterator<Entry<ByteString, ByteString>> iter = headers.iterator(); verifyPseudoHeadersFirst(headers);
List<ByteString> names = new ArrayList<ByteString>(); verifyAllPseudoHeadersPresent(headers);
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"))));
} }
private static ByteString bs(String str) { @Test
return new ByteString(str, CharsetUtil.US_ASCII); 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; 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.Bootstrap;
import io.netty.bootstrap.ServerBootstrap; import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
@ -53,15 +31,11 @@ import io.netty.channel.ChannelPromise;
import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel; 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.handler.codec.http2.Http2TestUtil.Http2Runnable;
import io.netty.util.AsciiString;
import io.netty.util.NetUtil; import io.netty.util.NetUtil;
import io.netty.util.concurrent.Future; 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.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -70,6 +44,32 @@ import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; 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. * Tests the full HTTP/2 framing stack including the connection and preface handlers.
*/ */
@ -495,8 +495,9 @@ public class Http2ConnectionRoundtripTest {
} }
private static Http2Headers dummyHeaders() { private static Http2Headers dummyHeaders() {
return new DefaultHttp2Headers().method(as("GET")).scheme(as("https")) return new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https"))
.authority(as("example.org")).path(as("/some/path/resource2")).add(randomString(), randomString()); .authority(new AsciiString("example.org")).path(new AsciiString("/some/path/resource2"))
.add(randomString(), randomString());
} }
private void mockFlowControl(Http2FrameListener listener) throws Http2Exception { 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.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http2.Http2TestUtil.Http2Runnable; import io.netty.handler.codec.http2.Http2TestUtil.Http2Runnable;
import io.netty.util.AsciiString;
import io.netty.util.NetUtil; import io.netty.util.NetUtil;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import org.junit.After; import org.junit.After;
@ -46,7 +47,6 @@ import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; 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.randomString;
import static io.netty.handler.codec.http2.Http2TestUtil.runInChannel; import static io.netty.handler.codec.http2.Http2TestUtil.runInChannel;
import static io.netty.util.CharsetUtil.UTF_8; import static io.netty.util.CharsetUtil.UTF_8;
@ -393,7 +393,8 @@ public class Http2FrameRoundtripTest {
} }
private static Http2Headers headers() { private static Http2Headers headers() {
return new DefaultHttp2Headers().method(as("GET")).scheme(as("https")) return new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https"))
.authority(as("example.org")).path(as("/some/path/resource2")).add(randomString(), randomString()); .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; 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.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.util.AsciiString;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; 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. * Tests for encoding/decoding HTTP2 header blocks.
*/ */
@ -55,23 +55,23 @@ public class Http2HeaderBlockIOTest {
@Test @Test
public void successiveCallsShouldSucceed() throws Http2Exception { public void successiveCallsShouldSucceed() throws Http2Exception {
Http2Headers in = Http2Headers in =
new DefaultHttp2Headers().method(as("GET")).scheme(as("https")) new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https"))
.authority(as("example.org")).path(as("/some/path")) .authority(new AsciiString("example.org")).path(new AsciiString("/some/path"))
.add(as("accept"), as("*/*")); .add(new AsciiString("accept"), new AsciiString("*/*"));
assertRoundtripSuccessful(in); assertRoundtripSuccessful(in);
in = in =
new DefaultHttp2Headers().method(as("GET")).scheme(as("https")) new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https"))
.authority(as("example.org")).path(as("/some/path/resource1")) .authority(new AsciiString("example.org")).path(new AsciiString("/some/path/resource1"))
.add(as("accept"), as("image/jpeg")) .add(new AsciiString("accept"), new AsciiString("image/jpeg"))
.add(as("cache-control"), as("no-cache")); .add(new AsciiString("cache-control"), new AsciiString("no-cache"));
assertRoundtripSuccessful(in); assertRoundtripSuccessful(in);
in = in =
new DefaultHttp2Headers().method(as("GET")).scheme(as("https")) new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https"))
.authority(as("example.org")).path(as("/some/path/resource2")) .authority(new AsciiString("example.org")).path(new AsciiString("/some/path/resource2"))
.add(as("accept"), as("image/png")) .add(new AsciiString("accept"), new AsciiString("image/png"))
.add(as("cache-control"), as("no-cache")); .add(new AsciiString("cache-control"), new AsciiString("no-cache"));
assertRoundtripSuccessful(in); assertRoundtripSuccessful(in);
} }
@ -91,11 +91,14 @@ public class Http2HeaderBlockIOTest {
} }
private static Http2Headers headers() { private static Http2Headers headers() {
return new DefaultHttp2Headers().method(as("GET")).scheme(as("https")) return new DefaultHttp2Headers().method(new AsciiString("GET")).scheme(new AsciiString("https"))
.authority(as("example.org")).path(as("/some/path/resource2")) .authority(new AsciiString("example.org")).path(new AsciiString("/some/path/resource2"))
.add(as("accept"), as("image/png")).add(as("cache-control"), as("no-cache")) .add(new AsciiString("accept"), new AsciiString("image/png"))
.add(as("custom"), as("value1")).add(as("custom"), as("value2")) .add(new AsciiString("cache-control"), new AsciiString("no-cache"))
.add(as("custom"), as("value3")).add(as("custom"), as("custom4")) .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()); .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. * 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. * Returns an {@link AsciiString} that wraps a randomly-filled byte array.
*/ */
public static ByteString randomString() { public static ByteString randomString() {
return bs(randomBytes()); return new ByteString(randomBytes());
} }
private Http2TestUtil() { private Http2TestUtil() {

View File

@ -14,23 +14,6 @@
*/ */
package io.netty.handler.codec.http2; 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.Bootstrap;
import io.netty.bootstrap.ServerBootstrap; import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf; 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.HttpRequest;
import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http2.Http2TestUtil.FrameCountDown; import io.netty.handler.codec.http2.Http2TestUtil.FrameCountDown;
import io.netty.util.AsciiString;
import io.netty.util.NetUtil; import io.netty.util.NetUtil;
import io.netty.util.concurrent.Future; 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.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -71,6 +48,29 @@ import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; 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 * 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("foo", "goo2");
httpHeaders.add("foo2", "goo2"); httpHeaders.add("foo2", "goo2");
final Http2Headers http2Headers = final Http2Headers http2Headers =
new DefaultHttp2Headers().method(as("GET")).path(as("/example")) new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/example"))
.authority(as("www.example.org:5555")).scheme(as("http")) .authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http"))
.add(as("foo"), as("goo")).add(as("foo"), as("goo2")) .add(new AsciiString("foo"), new AsciiString("goo"))
.add(as("foo2"), as("goo2")); .add(new AsciiString("foo"), new AsciiString("goo2"))
.add(new AsciiString("foo2"), new AsciiString("goo2"));
ChannelPromise writePromise = newPromise(); ChannelPromise writePromise = newPromise();
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise); ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise);
@ -161,10 +162,11 @@ public class HttpToHttp2ConnectionHandlerTest {
httpHeaders.add("foo", "goo2"); httpHeaders.add("foo", "goo2");
httpHeaders.add("foo2", "goo2"); httpHeaders.add("foo2", "goo2");
final Http2Headers http2Headers = final Http2Headers http2Headers =
new DefaultHttp2Headers().method(as("POST")).path(as("/example")) new DefaultHttp2Headers().method(new AsciiString("POST")).path(new AsciiString("/example"))
.authority(as("www.example.org:5555")).scheme(as("http")) .authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http"))
.add(as("foo"), as("goo")).add(as("foo"), as("goo2")) .add(new AsciiString("foo"), new AsciiString("goo"))
.add(as("foo2"), as("goo2")); .add(new AsciiString("foo"), new AsciiString("goo2"))
.add(new AsciiString("foo2"), new AsciiString("goo2"));
ChannelPromise writePromise = newPromise(); ChannelPromise writePromise = newPromise();
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise); ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise);
@ -202,14 +204,16 @@ public class HttpToHttp2ConnectionHandlerTest {
httpHeaders.add("foo", "goo2"); httpHeaders.add("foo", "goo2");
httpHeaders.add("foo2", "goo2"); httpHeaders.add("foo2", "goo2");
final Http2Headers http2Headers = final Http2Headers http2Headers =
new DefaultHttp2Headers().method(as("POST")).path(as("/example")) new DefaultHttp2Headers().method(new AsciiString("POST")).path(new AsciiString("/example"))
.authority(as("www.example.org:5555")).scheme(as("http")) .authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http"))
.add(as("foo"), as("goo")).add(as("foo"), as("goo2")) .add(new AsciiString("foo"), new AsciiString("goo"))
.add(as("foo2"), as("goo2")); .add(new AsciiString("foo"), new AsciiString("goo2"))
.add(new AsciiString("foo2"), new AsciiString("goo2"));
request.trailingHeaders().add("trailing", "bar"); 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(); ChannelPromise writePromise = newPromise();
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise); ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise);
@ -251,17 +255,19 @@ public class HttpToHttp2ConnectionHandlerTest {
httpHeaders.add("foo", "goo2"); httpHeaders.add("foo", "goo2");
httpHeaders.add("foo2", "goo2"); httpHeaders.add("foo2", "goo2");
final Http2Headers http2Headers = final Http2Headers http2Headers =
new DefaultHttp2Headers().method(as("POST")).path(as("/example")) new DefaultHttp2Headers().method(new AsciiString("POST")).path(new AsciiString("/example"))
.authority(as("www.example.org:5555")).scheme(as("http")) .authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http"))
.add(as("foo"), as("goo")).add(as("foo"), as("goo2")) .add(new AsciiString("foo"), new AsciiString("goo"))
.add(as("foo2"), as("goo2")); .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 DefaultHttpContent httpContent = new DefaultHttpContent(Unpooled.copiedBuffer(text, UTF_8));
final LastHttpContent lastHttpContent = new DefaultLastHttpContent(Unpooled.copiedBuffer(text2, UTF_8)); final LastHttpContent lastHttpContent = new DefaultLastHttpContent(Unpooled.copiedBuffer(text2, UTF_8));
lastHttpContent.trailingHeaders().add("trailing", "bar"); 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(); ChannelPromise writePromise = newPromise();
ChannelFuture writeFuture = clientChannel.write(request, writePromise); 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.Http2CodecUtil.getEmbeddedHttp2Exception;
import static io.netty.handler.codec.http2.Http2Exception.isStreamError; 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 io.netty.handler.codec.http2.Http2TestUtil.runInChannel;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
@ -210,8 +209,9 @@ public class InboundHttp2ToHttpAdapterTest {
httpHeaders.set(HttpUtil.ExtensionHeaderNames.AUTHORITY.text(), "example.org"); httpHeaders.set(HttpUtil.ExtensionHeaderNames.AUTHORITY.text(), "example.org");
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0); httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0);
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")).scheme(as("https")) final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).
.authority(as("example.org")).path(as("/some/path/resource2")); scheme(new AsciiString("https")).authority(new AsciiString("example.org"))
.path(new AsciiString("/some/path/resource2"));
runInChannel(clientChannel, new Http2Runnable() { runInChannel(clientChannel, new Http2Runnable() {
@Override @Override
public void run() { public void run() {
@ -232,10 +232,10 @@ public class InboundHttp2ToHttpAdapterTest {
@Test @Test
public void clientRequestSingleHeaderNonAsciiShouldThrow() throws Exception { public void clientRequestSingleHeaderNonAsciiShouldThrow() throws Exception {
final Http2Headers http2Headers = new DefaultHttp2Headers() final Http2Headers http2Headers = new DefaultHttp2Headers()
.method(as("GET")) .method(new AsciiString("GET"))
.scheme(as("https")) .scheme(new AsciiString("https"))
.authority(as("example.org")) .authority(new AsciiString("example.org"))
.path(as("/some/path/resource2")) .path(new AsciiString("/some/path/resource2"))
.add(new AsciiString("çã".getBytes(CharsetUtil.UTF_8)), .add(new AsciiString("çã".getBytes(CharsetUtil.UTF_8)),
new AsciiString("Ãã".getBytes(CharsetUtil.UTF_8))); new AsciiString("Ãã".getBytes(CharsetUtil.UTF_8)));
runInChannel(clientChannel, new Http2Runnable() { runInChannel(clientChannel, new Http2Runnable() {
@ -259,8 +259,8 @@ public class InboundHttp2ToHttpAdapterTest {
HttpHeaders httpHeaders = request.headers(); HttpHeaders httpHeaders = request.headers();
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length()); httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length());
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")).path( final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
as("/some/path/resource2")); new AsciiString("/some/path/resource2"));
runInChannel(clientChannel, new Http2Runnable() { runInChannel(clientChannel, new Http2Runnable() {
@Override @Override
public void run() { public void run() {
@ -289,8 +289,8 @@ public class InboundHttp2ToHttpAdapterTest {
HttpHeaders httpHeaders = request.headers(); HttpHeaders httpHeaders = request.headers();
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length()); httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length());
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")).path( final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
as("/some/path/resource2")); new AsciiString("/some/path/resource2"));
final int midPoint = text.length() / 2; final int midPoint = text.length() / 2;
runInChannel(clientChannel, new Http2Runnable() { runInChannel(clientChannel, new Http2Runnable() {
@Override @Override
@ -323,8 +323,8 @@ public class InboundHttp2ToHttpAdapterTest {
HttpHeaders httpHeaders = request.headers(); HttpHeaders httpHeaders = request.headers();
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length()); httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length());
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")).path( final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
as("/some/path/resource2")); new AsciiString("/some/path/resource2"));
runInChannel(clientChannel, new Http2Runnable() { runInChannel(clientChannel, new Http2Runnable() {
@Override @Override
public void run() { public void run() {
@ -361,10 +361,12 @@ public class InboundHttp2ToHttpAdapterTest {
trailingHeaders.set("FoO", "goo"); trailingHeaders.set("FoO", "goo");
trailingHeaders.set("foO2", "goo2"); trailingHeaders.set("foO2", "goo2");
trailingHeaders.add("fOo2", "goo3"); trailingHeaders.add("fOo2", "goo3");
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")).path( final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
as("/some/path/resource2")); new AsciiString("/some/path/resource2"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers().set(as("foo"), as("goo")) final Http2Headers http2Headers2 = new DefaultHttp2Headers()
.set(as("foo2"), as("goo2")).add(as("foo2"), as("goo3")); .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() { runInChannel(clientChannel, new Http2Runnable() {
@Override @Override
public void run() { public void run() {
@ -398,10 +400,12 @@ public class InboundHttp2ToHttpAdapterTest {
trailingHeaders.set("Foo", "goo"); trailingHeaders.set("Foo", "goo");
trailingHeaders.set("fOo2", "goo2"); trailingHeaders.set("fOo2", "goo2");
trailingHeaders.add("foO2", "goo3"); trailingHeaders.add("foO2", "goo3");
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")).path( final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
as("/some/path/resource2")); new AsciiString("/some/path/resource2"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers().set(as("foo"), as("goo")) final Http2Headers http2Headers2 = new DefaultHttp2Headers()
.set(as("foo2"), as("goo2")).add(as("foo2"), as("goo3")); .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() { runInChannel(clientChannel, new Http2Runnable() {
@Override @Override
public void run() { public void run() {
@ -441,10 +445,10 @@ public class InboundHttp2ToHttpAdapterTest {
httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3); httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3);
httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), 123); httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), 123);
httpHeaders2.setInt(HttpHeaderNames.CONTENT_LENGTH, text2.length()); httpHeaders2.setInt(HttpHeaderNames.CONTENT_LENGTH, text2.length());
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("PUT")).path( final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("PUT")).path(
as("/some/path/resource")); new AsciiString("/some/path/resource"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(as("PUT")).path( final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(new AsciiString("PUT")).path(
as("/some/path/resource2")); new AsciiString("/some/path/resource2"));
runInChannel(clientChannel, new Http2Runnable() { runInChannel(clientChannel, new Http2Runnable() {
@Override @Override
public void run() { public void run() {
@ -488,10 +492,10 @@ public class InboundHttp2ToHttpAdapterTest {
HttpHeaders httpHeaders2 = request2.headers(); HttpHeaders httpHeaders2 = request2.headers();
httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
httpHeaders2.setInt(HttpHeaderNames.CONTENT_LENGTH, text2.length()); httpHeaders2.setInt(HttpHeaderNames.CONTENT_LENGTH, text2.length());
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("PUT")).path( final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("PUT")).path(
as("/some/path/resource")); new AsciiString("/some/path/resource"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(as("PUT")).path( final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(new AsciiString("PUT")).path(
as("/some/path/resource2")); new AsciiString("/some/path/resource2"));
HttpHeaders httpHeaders3 = request3.headers(); HttpHeaders httpHeaders3 = request3.headers();
httpHeaders3.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); httpHeaders3.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
httpHeaders3.setInt(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3); httpHeaders3.setInt(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3);
@ -549,7 +553,8 @@ public class InboundHttp2ToHttpAdapterTest {
httpHeaders = request.headers(); httpHeaders = request.headers();
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0); 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() { runInChannel(clientChannel, new Http2Runnable() {
@Override @Override
public void run() { public void run() {
@ -563,9 +568,10 @@ public class InboundHttp2ToHttpAdapterTest {
capturedRequests = requestCaptor.getAllValues(); capturedRequests = requestCaptor.getAllValues();
assertEquals(request, capturedRequests.get(0)); assertEquals(request, capturedRequests.get(0));
final Http2Headers http2Headers = new DefaultHttp2Headers().status(as("200")); final Http2Headers http2Headers = new DefaultHttp2Headers().status(new AsciiString("200"));
final Http2Headers http2Headers2 = new DefaultHttp2Headers().status(as("201")).scheme(as("https")) final Http2Headers http2Headers2 = new DefaultHttp2Headers().status(new AsciiString("201"))
.authority(as("example.org")); .scheme(new AsciiString("https"))
.authority(new AsciiString("example.org"));
runInChannel(serverConnectedChannel, new Http2Runnable() { runInChannel(serverConnectedChannel, new Http2Runnable() {
@Override @Override
public void run() { public void run() {
@ -597,8 +603,10 @@ public class InboundHttp2ToHttpAdapterTest {
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.set(HttpHeaderNames.EXPECT, HttpHeaderValues.CONTINUE); httpHeaders.set(HttpHeaderNames.EXPECT, HttpHeaderValues.CONTINUE);
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0); httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0);
final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("PUT")).path(as("/info/test")) final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("PUT"))
.set(as(HttpHeaderNames.EXPECT.toString()), as(HttpHeaderValues.CONTINUE.toString())); .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 FullHttpMessage response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
final String text = "a big payload"; final String text = "a big payload";
final ByteBuf payload = Unpooled.copiedBuffer(text.getBytes()); final ByteBuf payload = Unpooled.copiedBuffer(text.getBytes());
@ -624,7 +632,7 @@ public class InboundHttp2ToHttpAdapterTest {
httpHeaders = response.headers(); httpHeaders = response.headers();
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0); 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() { runInChannel(serverConnectedChannel, new Http2Runnable() {
@Override @Override
public void run() { public void run() {
@ -661,7 +669,7 @@ public class InboundHttp2ToHttpAdapterTest {
httpHeaders = response2.headers(); httpHeaders = response2.headers();
httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3);
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0); 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() { runInChannel(serverConnectedChannel, new Http2Runnable() {
@Override @Override
public void run() { public void run() {

View File

@ -16,15 +16,21 @@
package io.netty.handler.codec.stomp; package io.netty.handler.codec.stomp;
import io.netty.handler.codec.DefaultTextHeaders; import io.netty.handler.codec.CharSequenceValueConverter;
import io.netty.handler.codec.TextHeaders; 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() { public DefaultStompHeaders() {
super(new TreeMap<CharSequence, Object>(), NO_NAME_VALIDATOR, CharSequenceConverter.INSTANCE, false); super(CharSequenceValueConverter.INSTANCE);
} }
@Override @Override
@ -118,7 +124,7 @@ public class DefaultStompHeaders extends DefaultTextHeaders implements StompHead
} }
@Override @Override
public StompHeaders add(TextHeaders headers) { public StompHeaders add(Headers<? extends CharSequence> headers) {
super.add(headers); super.add(headers);
return this; return this;
} }
@ -214,13 +220,13 @@ public class DefaultStompHeaders extends DefaultTextHeaders implements StompHead
} }
@Override @Override
public StompHeaders set(TextHeaders headers) { public StompHeaders set(Headers<? extends CharSequence> headers) {
super.set(headers); super.set(headers);
return this; return this;
} }
@Override @Override
public StompHeaders setAll(TextHeaders headers) { public StompHeaders setAll(Headers<? extends CharSequence> headers) {
super.setAll(headers); super.setAll(headers);
return this; return this;
} }
@ -230,4 +236,30 @@ public class DefaultStompHeaders extends DefaultTextHeaders implements StompHead
super.clear(); super.clear();
return this; 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; package io.netty.handler.codec.stomp;
import io.netty.handler.codec.TextHeaders; import io.netty.handler.codec.Headers;
import io.netty.util.AsciiString; 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 * The multimap data structure for the STOMP header names and values. It also provides the constants for the standard
* STOMP header names and values. * STOMP header names and values.
*/ */
public interface StompHeaders extends TextHeaders { public interface StompHeaders extends Headers<CharSequence> {
AsciiString ACCEPT_VERSION = new AsciiString("accept-version"); AsciiString ACCEPT_VERSION = new AsciiString("accept-version");
AsciiString HOST = new AsciiString("host"); AsciiString HOST = new AsciiString("host");
@ -90,7 +94,7 @@ public interface StompHeaders extends TextHeaders {
StompHeaders addTimeMillis(CharSequence name, long value); StompHeaders addTimeMillis(CharSequence name, long value);
@Override @Override
StompHeaders add(TextHeaders headers); StompHeaders add(Headers<? extends CharSequence> headers);
@Override @Override
StompHeaders set(CharSequence name, CharSequence value); StompHeaders set(CharSequence name, CharSequence value);
@ -138,11 +142,41 @@ public interface StompHeaders extends TextHeaders {
StompHeaders setTimeMillis(CharSequence name, long value); StompHeaders setTimeMillis(CharSequence name, long value);
@Override @Override
StompHeaders set(TextHeaders headers); StompHeaders set(Headers<? extends CharSequence> headers);
@Override @Override
StompHeaders setAll(TextHeaders headers); StompHeaders setAll(Headers<? extends CharSequence> headers);
@Override @Override
StompHeaders clear(); 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; package io.netty.handler.codec.stomp;
import java.util.List;
import java.util.Map.Entry;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.AsciiHeadersEncoder; 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.AsciiHeadersEncoder.SeparatorType;
import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.util.CharsetUtil; 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}. * 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 final class StompTestConstants {
public static final String CONNECT_FRAME = public static final String CONNECT_FRAME =
"CONNECT\n" + "CONNECT\n" +
"accept-version:1.1,1.2\n" +
"host:stomp.github.org\n" + "host:stomp.github.org\n" +
"accept-version:1.1,1.2\n" +
'\n' + '\n' +
'\0'; '\0';
public static final String CONNECTED_FRAME = 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(false);
}
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;
}
}
protected 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; package io.netty.handler.codec;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -292,11 +291,6 @@ public class EmptyHeaders<T> implements Headers<T> {
return false; return false;
} }
@Override
public boolean contains(T name, T value, Comparator<? super T> valueComparator) {
return false;
}
@Override @Override
public int size() { public int size() {
return 0; 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; package io.netty.handler.codec;
import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
public interface Headers<T> extends Iterable<Entry<T, T>> { 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, * 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. * 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 * 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. * 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. * 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 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 * @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}. * 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 * 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. * 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. * 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 name the name of the header to search
* @param defaultValue the default value * @param defaultValue the default value
* @return the {@code boolean} value of the first value in insertion order or {@code defaultValue} if there is no * @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 * 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. * 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. * 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 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 * @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}. * 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 * 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. * 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. * 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 name the name of the header to search
* @param defaultValue the default value * @param defaultValue the default value
* @return the {@code byte} value of the first value in insertion order or {@code defaultValue} if there is no * @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 * 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. * 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. * 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 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 * @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}. * 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 * 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. * 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. * 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 name the name of the header to search
* @param defaultValue the default value * @param defaultValue the default value
* @return the {@code char} value of the first value in insertion order or {@code defaultValue} if there is no * @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 * 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. * 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. * 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 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 * @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}. * 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 * 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. * 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. * 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 name the name of the header to search
* @param defaultValue the default value * @param defaultValue the default value
* @return the {@code short} value of the first value in insertion order or {@code defaultValue} if there is no * @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 * 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. * 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. * 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 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 * @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}. * 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 * 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. * 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. * 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 name the name of the header to search
* @param defaultValue the default value * @param defaultValue the default value
* @return the {@code int} value of the first value in insertion order or {@code defaultValue} if there is no * @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 * 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. * 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. * 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 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 * @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}. * 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 * 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. * 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. * 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 name the name of the header to search
* @param defaultValue the default value * @param defaultValue the default value
* @return the {@code long} value of the first value in insertion order or {@code defaultValue} if there is no * @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 * 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. * 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. * 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 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 * @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}. * 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 * 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. * 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. * 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 name the name of the header to search
* @param defaultValue the default value * @param defaultValue the default value
* @return the {@code float} value of the first value in insertion order or {@code defaultValue} if there is no * @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 * 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. * 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. * 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 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 * @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}. * 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 * 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. * 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. * 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 name the name of the header to search
* @param defaultValue the default value * @param defaultValue the default value
* @return the {@code double} value of the first value in insertion order or {@code defaultValue} if there is no * @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 * 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 * 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. * 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 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 * @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. * 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 * 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 * 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. * 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 name the name of the header to retrieve
* @param defaultValue the default value * @param defaultValue the default value
* @return the milliseconds value of the first value in insertion order or {@code defaultValue} if there is no such * @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}. * The {@link Object#equals(Object)} method is used to test for equality of {@code value}.
* </p> * </p>
* @param name the header name * @param name the header name
* @param value the header value of the header to find
*/ */
boolean contains(T name, T value); 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); 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. * 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); 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 * Adds a new header. Before the {@code value} is added, it's converted to type {@code T}.
* {@link ValueConverter#convertObject(java.lang.Object)}.
* *
* @param name the header name * @param name the header name
* @param value the value of the header * @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 * 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 name the header name
* @param value the value of the header * @param value the value of the header

View File

@ -0,0 +1,131 @@
/*
* 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.Iterator;
import java.util.List;
import java.util.Map.Entry;
/**
* Provides utility methods related to {@link Headers}.
*/
public final class HeadersUtils {
private HeadersUtils() {
}
/**
* {@link #getAll(CharSequence)} 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(CharSequence)} 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 #iterator()} that 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 StringIterator(headers.iterator());
}
private static final class StringIterator implements Iterator<Entry<String, String>> {
private final Iterator<Entry<CharSequence, CharSequence>> iter;
public StringIterator(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();
}
}
}

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

View File

@ -1,358 +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 org.junit.Assert.assertEquals;
import static io.netty.util.internal.StringUtil.COMMA;
import static io.netty.util.internal.StringUtil.DOUBLE_QUOTE;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
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; 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.ByteProcessor.IndexOfProcessor;
import io.netty.util.internal.EmptyArrays; import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
@ -24,11 +22,12 @@ import io.netty.util.internal.PlatformDependent;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException; 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 * 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 * 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; private static final char MAX_CHAR_VALUE = 255;
public static final AsciiString EMPTY_STRING = new AsciiString(""); 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 @Override
public int compare(AsciiString o1, AsciiString o2) { public int hashCode(CharSequence o) {
return CHARSEQUENCE_CASE_INSENSITIVE_ORDER.compare(o1, o2); 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 @Override
public int compare(AsciiString o1, AsciiString o2) { public int hashCode(CharSequence o) {
return CHARSEQUENCE_CASE_SENSITIVE_ORDER.compare(o1, o2); return AsciiString.hashCode(o);
} }
};
public static final Comparator<CharSequence> CHARSEQUENCE_CASE_INSENSITIVE_ORDER = new Comparator<CharSequence>() {
@Override @Override
public int compare(CharSequence o1, CharSequence o2) { public boolean equals(CharSequence a, CharSequence b) {
int len1 = o1.length(); return AsciiString.contentEquals(a, b);
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;
} }
}; };
@ -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 String string;
private int caseInsensitiveHash;
/** /**
* Returns an {@link AsciiString} containing the given character sequence. If the given string is already a * 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 @Override
public char charAt(int index) { public char charAt(int index) {
return b2c(byteAt(index)); return (char) (byteAt(index) & 0xFF);
} }
@Override @Override
public void arrayChanged() { public void arrayChanged() {
string = null; string = null;
caseInsensitiveHash = 0;
super.arrayChanged(); 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 @Override
public String toString(Charset charset, int start, int end) { public String toString(Charset charset, int start, int end) {
if (start == 0 && end == length()) { if (start == 0 && end == length()) {
@ -403,7 +189,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
int length2 = string.length(); int length2 = string.length();
int minLength = Math.min(length1, length2); int minLength = Math.min(length1, length2);
for (int i = 0, j = arrayOffset(); i < minLength; i++, j++) { 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) { if (result != 0) {
return result; return result;
} }
@ -412,24 +198,6 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return length1 - length2; 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. * 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. * @param string the string to compare.
* @return {@code true} if the specified string is equal to this string, {@code false} otherwise. * @return {@code true} if the specified string is equal to this string, {@code false} otherwise.
*/ */
public boolean equalsIgnoreCase(CharSequence string) { public boolean contentEqualsIgnoreCase(CharSequence string) {
if (string == this) { 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; return true;
} }
if (string == null) { for (int i = arrayOffset(), j = 0; i < length(); ++i, ++j) {
return false; char c1 = (char) (value[i] & 0xFF);
} char c2 = string.charAt(j);
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);
if (c1 != c2 && toLowerCase(c1) != toLowerCase(c2)) { if (c1 != c2 && toLowerCase(c1) != toLowerCase(c2)) {
return false; return false;
} }
@ -539,7 +309,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
final char[] buffer = new char[length]; final char[] buffer = new char[length];
for (int i = 0, j = start + arrayOffset(); i < length; i++, j++) { for (int i = 0, j = start + arrayOffset(); i < length; i++, j++) {
buffer[i] = b2c(value[j]); buffer[i] = (char) (value[j] & 0xFF);
} }
return buffer; return buffer;
} }
@ -564,7 +334,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
final int dstEnd = dstIdx + length; final int dstEnd = dstIdx + length;
for (int i = dstIdx, j = srcIdx + arrayOffset(); i < dstEnd; i++, j++) { 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 return -1; // handles subCount > count || start >= count
} }
int o1 = i, o2 = 0; 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 // Intentionally empty
} }
if (o2 == subCount) { if (o2 == subCount) {
@ -698,7 +468,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return -1; return -1;
} }
int o1 = i, o2 = 0; 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 // Intentionally empty
} }
if (o2 == subCount) { if (o2 == subCount) {
@ -743,7 +513,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
final int thatEnd = start + length; final int thatEnd = start + length;
for (int i = start, j = thisStart + arrayOffset(); i < thatEnd; i++, j++) { 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; return false;
} }
} }
@ -782,7 +552,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
thisStart += arrayOffset(); thisStart += arrayOffset();
final int thisEnd = thisStart + length; final int thisEnd = thisStart + length;
while (thisStart < thisEnd) { while (thisStart < thisEnd) {
char c1 = b2c(value[thisStart++]); char c1 = (char) (value[thisStart++] & 0xFF);
char c2 = string.charAt(start++); char c2 = string.charAt(start++);
if (c1 != c2 && toLowerCase(c1) != toLowerCase(c2)) { if (c1 != c2 && toLowerCase(c1) != toLowerCase(c2)) {
return false; 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. * 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} * @return {@code true} if equal, otherwise {@code false}
*/ */
public boolean contentEquals(CharSequence cs) { public boolean contentEquals(CharSequence a) {
if (cs == null) { if (this == a) {
throw new NullPointerException(); return true;
}
if (a instanceof AsciiString) {
return equals(a);
} }
int length1 = length(); if (a.length() != length()) {
int length2 = cs.length();
if (length1 != length2) {
return false; return false;
} else if (length1 == 0) {
return true; // since both are empty strings
} }
for (int i = arrayOffset(), j = 0; j < a.length(); ++i, ++j) {
return regionMatches(0, cs, 0, length2); 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)); return toAsciiStringArray(Pattern.compile(expr).split(this, max));
} }
private static AsciiString[] toAsciiStringArray(String[] jdkResult) { private static byte c2b(char c) {
AsciiString[] res = new AsciiString[jdkResult.length]; if (c > MAX_CHAR_VALUE) {
for (int i = 0; i < jdkResult.length; i++) { return '?';
res[i] = new AsciiString(jdkResult[i]);
} }
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()]); 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. * 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; 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 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.ByteBuffer;
import java.nio.CharBuffer; import java.nio.CharBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder; import java.nio.charset.CharsetEncoder;
import java.util.Arrays; 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 * 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. * this object is immutable.
*/ */
public class ByteString { 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 * Allows sub classes to take advantage of {@link ByteString} operations which need to generate new
* ByteString objects. * ByteString objects.
@ -77,7 +51,7 @@ public class ByteString {
}; };
public static final ByteString EMPTY_STRING = new ByteString(0); 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()}. * If this value is modified outside the constructor then call {@link #arrayChanged()}.
@ -127,16 +101,15 @@ public class ByteString {
* will be shared. * will be shared.
*/ */
public ByteString(byte[] value, int start, int length, boolean copy) { 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) { if (copy) {
this.value = Arrays.copyOfRange(value, start, start + length); this.value = Arrays.copyOfRange(value, start, start + length);
offset = 0; this.offset = 0;
this.length = length; this.length = length;
} else { } 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.value = value;
this.offset = start; this.offset = start;
this.length = length; this.length = length;
@ -268,6 +241,14 @@ public class ByteString {
this.length = this.value.length; 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. * 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}. * The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
*/ */
public final int forEachByte(ByteProcessor visitor) throws Exception { 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() + ')'); + ") <= " + "length(" + length() + ')');
} }
return forEachByte0(index, length, visitor);
}
private int forEachByte0(int index, int length, ByteProcessor visitor) throws Exception {
final int len = offset + length; final int len = offset + length;
for (int i = offset + index; i < len; ++i) { for (int i = offset + index; i < len; ++i) {
if (!visitor.process(value[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}. * The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
*/ */
public final int forEachByteDesc(ByteProcessor visitor) throws Exception { 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() + ')'); + ") <= " + "length(" + length() + ')');
} }
return forEachByteDesc0(index, length, visitor);
}
private int forEachByteDesc0(int index, int length, ByteProcessor visitor) throws Exception {
final int end = offset + index; final int end = offset + index;
for (int i = offset + index + length - 1; i >= end; --i) { for (int i = offset + index + length - 1; i >= end; --i) {
if (!visitor.process(value[i])) { if (!visitor.process(value[i])) {
@ -423,7 +412,7 @@ public class ByteString {
if (h == 0) { if (h == 0) {
final int end = offset + length; final int end = offset + length;
for (int i = offset; i < end; ++i) { 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; 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 CARRIAGE_RETURN = '\r';
public static final char TAB = '\t'; 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_PAD = new String[256];
private static final String[] BYTE2HEX_NOPAD = new String[256]; private static final String[] BYTE2HEX_NOPAD = new String[256];

View File

@ -15,14 +15,18 @@
*/ */
package io.netty.util; 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.Assert;
import org.junit.Test; 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 * Test for the {@link AsciiString} class
*/ */
@ -87,6 +91,8 @@ public class AsciiStringTest {
final int end = init.length; final int end = init.length;
AsciiString sub1 = ascii.subSequence(start, end, false); AsciiString sub1 = ascii.subSequence(start, end, false);
AsciiString sub2 = ascii.subSequence(start, end, true); AsciiString sub2 = ascii.subSequence(start, end, true);
assertEquals(sub1.hashCode(), sub2.hashCode());
assertEquals(sub1.hashCodeCaseInsensitive(), sub2.hashCode());
assertEquals(sub1, sub2); assertEquals(sub1, sub2);
for (int i = start; i < end; ++i) { for (int i = start; i < end; ++i) {
assertEquals(init[i], sub1.byteAt(i - start)); assertEquals(init[i], sub1.byteAt(i - start));
@ -94,9 +100,77 @@ public class AsciiStringTest {
} }
@Test @Test
public void caseInsensativeHasher() { public void testCaseSensitivity() {
String s1 = new String("TransfeR-EncodinG"); Random r = new Random();
AsciiString s2 = new AsciiString("transfer-encoding"); int i = 0;
assertEquals(AsciiString.caseInsensitiveHashCode(s1), AsciiString.caseInsensitiveHashCode(s2)); 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.assertEquals;
import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import java.util.Random; import java.util.Random;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@ -36,8 +35,6 @@ public class ByteStringTest {
private int length = 100; private int length = 100;
private ByteString aByteString; private ByteString aByteString;
private ByteString bByteString; private ByteString bByteString;
private ByteString greaterThanAByteString;
private ByteString lessThanAByteString;
private Random r = new Random(); private Random r = new Random();
@Before @Before
@ -52,66 +49,6 @@ public class ByteStringTest {
System.arraycopy(a, aOffset, b, bOffset, length); System.arraycopy(a, aOffset, b, bOffset, length);
aByteString = new ByteString(a, aOffset, length, false); aByteString = new ByteString(a, aOffset, length, false);
bByteString = new ByteString(b, bOffset, 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 @Test

View File

@ -150,7 +150,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
} }
// Cache Validation // Cache Validation
String ifModifiedSince = request.headers().getAndConvert(IF_MODIFIED_SINCE); String ifModifiedSince = request.headers().getAsString(IF_MODIFIED_SINCE);
if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) { if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) {
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US); SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince); Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);

View File

@ -163,7 +163,7 @@ public class HttpSnoopServerHandler extends SimpleChannelInboundHandler<Object>
} }
// Encode the cookie. // Encode the cookie.
String cookieString = request.headers().getAndConvert(COOKIE); String cookieString = request.headers().getAsString(COOKIE);
if (cookieString != null) { if (cookieString != null) {
Set<Cookie> cookies = ServerCookieDecoder.STRICT.decode(cookieString); Set<Cookie> cookies = ServerCookieDecoder.STRICT.decode(cookieString);
if (!cookies.isEmpty()) { if (!cookies.isEmpty()) {

View File

@ -195,7 +195,7 @@ public final class HttpUploadClient {
// convert headers to list // convert headers to list
List<Entry<String, String>> entries = new ArrayList<Entry<String, String>>(headers.size()); List<Entry<String, String>> entries = new ArrayList<Entry<String, String>>(headers.size());
Iterator<Entry<String, String>> iterConverted = headers.iteratorConverted(); Iterator<Entry<String, String>> iterConverted = headers.iteratorAsString();
while (iterConverted.hasNext()) { while (iterConverted.hasNext()) {
entries.add(iterConverted.next()); entries.add(iterConverted.next());
} }
@ -270,7 +270,7 @@ public final class HttpUploadClient {
*/ */
private static void formpostmultipart( private static void formpostmultipart(
Bootstrap bootstrap, String host, int port, URI uriFile, HttpDataFactory factory, 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 // XXX /formpostmultipart
// Start the connection attempt. // Start the connection attempt.
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));

View File

@ -123,7 +123,7 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
// new getMethod // new getMethod
Set<Cookie> cookies; Set<Cookie> cookies;
String value = request.headers().getAndConvert(HttpHeaderNames.COOKIE); String value = request.headers().getAsString(HttpHeaderNames.COOKIE);
if (value == null) { if (value == null) {
cookies = Collections.emptySet(); cookies = Collections.emptySet();
} else { } else {
@ -335,7 +335,7 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
} }
Set<Cookie> cookies; Set<Cookie> cookies;
String value = request.headers().getAndConvert(HttpHeaderNames.COOKIE); String value = request.headers().getAsString(HttpHeaderNames.COOKIE);
if (value == null) { if (value == null) {
cookies = Collections.emptySet(); cookies = Collections.emptySet();
} else { } else {

View File

@ -19,6 +19,7 @@ package io.netty.example.http2.helloworld.server;
import io.netty.bootstrap.ServerBootstrap; import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelOption; import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel;

View File

@ -107,7 +107,7 @@ public class Http2RequestHandler extends SimpleChannelInboundHandler<FullHttpReq
} }
private String streamId(FullHttpRequest request) { private String streamId(FullHttpRequest request) {
return request.headers().getAndConvert(HttpUtil.ExtensionHeaderNames.STREAM_ID.text()); return request.headers().getAsString(HttpUtil.ExtensionHeaderNames.STREAM_ID.text());
} }
private void streamId(FullHttpResponse response, String streamId) { private void streamId(FullHttpResponse response, String streamId) {

View File

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

Some files were not shown because too many files have changed in this diff Show More