HTTP/2 Headers Type Updates

Motivation:
The HTTP/2 RFC (https://tools.ietf.org/html/rfc7540#section-8.1.2) indicates that header names consist of ASCII characters. We currently use ByteString to represent HTTP/2 header names. The HTTP/2 RFC (https://tools.ietf.org/html/rfc7540#section-10.3) also eludes to header values inheriting the same validity characteristics as HTTP/1.x. Using AsciiString for the value type of HTTP/2 headers would allow for re-use of predefined HTTP/1.x values, and make comparisons more intuitive. The Headers<T> interface could also be expanded to allow for easier use of header types which do not have the same Key and Value type.

Motivation:
- Change Headers<T> to Headers<K, V>
- Change Http2Headers<ByteString> to Http2Headers<CharSequence, CharSequence>
- Remove ByteString. Having AsciiString extend ByteString complicates equality comparisons when the hash code algorithm is no longer shared.

Result:
Http2Header types are more representative of the HTTP/2 RFC, and relationship between HTTP/2 header name/values more directly relates to HTTP/1.x header names/values.
This commit is contained in:
Scott Mitchell 2015-10-27 13:47:47 -07:00
parent 07d861c5ff
commit 19658e9cd8
44 changed files with 1902 additions and 3088 deletions

View File

@ -33,6 +33,7 @@ import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel; import java.nio.channels.ScatteringByteChannel;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import static io.netty.util.internal.MathUtil.isOutOfBounds;
/** /**
* A skeletal implementation of a buffer. * A skeletal implementation of a buffer.
@ -1110,7 +1111,7 @@ public abstract class AbstractByteBuf extends ByteBuf {
} }
final void checkIndex0(int index, int fieldLength) { final void checkIndex0(int index, int fieldLength) {
if (isInvalid(index, fieldLength, capacity())) { if (isOutOfBounds(index, fieldLength, capacity())) {
throw new IndexOutOfBoundsException(String.format( throw new IndexOutOfBoundsException(String.format(
"index: %d, length: %d (expected: range(0, %d))", index, fieldLength, capacity())); "index: %d, length: %d (expected: range(0, %d))", index, fieldLength, capacity()));
} }
@ -1118,7 +1119,7 @@ public abstract class AbstractByteBuf extends ByteBuf {
protected final void checkSrcIndex(int index, int length, int srcIndex, int srcCapacity) { protected final void checkSrcIndex(int index, int length, int srcIndex, int srcCapacity) {
checkIndex(index, length); checkIndex(index, length);
if (isInvalid(srcIndex, length, srcCapacity)) { if (isOutOfBounds(srcIndex, length, srcCapacity)) {
throw new IndexOutOfBoundsException(String.format( throw new IndexOutOfBoundsException(String.format(
"srcIndex: %d, length: %d (expected: range(0, %d))", srcIndex, length, srcCapacity)); "srcIndex: %d, length: %d (expected: range(0, %d))", srcIndex, length, srcCapacity));
} }
@ -1126,16 +1127,12 @@ public abstract class AbstractByteBuf extends ByteBuf {
protected final void checkDstIndex(int index, int length, int dstIndex, int dstCapacity) { protected final void checkDstIndex(int index, int length, int dstIndex, int dstCapacity) {
checkIndex(index, length); checkIndex(index, length);
if (isInvalid(dstIndex, length, dstCapacity)) { if (isOutOfBounds(dstIndex, length, dstCapacity)) {
throw new IndexOutOfBoundsException(String.format( throw new IndexOutOfBoundsException(String.format(
"dstIndex: %d, length: %d (expected: range(0, %d))", dstIndex, length, dstCapacity)); "dstIndex: %d, length: %d (expected: range(0, %d))", dstIndex, length, dstCapacity));
} }
} }
static boolean isInvalid(int index, int length, int capacity) {
return (index | length | (index + length) | (capacity - (index + length))) < 0;
}
/** /**
* Throws an {@link IndexOutOfBoundsException} if the current * Throws an {@link IndexOutOfBoundsException} if the current
* {@linkplain #readableBytes() readable bytes} of this buffer is less * {@linkplain #readableBytes() readable bytes} of this buffer is less

View File

@ -19,6 +19,8 @@ import io.netty.util.internal.PlatformDependent;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import static io.netty.util.internal.PlatformDependent.BIG_ENDIAN_NATIVE_ORDER;
/** /**
* Special {@link SwappedByteBuf} for {@link ByteBuf}s that is using unsafe. * Special {@link SwappedByteBuf} for {@link ByteBuf}s that is using unsafe.
*/ */
@ -30,7 +32,7 @@ abstract class AbstractUnsafeSwappedByteBuf extends SwappedByteBuf {
super(buf); super(buf);
assert PlatformDependent.isUnaligned(); assert PlatformDependent.isUnaligned();
wrapped = buf; wrapped = buf;
nativeByteOrder = UnsafeByteBufUtil.BIG_ENDIAN_NATIVE_ORDER == (order() == ByteOrder.BIG_ENDIAN); nativeByteOrder = BIG_ENDIAN_NATIVE_ORDER == (order() == ByteOrder.BIG_ENDIAN);
} }
@Override @Override

View File

@ -15,11 +15,8 @@
*/ */
package io.netty.buffer; package io.netty.buffer;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.ByteProcessor; import io.netty.util.ByteProcessor;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.Recycler; import io.netty.util.Recycler;
import io.netty.util.Recycler.Handle; import io.netty.util.Recycler.Handle;
@ -41,6 +38,9 @@ import java.nio.charset.CoderResult;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import static io.netty.util.internal.MathUtil.isOutOfBounds;
/** /**
* A collection of utility methods that is related with handling {@link ByteBuf}, * A collection of utility methods that is related with handling {@link ByteBuf},
* such as the generation of hex dump and swapping an integer's byte order. * such as the generation of hex dump and swapping an integer's byte order.
@ -661,7 +661,7 @@ public final class ByteBufUtil {
* The copy will start at {@link ByteBuf#readerIndex()} and copy {@link ByteBuf#readableBytes()} bytes. * The copy will start at {@link ByteBuf#readerIndex()} and copy {@link ByteBuf#readableBytes()} bytes.
*/ */
public static byte[] getBytes(ByteBuf buf) { public static byte[] getBytes(ByteBuf buf) {
return getBytes(buf, checkNotNull(buf, "buf").readerIndex(), buf.readableBytes()); return getBytes(buf, buf.readerIndex(), buf.readableBytes());
} }
/** /**
@ -679,7 +679,7 @@ public final class ByteBufUtil {
* If {@code copy} is false the underlying storage will be shared, if possible. * If {@code copy} is false the underlying storage will be shared, if possible.
*/ */
public static byte[] getBytes(ByteBuf buf, int start, int length, boolean copy) { public static byte[] getBytes(ByteBuf buf, int start, int length, boolean copy) {
if (start < 0 || length > checkNotNull(buf, "buf").capacity() - start) { if (isOutOfBounds(start, length, buf.capacity())) {
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
+ ") <= " + "buf.capacity(" + buf.capacity() + ')'); + ") <= " + "buf.capacity(" + buf.capacity() + ')');
} }
@ -706,12 +706,10 @@ public final class ByteBufUtil {
* @param dstIdx the starting offset in the destination byte array. * @param dstIdx the starting offset in the destination byte array.
* @param length the number of characters to copy. * @param length the number of characters to copy.
*/ */
public static void copy(ByteString src, int srcIdx, ByteBuf dst, int dstIdx, int length) { public static void copy(AsciiString src, int srcIdx, ByteBuf dst, int dstIdx, int length) {
final int thisLen = src.length(); if (isOutOfBounds(srcIdx, length, src.length())) {
if (srcIdx < 0 || length > thisLen - srcIdx) {
throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
+ length + ") <= srcLen(" + thisLen + ')'); + length + ") <= srcLen(" + src.length() + ')');
} }
checkNotNull(dst, "dst").setBytes(dstIdx, src.array(), srcIdx + src.arrayOffset(), length); checkNotNull(dst, "dst").setBytes(dstIdx, src.array(), srcIdx + src.arrayOffset(), length);
@ -724,12 +722,10 @@ public final class ByteBufUtil {
* @param dst the destination byte array. * @param dst the destination byte array.
* @param length the number of characters to copy. * @param length the number of characters to copy.
*/ */
public static void copy(ByteString src, int srcIdx, ByteBuf dst, int length) { public static void copy(AsciiString src, int srcIdx, ByteBuf dst, int length) {
final int thisLen = src.length(); if (isOutOfBounds(srcIdx, length, src.length())) {
if (srcIdx < 0 || length > thisLen - srcIdx) {
throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
+ length + ") <= srcLen(" + thisLen + ')'); + length + ") <= srcLen(" + src.length() + ')');
} }
checkNotNull(dst, "dst").writeBytes(src.array(), srcIdx + src.arrayOffset(), length); checkNotNull(dst, "dst").writeBytes(src.array(), srcIdx + src.arrayOffset(), length);
@ -771,7 +767,7 @@ public final class ByteBufUtil {
* the given {@code length}. * the given {@code length}.
*/ */
public static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf, int offset, int length) { public static void appendPrettyHexDump(StringBuilder dump, ByteBuf buf, int offset, int length) {
if (offset < 0 || length > checkNotNull(buf, "buf").capacity() - offset) { if (isOutOfBounds(offset, length, buf.capacity())) {
throw new IndexOutOfBoundsException( throw new IndexOutOfBoundsException(
"expected: " + "0 <= offset(" + offset + ") <= offset + length(" + length "expected: " + "0 <= offset(" + offset + ") <= offset + length(" + length
+ ") <= " + "buf.capacity(" + buf.capacity() + ')'); + ") <= " + "buf.capacity(" + buf.capacity() + ')');

View File

@ -23,14 +23,14 @@ import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import static io.netty.util.internal.MathUtil.isOutOfBounds;
import static io.netty.util.internal.ObjectUtil.checkNotNull; import static io.netty.util.internal.ObjectUtil.checkNotNull;
import static io.netty.util.internal.PlatformDependent.BIG_ENDIAN_NATIVE_ORDER;
/** /**
* All operations get and set as {@link ByteOrder#BIG_ENDIAN}. * All operations get and set as {@link ByteOrder#BIG_ENDIAN}.
*/ */
final class UnsafeByteBufUtil { final class UnsafeByteBufUtil {
static final boolean BIG_ENDIAN_NATIVE_ORDER = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
private static final boolean UNALIGNED = PlatformDependent.isUnaligned(); private static final boolean UNALIGNED = PlatformDependent.isUnaligned();
static byte getByte(long address) { static byte getByte(long address) {
@ -282,7 +282,7 @@ final class UnsafeByteBufUtil {
static void getBytes(AbstractByteBuf buf, long addr, int index, ByteBuf dst, int dstIndex, int length) { static void getBytes(AbstractByteBuf buf, long addr, int index, ByteBuf dst, int dstIndex, int length) {
buf.checkIndex(index, length); buf.checkIndex(index, length);
checkNotNull(dst, "dst"); checkNotNull(dst, "dst");
if (AbstractByteBuf.isInvalid(dstIndex, length, dst.capacity())) { if (isOutOfBounds(dstIndex, length, dst.capacity())) {
throw new IndexOutOfBoundsException("dstIndex: " + dstIndex); throw new IndexOutOfBoundsException("dstIndex: " + dstIndex);
} }
@ -298,7 +298,7 @@ final class UnsafeByteBufUtil {
static void getBytes(AbstractByteBuf buf, long addr, int index, byte[] dst, int dstIndex, int length) { static void getBytes(AbstractByteBuf buf, long addr, int index, byte[] dst, int dstIndex, int length) {
buf.checkIndex(index, length); buf.checkIndex(index, length);
checkNotNull(dst, "dst"); checkNotNull(dst, "dst");
if (AbstractByteBuf.isInvalid(dstIndex, length, dst.length)) { if (isOutOfBounds(dstIndex, length, dst.length)) {
throw new IndexOutOfBoundsException("dstIndex: " + dstIndex); throw new IndexOutOfBoundsException("dstIndex: " + dstIndex);
} }
if (length != 0) { if (length != 0) {
@ -328,7 +328,7 @@ final class UnsafeByteBufUtil {
static void setBytes(AbstractByteBuf buf, long addr, int index, ByteBuf src, int srcIndex, int length) { static void setBytes(AbstractByteBuf buf, long addr, int index, ByteBuf src, int srcIndex, int length) {
buf.checkIndex(index, length); buf.checkIndex(index, length);
checkNotNull(src, "src"); checkNotNull(src, "src");
if (AbstractByteBuf.isInvalid(srcIndex, length, src.capacity())) { if (isOutOfBounds(srcIndex, length, src.capacity())) {
throw new IndexOutOfBoundsException("srcIndex: " + srcIndex); throw new IndexOutOfBoundsException("srcIndex: " + srcIndex);
} }

View File

@ -36,7 +36,8 @@ public class CombinedHttpHeaders extends DefaultHttpHeaders {
super(new CombinedHttpHeadersImpl(CASE_INSENSITIVE_HASHER, valueConverter(validate), nameValidator(validate))); super(new CombinedHttpHeadersImpl(CASE_INSENSITIVE_HASHER, valueConverter(validate), nameValidator(validate)));
} }
private static final class CombinedHttpHeadersImpl extends DefaultHeaders<CharSequence> { private static final class CombinedHttpHeadersImpl
extends DefaultHeaders<CharSequence, CharSequence, CombinedHttpHeadersImpl> {
/** /**
* An estimate of the size of a header value. * An estimate of the size of a header value.
*/ */

View File

@ -18,6 +18,7 @@ package io.netty.handler.codec.http;
import io.netty.handler.codec.CharSequenceValueConverter; import io.netty.handler.codec.CharSequenceValueConverter;
import io.netty.handler.codec.DefaultHeaders; import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.DefaultHeaders.NameValidator; import io.netty.handler.codec.DefaultHeaders.NameValidator;
import io.netty.handler.codec.DefaultHeadersImpl;
import io.netty.handler.codec.HeadersUtils; import io.netty.handler.codec.HeadersUtils;
import io.netty.handler.codec.ValueConverter; import io.netty.handler.codec.ValueConverter;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
@ -36,7 +37,6 @@ import java.util.Set;
import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER; import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER;
import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER; import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/** /**
* Default implementation of {@link HttpHeaders}. * Default implementation of {@link HttpHeaders}.
@ -46,7 +46,7 @@ public class DefaultHttpHeaders extends HttpHeaders {
private static final ByteProcessor HEADER_NAME_VALIDATOR = new ByteProcessor() { private static final ByteProcessor HEADER_NAME_VALIDATOR = new ByteProcessor() {
@Override @Override
public boolean process(byte value) throws Exception { public boolean process(byte value) throws Exception {
validateChar((char) (value & 0xFF)); validateHeaderNameElement(value);
return true; return true;
} }
}; };
@ -60,16 +60,15 @@ public class DefaultHttpHeaders extends HttpHeaders {
PlatformDependent.throwException(e); PlatformDependent.throwException(e);
} }
} else { } else {
checkNotNull(name, "name");
// Go through each character in the name // Go through each character in the name
for (int index = 0; index < name.length(); ++index) { for (int index = 0; index < name.length(); ++index) {
validateChar(name.charAt(index)); validateHeaderNameElement(name.charAt(index));
} }
} }
} }
}; };
private final DefaultHeaders<CharSequence> headers; private final DefaultHeaders<CharSequence, CharSequence, ?> headers;
public DefaultHttpHeaders() { public DefaultHttpHeaders() {
this(true); this(true);
@ -80,10 +79,12 @@ public class DefaultHttpHeaders extends HttpHeaders {
} }
protected DefaultHttpHeaders(boolean validate, NameValidator<CharSequence> nameValidator) { protected DefaultHttpHeaders(boolean validate, NameValidator<CharSequence> nameValidator) {
this(new DefaultHeaders<CharSequence>(CASE_INSENSITIVE_HASHER, valueConverter(validate), nameValidator)); this(new DefaultHeadersImpl<CharSequence, CharSequence>(CASE_INSENSITIVE_HASHER,
valueConverter(validate),
nameValidator));
} }
protected DefaultHttpHeaders(DefaultHeaders<CharSequence> headers) { protected DefaultHttpHeaders(DefaultHeaders<CharSequence, CharSequence, ?> headers) {
this.headers = headers; this.headers = headers;
} }
@ -319,8 +320,9 @@ public class DefaultHttpHeaders extends HttpHeaders {
return headers.hashCode(CASE_SENSITIVE_HASHER); return headers.hashCode(CASE_SENSITIVE_HASHER);
} }
private static void validateChar(char character) { private static void validateHeaderNameElement(byte value) {
switch (character) { switch (value) {
case 0x00:
case '\t': case '\t':
case '\n': case '\n':
case 0x0b: case 0x0b:
@ -333,12 +335,37 @@ public class DefaultHttpHeaders extends HttpHeaders {
case '=': case '=':
throw new IllegalArgumentException( throw new IllegalArgumentException(
"a header name cannot contain the following prohibited characters: =,;: \\t\\r\\n\\v\\f: " + "a header name cannot contain the following prohibited characters: =,;: \\t\\r\\n\\v\\f: " +
character); value);
default: default:
// Check to see if the character is not an ASCII character, or invalid // Check to see if the character is not an ASCII character, or invalid
if (character > 127) { if (value < 0) {
throw new IllegalArgumentException("a header name cannot contain non-ASCII character: " + throw new IllegalArgumentException("a header name cannot contain non-ASCII character: " +
character); value);
}
}
}
private static void validateHeaderNameElement(char value) {
switch (value) {
case 0x00:
case '\t':
case '\n':
case 0x0b:
case '\f':
case '\r':
case ' ':
case ',':
case ':':
case ';':
case '=':
throw new IllegalArgumentException(
"a header name cannot contain the following prohibited characters: =,;: \\t\\r\\n\\v\\f: " +
value);
default:
// Check to see if the character is not an ASCII character, or invalid
if (value > 127) {
throw new IllegalArgumentException("a header name cannot contain non-ASCII character: " +
value);
} }
} }
} }
@ -357,13 +384,9 @@ public class DefaultHttpHeaders extends HttpHeaders {
@Override @Override
public CharSequence convertObject(Object value) { public CharSequence convertObject(Object value) {
checkNotNull(value, "value");
if (value instanceof CharSequence) { if (value instanceof CharSequence) {
return (CharSequence) value; return (CharSequence) value;
} }
if (value instanceof Number) {
return value.toString();
}
if (value instanceof Date) { if (value instanceof Date) {
return HttpHeaderDateFormat.get().format((Date) value); return HttpHeaderDateFormat.get().format((Date) value);
} }

View File

@ -19,6 +19,7 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import static io.netty.util.AsciiString.c2b;
final class HttpHeadersEncoder { final class HttpHeadersEncoder {
@ -29,8 +30,8 @@ final class HttpHeadersEncoder {
final int nameLen = name.length(); final int nameLen = name.length();
final int valueLen = value.length(); final int valueLen = value.length();
final int entryLen = nameLen + valueLen + 4; final int entryLen = nameLen + valueLen + 4;
int offset = buf.writerIndex();
buf.ensureWritable(entryLen); buf.ensureWritable(entryLen);
int offset = buf.writerIndex();
writeAscii(buf, offset, name, nameLen); writeAscii(buf, offset, name, nameLen);
offset += nameLen; offset += nameLen;
buf.setByte(offset ++, ':'); buf.setByte(offset ++, ':');
@ -44,23 +45,15 @@ final class HttpHeadersEncoder {
private static void writeAscii(ByteBuf buf, int offset, CharSequence value, int valueLen) { private static void writeAscii(ByteBuf buf, int offset, CharSequence value, int valueLen) {
if (value instanceof AsciiString) { if (value instanceof AsciiString) {
writeAsciiString(buf, offset, (AsciiString) value, valueLen); ByteBufUtil.copy((AsciiString) value, 0, buf, offset, valueLen);
} else { } else {
writeCharSequence(buf, offset, value, valueLen); writeCharSequence(buf, offset, value, valueLen);
} }
} }
private static void writeAsciiString(ByteBuf buf, int offset, AsciiString value, int valueLen) {
ByteBufUtil.copy(value, 0, buf, offset, valueLen);
}
private static void writeCharSequence(ByteBuf buf, int offset, CharSequence value, int valueLen) { private static void writeCharSequence(ByteBuf buf, int offset, CharSequence value, int valueLen) {
for (int i = 0; i < valueLen; i ++) { for (int i = 0; i < valueLen; ++i) {
buf.setByte(offset ++, c2b(value.charAt(i))); buf.setByte(offset ++, c2b(value.charAt(i)));
} }
} }
private static int c2b(char ch) {
return ch < 256? (byte) ch : '?';
}
} }

View File

@ -19,7 +19,6 @@ import static io.netty.handler.codec.http.HttpConstants.SP;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.ByteProcessor; import io.netty.util.ByteProcessor;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
/** /**
@ -471,7 +470,7 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
private static final class HttpStatusLineProcessor implements ByteProcessor { private static final class HttpStatusLineProcessor implements ByteProcessor {
private static final byte ASCII_SPACE = (byte) ' '; private static final byte ASCII_SPACE = (byte) ' ';
private final ByteString string; private final AsciiString string;
private int i; private int i;
/** /**
* 0 = New or havn't seen {@link ASCII_SPACE}. * 0 = New or havn't seen {@link ASCII_SPACE}.
@ -482,7 +481,7 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
private int state; private int state;
private HttpResponseStatus status; private HttpResponseStatus status;
public HttpStatusLineProcessor(ByteString string) { public HttpStatusLineProcessor(AsciiString string) {
this.string = string; this.string = string;
} }
@ -506,7 +505,7 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
} }
private void parseStatus(int codeEnd) { private void parseStatus(int codeEnd) {
int code = string.parseAsciiInt(0, codeEnd); int code = string.parseInt(0, codeEnd);
status = valueOf(code); status = valueOf(code);
if (codeEnd < string.length()) { if (codeEnd < string.length()) {
String actualReason = string.toString(codeEnd + 1, string.length()); String actualReason = string.toString(codeEnd + 1, string.length());
@ -534,7 +533,7 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
* *
* @throws IllegalArgumentException if the specified status line is malformed * @throws IllegalArgumentException if the specified status line is malformed
*/ */
public static HttpResponseStatus parseLine(ByteString line) { public static HttpResponseStatus parseLine(AsciiString line) {
try { try {
HttpStatusLineProcessor processor = new HttpStatusLineProcessor(line); HttpStatusLineProcessor processor = new HttpStatusLineProcessor(line);
line.forEachByte(processor); line.forEachByte(processor);

View File

@ -17,7 +17,6 @@ package io.netty.handler.codec.spdy;
import io.netty.handler.codec.CharSequenceValueConverter; import io.netty.handler.codec.CharSequenceValueConverter;
import io.netty.handler.codec.DefaultHeaders; import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.Headers;
import io.netty.handler.codec.HeadersUtils; import io.netty.handler.codec.HeadersUtils;
import java.util.Iterator; import java.util.Iterator;
@ -27,7 +26,7 @@ import java.util.Map.Entry;
import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER; import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER;
import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER; import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER;
public class DefaultSpdyHeaders extends DefaultHeaders<CharSequence> implements SpdyHeaders { public class DefaultSpdyHeaders extends DefaultHeaders<CharSequence, CharSequence, SpdyHeaders> implements SpdyHeaders {
private static final NameValidator<CharSequence> SpydNameValidator = new NameValidator<CharSequence>() { private static final NameValidator<CharSequence> SpydNameValidator = new NameValidator<CharSequence>() {
@Override @Override
public void validateName(CharSequence name) { public void validateName(CharSequence name) {
@ -42,214 +41,10 @@ public class DefaultSpdyHeaders extends DefaultHeaders<CharSequence> implements
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public DefaultSpdyHeaders(boolean validate) { public DefaultSpdyHeaders(boolean validate) {
super(CASE_INSENSITIVE_HASHER, super(CASE_INSENSITIVE_HASHER,
validate ? HeaderValueConverterAndValidator.INSTANCE : HeaderValueConverter.INSTANCE, validate ? HeaderValueConverterAndValidator.INSTANCE : CharSequenceValueConverter.INSTANCE,
validate ? SpydNameValidator : NameValidator.NOT_NULL); validate ? SpydNameValidator : NameValidator.NOT_NULL);
} }
@Override
public SpdyHeaders add(CharSequence name, CharSequence value) {
super.add(name, value);
return this;
}
@Override
public SpdyHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
super.add(name, values);
return this;
}
@Override
public SpdyHeaders add(CharSequence name, CharSequence... values) {
super.add(name, values);
return this;
}
@Override
public SpdyHeaders addObject(CharSequence name, Object value) {
super.addObject(name, value);
return this;
}
@Override
public SpdyHeaders addObject(CharSequence name, Iterable<?> values) {
super.addObject(name, values);
return this;
}
@Override
public SpdyHeaders addObject(CharSequence name, Object... values) {
super.addObject(name, values);
return this;
}
@Override
public SpdyHeaders addBoolean(CharSequence name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public SpdyHeaders addChar(CharSequence name, char value) {
super.addChar(name, value);
return this;
}
@Override
public SpdyHeaders addByte(CharSequence name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public SpdyHeaders addShort(CharSequence name, short value) {
super.addShort(name, value);
return this;
}
@Override
public SpdyHeaders addInt(CharSequence name, int value) {
super.addInt(name, value);
return this;
}
@Override
public SpdyHeaders addLong(CharSequence name, long value) {
super.addLong(name, value);
return this;
}
@Override
public SpdyHeaders addFloat(CharSequence name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public SpdyHeaders addDouble(CharSequence name, double value) {
super.addDouble(name, value);
return this;
}
@Override
public SpdyHeaders addTimeMillis(CharSequence name, long value) {
super.addTimeMillis(name, value);
return this;
}
@Override
public SpdyHeaders add(Headers<? extends CharSequence> headers) {
super.add(headers);
return this;
}
@Override
public SpdyHeaders set(CharSequence name, CharSequence value) {
super.set(name, value);
return this;
}
@Override
public SpdyHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
super.set(name, values);
return this;
}
@Override
public SpdyHeaders set(CharSequence name, CharSequence... values) {
super.set(name, values);
return this;
}
@Override
public SpdyHeaders setObject(CharSequence name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public SpdyHeaders setObject(CharSequence name, Iterable<?> values) {
super.setObject(name, values);
return this;
}
@Override
public SpdyHeaders setObject(CharSequence name, Object... values) {
super.setObject(name, values);
return this;
}
@Override
public SpdyHeaders setBoolean(CharSequence name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public SpdyHeaders setChar(CharSequence name, char value) {
super.setChar(name, value);
return this;
}
@Override
public SpdyHeaders setByte(CharSequence name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public SpdyHeaders setShort(CharSequence name, short value) {
super.setShort(name, value);
return this;
}
@Override
public SpdyHeaders setInt(CharSequence name, int value) {
super.setInt(name, value);
return this;
}
@Override
public SpdyHeaders setLong(CharSequence name, long value) {
super.setLong(name, value);
return this;
}
@Override
public SpdyHeaders setFloat(CharSequence name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public SpdyHeaders setDouble(CharSequence name, double value) {
super.setDouble(name, value);
return this;
}
@Override
public SpdyHeaders setTimeMillis(CharSequence name, long value) {
super.setTimeMillis(name, value);
return this;
}
@Override
public SpdyHeaders set(Headers<? extends CharSequence> headers) {
super.set(headers);
return this;
}
@Override
public SpdyHeaders setAll(Headers<? extends CharSequence> headers) {
super.setAll(headers);
return this;
}
@Override
public SpdyHeaders clear() {
super.clear();
return this;
}
@Override @Override
public String getAsString(CharSequence name) { public String getAsString(CharSequence name) {
return HeadersUtils.getAsString(this, name); return HeadersUtils.getAsString(this, name);
@ -276,23 +71,7 @@ public class DefaultSpdyHeaders extends DefaultHeaders<CharSequence> implements
ignoreCase ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER); ignoreCase ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER);
} }
private static class HeaderValueConverter extends CharSequenceValueConverter { private static final class HeaderValueConverterAndValidator extends CharSequenceValueConverter {
public static final HeaderValueConverter INSTANCE = new HeaderValueConverter();
@Override
public CharSequence convertObject(Object value) {
final CharSequence seq;
if (value instanceof CharSequence) {
seq = (CharSequence) value;
} else {
seq = value.toString();
}
return seq;
}
}
private static final class HeaderValueConverterAndValidator extends HeaderValueConverter {
public static final HeaderValueConverterAndValidator INSTANCE = new HeaderValueConverterAndValidator(); public static final HeaderValueConverterAndValidator INSTANCE = new HeaderValueConverterAndValidator();
@Override @Override

View File

@ -15,17 +15,15 @@
*/ */
package io.netty.handler.codec.spdy; package io.netty.handler.codec.spdy;
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_MAX_NV_LENGTH;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.util.AsciiString;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil;
import java.util.Set; import java.util.Set;
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_MAX_NV_LENGTH;
public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder { public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder {
private final int version; private final int version;
@ -59,18 +57,15 @@ public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder {
ByteBuf headerBlock = alloc.heapBuffer(); ByteBuf headerBlock = alloc.heapBuffer();
writeLengthField(headerBlock, numHeaders); writeLengthField(headerBlock, numHeaders);
for (CharSequence name: names) { for (CharSequence name: names) {
final ByteString nameBytes = new ByteString(name, CharsetUtil.UTF_8); writeLengthField(headerBlock, name.length());
int length = nameBytes.length(); ByteBufUtil.writeAscii(headerBlock, name);
writeLengthField(headerBlock, length);
ByteBufUtil.copy(nameBytes, 0, headerBlock, length);
int savedIndex = headerBlock.writerIndex(); int savedIndex = headerBlock.writerIndex();
int valueLength = 0; int valueLength = 0;
writeLengthField(headerBlock, valueLength); writeLengthField(headerBlock, valueLength);
for (CharSequence value: frame.headers().getAll(name)) { for (CharSequence value: frame.headers().getAll(name)) {
final ByteString valueBytes = new ByteString(value, CharsetUtil.UTF_8); int length = value.length();
length = valueBytes.length();
if (length > 0) { if (length > 0) {
ByteBufUtil.copy(valueBytes, 0, headerBlock, length); ByteBufUtil.writeAscii(headerBlock, value);
headerBlock.writeByte(0); headerBlock.writeByte(0);
valueLength += length + 1; valueLength += length + 1;
} }

View File

@ -26,7 +26,7 @@ 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 Headers<CharSequence> { public interface SpdyHeaders extends Headers<CharSequence, CharSequence, SpdyHeaders> {
/** /**
* SPDY HTTP header names * SPDY HTTP header names
@ -60,108 +60,6 @@ public interface SpdyHeaders extends Headers<CharSequence> {
private HttpNames() { } private HttpNames() { }
} }
@Override
SpdyHeaders add(CharSequence name, CharSequence value);
@Override
SpdyHeaders add(CharSequence name, Iterable<? extends CharSequence> values);
@Override
SpdyHeaders add(CharSequence name, CharSequence... values);
@Override
SpdyHeaders addObject(CharSequence name, Object value);
@Override
SpdyHeaders addObject(CharSequence name, Iterable<?> values);
@Override
SpdyHeaders addObject(CharSequence name, Object... values);
@Override
SpdyHeaders addBoolean(CharSequence name, boolean value);
@Override
SpdyHeaders addByte(CharSequence name, byte value);
@Override
SpdyHeaders addChar(CharSequence name, char value);
@Override
SpdyHeaders addShort(CharSequence name, short value);
@Override
SpdyHeaders addInt(CharSequence name, int value);
@Override
SpdyHeaders addLong(CharSequence name, long value);
@Override
SpdyHeaders addFloat(CharSequence name, float value);
@Override
SpdyHeaders addDouble(CharSequence name, double value);
@Override
SpdyHeaders addTimeMillis(CharSequence name, long value);
@Override
SpdyHeaders add(Headers<? extends CharSequence> headers);
@Override
SpdyHeaders set(CharSequence name, CharSequence value);
@Override
SpdyHeaders set(CharSequence name, Iterable<? extends CharSequence> values);
@Override
SpdyHeaders set(CharSequence name, CharSequence... values);
@Override
SpdyHeaders setBoolean(CharSequence name, boolean value);
@Override
SpdyHeaders setByte(CharSequence name, byte value);
@Override
SpdyHeaders setChar(CharSequence name, char value);
@Override
SpdyHeaders setShort(CharSequence name, short value);
@Override
SpdyHeaders setInt(CharSequence name, int value);
@Override
SpdyHeaders setLong(CharSequence name, long value);
@Override
SpdyHeaders setFloat(CharSequence name, float value);
@Override
SpdyHeaders setDouble(CharSequence name, double value);
@Override
SpdyHeaders setTimeMillis(CharSequence name, long value);
@Override
SpdyHeaders setObject(CharSequence name, Object value);
@Override
SpdyHeaders setObject(CharSequence name, Iterable<?> values);
@Override
SpdyHeaders setObject(CharSequence name, Object... values);
@Override
SpdyHeaders set(Headers<? extends CharSequence> headers);
@Override
SpdyHeaders setAll(Headers<? extends CharSequence> headers);
@Override
SpdyHeaders clear();
/** /**
* {@link Headers#get(Object)} and convert the result to a {@link String}. * {@link Headers#get(Object)} and convert the result to a {@link String}.
* @param name the name of the header to retrieve * @param name the name of the header to retrieve

View File

@ -0,0 +1,37 @@
/*
* 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.http2;
import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.UnsupportedValueConverter;
import io.netty.handler.codec.ValueConverter;
import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER;
import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER;
final class CharSequenceMap<V> extends DefaultHeaders<CharSequence, V, CharSequenceMap<V>> {
public CharSequenceMap() {
this(true);
}
public CharSequenceMap(boolean caseSensative) {
this(caseSensative, UnsupportedValueConverter.<V>instance());
}
public CharSequenceMap(boolean caseSensative, ValueConverter<V> valueConverter) {
super(caseSensative ? CASE_SENSITIVE_HASHER : CASE_INSENSITIVE_HASHER, valueConverter);
}
}

View File

@ -14,13 +14,6 @@
*/ */
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_ENCODING;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaderValues.DEFLATE;
import static io.netty.handler.codec.http.HttpHeaderValues.GZIP;
import static io.netty.handler.codec.http.HttpHeaderValues.IDENTITY;
import static io.netty.handler.codec.http.HttpHeaderValues.X_DEFLATE;
import static io.netty.handler.codec.http.HttpHeaderValues.X_GZIP;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
@ -31,7 +24,14 @@ import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.compression.ZlibCodecFactory; import io.netty.handler.codec.compression.ZlibCodecFactory;
import io.netty.handler.codec.compression.ZlibWrapper; import io.netty.handler.codec.compression.ZlibWrapper;
import io.netty.util.ByteString;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_ENCODING;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaderValues.DEFLATE;
import static io.netty.handler.codec.http.HttpHeaderValues.GZIP;
import static io.netty.handler.codec.http.HttpHeaderValues.IDENTITY;
import static io.netty.handler.codec.http.HttpHeaderValues.X_DEFLATE;
import static io.netty.handler.codec.http.HttpHeaderValues.X_GZIP;
/** /**
* A decorating HTTP2 encoder that will compress data frames according to the {@code content-encoding} header for each * A decorating HTTP2 encoder that will compress data frames according to the {@code content-encoding} header for each
@ -195,11 +195,11 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
* (alternatively, you can throw a {@link Http2Exception} to block unknown encoding). * (alternatively, you can throw a {@link Http2Exception} to block unknown encoding).
* @throws Http2Exception If the specified encoding is not not supported and warrants an exception * @throws Http2Exception If the specified encoding is not not supported and warrants an exception
*/ */
protected EmbeddedChannel newContentCompressor(ByteString contentEncoding) throws Http2Exception { protected EmbeddedChannel newContentCompressor(CharSequence contentEncoding) throws Http2Exception {
if (GZIP.equals(contentEncoding) || X_GZIP.equals(contentEncoding)) { if (GZIP.contentEqualsIgnoreCase(contentEncoding) || X_GZIP.contentEqualsIgnoreCase(contentEncoding)) {
return newCompressionChannel(ZlibWrapper.GZIP); return newCompressionChannel(ZlibWrapper.GZIP);
} }
if (DEFLATE.equals(contentEncoding) || X_DEFLATE.equals(contentEncoding)) { if (DEFLATE.contentEqualsIgnoreCase(contentEncoding) || X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) {
return newCompressionChannel(ZlibWrapper.ZLIB); return newCompressionChannel(ZlibWrapper.ZLIB);
} }
// 'identity' or unsupported // 'identity' or unsupported
@ -214,7 +214,7 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
* @return the expected content encoding of the new content. * @return the expected content encoding of the new content.
* @throws Http2Exception if the {@code contentEncoding} is not supported and warrants an exception * @throws Http2Exception if the {@code contentEncoding} is not supported and warrants an exception
*/ */
protected ByteString getTargetContentEncoding(ByteString contentEncoding) throws Http2Exception { protected CharSequence getTargetContentEncoding(CharSequence contentEncoding) throws Http2Exception {
return contentEncoding; return contentEncoding;
} }
@ -241,14 +241,14 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
return null; return null;
} }
ByteString encoding = headers.get(CONTENT_ENCODING); CharSequence encoding = headers.get(CONTENT_ENCODING);
if (encoding == null) { if (encoding == null) {
encoding = IDENTITY; encoding = IDENTITY;
} }
final EmbeddedChannel compressor = newContentCompressor(encoding); final EmbeddedChannel compressor = newContentCompressor(encoding);
if (compressor != null) { if (compressor != null) {
ByteString targetContentEncoding = getTargetContentEncoding(encoding); CharSequence targetContentEncoding = getTargetContentEncoding(encoding);
if (IDENTITY.equals(targetContentEncoding)) { if (IDENTITY.contentEqualsIgnoreCase(targetContentEncoding)) {
headers.remove(CONTENT_ENCODING); headers.remove(CONTENT_ENCODING);
} else { } else {
headers.set(CONTENT_ENCODING, targetContentEncoding); headers.set(CONTENT_ENCODING, targetContentEncoding);

View File

@ -14,9 +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;
/** /**
* Provides common functionality for {@link Http2HeaderTable} * Provides common functionality for {@link Http2HeaderTable}
*/ */

View File

@ -14,28 +14,32 @@
*/ */
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import static io.netty.handler.codec.http2.Http2Exception.connectionError; import io.netty.handler.codec.CharSequenceValueConverter;
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import io.netty.handler.codec.ByteStringValueConverter;
import io.netty.handler.codec.DefaultHeaders; import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.Headers; import io.netty.util.AsciiString;
import io.netty.util.ByteProcessor; import io.netty.util.ByteProcessor;
import io.netty.util.ByteString;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
public class DefaultHttp2Headers extends DefaultHeaders<ByteString> implements Http2Headers { import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER;
import static io.netty.util.AsciiString.isUpperCase;
public class DefaultHttp2Headers
extends DefaultHeaders<CharSequence, CharSequence, Http2Headers> implements Http2Headers {
private static final ByteProcessor HTTP2_NAME_VALIDATOR_PROCESSOR = new ByteProcessor() { private static final ByteProcessor HTTP2_NAME_VALIDATOR_PROCESSOR = new ByteProcessor() {
@Override @Override
public boolean process(byte value) throws Exception { public boolean process(byte value) throws Exception {
return value < 'A' || value > 'Z'; return !isUpperCase(value);
} }
}; };
private static final NameValidator<ByteString> HTTP2_NAME_VALIDATOR = new NameValidator<ByteString>() { private static final NameValidator<CharSequence> HTTP2_NAME_VALIDATOR = new NameValidator<CharSequence>() {
@Override @Override
public void validateName(ByteString name) { public void validateName(CharSequence name) {
if (name instanceof AsciiString) {
final int index; final int index;
try { try {
index = name.forEachByte(HTTP2_NAME_VALIDATOR_PROCESSOR); index = ((AsciiString) name).forEachByte(HTTP2_NAME_VALIDATOR_PROCESSOR);
} catch (Http2Exception e) { } catch (Http2Exception e) {
PlatformDependent.throwException(e); PlatformDependent.throwException(e);
return; return;
@ -46,11 +50,21 @@ public class DefaultHttp2Headers extends DefaultHeaders<ByteString> implements H
} }
if (index != -1) { if (index != -1) {
PlatformDependent.throwException(connectionError(PROTOCOL_ERROR, "invalid header name [%s]", name)); PlatformDependent.throwException(connectionError(PROTOCOL_ERROR,
"invalid header name [%s]", name));
}
} else {
for (int i = 0; i < name.length(); ++i) {
if (isUpperCase(name.charAt(i))) {
PlatformDependent.throwException(connectionError(PROTOCOL_ERROR,
"invalid header name [%s]", name));
}
}
} }
} }
}; };
private HeaderEntry<ByteString> firstNonPseudo = head;
private HeaderEntry<CharSequence, CharSequence> firstNonPseudo = head;
/** /**
* Create a new instance. * Create a new instance.
@ -69,282 +83,83 @@ public class DefaultHttp2Headers extends DefaultHeaders<ByteString> implements H
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public DefaultHttp2Headers(boolean validate) { public DefaultHttp2Headers(boolean validate) {
super(ByteStringValueConverter.INSTANCE, validate ? HTTP2_NAME_VALIDATOR : NameValidator.NOT_NULL); // Case sensitive compare is used because it is cheaper, and header validation can be used to catch invalid
// headers.
super(CASE_SENSITIVE_HASHER,
CharSequenceValueConverter.INSTANCE,
validate ? HTTP2_NAME_VALIDATOR : NameValidator.NOT_NULL);
} }
@Override @Override
public Http2Headers add(ByteString name, ByteString value) { public Http2Headers method(CharSequence value) {
super.add(name, value);
return this;
}
@Override
public Http2Headers add(ByteString name, Iterable<? extends ByteString> values) {
super.add(name, values);
return this;
}
@Override
public Http2Headers add(ByteString name, ByteString... values) {
super.add(name, values);
return this;
}
@Override
public Http2Headers addObject(ByteString name, Object value) {
super.addObject(name, value);
return this;
}
@Override
public Http2Headers addObject(ByteString name, Iterable<?> values) {
super.addObject(name, values);
return this;
}
@Override
public Http2Headers addObject(ByteString name, Object... values) {
super.addObject(name, values);
return this;
}
@Override
public Http2Headers addBoolean(ByteString name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public Http2Headers addChar(ByteString name, char value) {
super.addChar(name, value);
return this;
}
@Override
public Http2Headers addByte(ByteString name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public Http2Headers addShort(ByteString name, short value) {
super.addShort(name, value);
return this;
}
@Override
public Http2Headers addInt(ByteString name, int value) {
super.addInt(name, value);
return this;
}
@Override
public Http2Headers addLong(ByteString name, long value) {
super.addLong(name, value);
return this;
}
@Override
public Http2Headers addFloat(ByteString name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public Http2Headers addDouble(ByteString name, double value) {
super.addDouble(name, value);
return this;
}
@Override
public Http2Headers addTimeMillis(ByteString name, long value) {
super.addTimeMillis(name, value);
return this;
}
@Override
public Http2Headers add(Headers<? extends ByteString> headers) {
super.add(headers);
return this;
}
@Override
public Http2Headers set(ByteString name, ByteString value) {
super.set(name, value);
return this;
}
@Override
public Http2Headers set(ByteString name, Iterable<? extends ByteString> values) {
super.set(name, values);
return this;
}
@Override
public Http2Headers set(ByteString name, ByteString... values) {
super.set(name, values);
return this;
}
@Override
public Http2Headers setObject(ByteString name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public Http2Headers setObject(ByteString name, Iterable<?> values) {
super.setObject(name, values);
return this;
}
@Override
public Http2Headers setObject(ByteString name, Object... values) {
super.setObject(name, values);
return this;
}
@Override
public Http2Headers setBoolean(ByteString name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public Http2Headers setChar(ByteString name, char value) {
super.setChar(name, value);
return this;
}
@Override
public Http2Headers setByte(ByteString name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public Http2Headers setShort(ByteString name, short value) {
super.setShort(name, value);
return this;
}
@Override
public Http2Headers setInt(ByteString name, int value) {
super.setInt(name, value);
return this;
}
@Override
public Http2Headers setLong(ByteString name, long value) {
super.setLong(name, value);
return this;
}
@Override
public Http2Headers setFloat(ByteString name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public Http2Headers setDouble(ByteString name, double value) {
super.setDouble(name, value);
return this;
}
@Override
public Http2Headers setTimeMillis(ByteString name, long value) {
super.setTimeMillis(name, value);
return this;
}
@Override
public Http2Headers set(Headers<? extends ByteString> headers) {
super.set(headers);
return this;
}
@Override
public Http2Headers setAll(Headers<? extends ByteString> headers) {
super.setAll(headers);
return this;
}
@Override
public Http2Headers clear() {
super.clear();
return this;
}
@Override
public Http2Headers method(ByteString value) {
set(PseudoHeaderName.METHOD.value(), value); set(PseudoHeaderName.METHOD.value(), value);
return this; return this;
} }
@Override @Override
public Http2Headers scheme(ByteString value) { public Http2Headers scheme(CharSequence value) {
set(PseudoHeaderName.SCHEME.value(), value); set(PseudoHeaderName.SCHEME.value(), value);
return this; return this;
} }
@Override @Override
public Http2Headers authority(ByteString value) { public Http2Headers authority(CharSequence value) {
set(PseudoHeaderName.AUTHORITY.value(), value); set(PseudoHeaderName.AUTHORITY.value(), value);
return this; return this;
} }
@Override @Override
public Http2Headers path(ByteString value) { public Http2Headers path(CharSequence value) {
set(PseudoHeaderName.PATH.value(), value); set(PseudoHeaderName.PATH.value(), value);
return this; return this;
} }
@Override @Override
public Http2Headers status(ByteString value) { public Http2Headers status(CharSequence value) {
set(PseudoHeaderName.STATUS.value(), value); set(PseudoHeaderName.STATUS.value(), value);
return this; return this;
} }
@Override @Override
public ByteString method() { public CharSequence method() {
return get(PseudoHeaderName.METHOD.value()); return get(PseudoHeaderName.METHOD.value());
} }
@Override @Override
public ByteString scheme() { public CharSequence scheme() {
return get(PseudoHeaderName.SCHEME.value()); return get(PseudoHeaderName.SCHEME.value());
} }
@Override @Override
public ByteString authority() { public CharSequence authority() {
return get(PseudoHeaderName.AUTHORITY.value()); return get(PseudoHeaderName.AUTHORITY.value());
} }
@Override @Override
public ByteString path() { public CharSequence path() {
return get(PseudoHeaderName.PATH.value()); return get(PseudoHeaderName.PATH.value());
} }
@Override @Override
public ByteString status() { public CharSequence status() {
return get(PseudoHeaderName.STATUS.value()); return get(PseudoHeaderName.STATUS.value());
} }
@Override @Override
protected final HeaderEntry<ByteString> newHeaderEntry(int h, ByteString name, ByteString value, protected final HeaderEntry<CharSequence, CharSequence> newHeaderEntry(int h, CharSequence name, CharSequence value,
HeaderEntry<ByteString> next) { HeaderEntry<CharSequence, CharSequence> next) {
return new Http2HeaderEntry(h, name, value, next); return new Http2HeaderEntry(h, name, value, next);
} }
private final class Http2HeaderEntry extends HeaderEntry<ByteString> { private final class Http2HeaderEntry extends HeaderEntry<CharSequence, CharSequence> {
protected Http2HeaderEntry(int hash, ByteString key, ByteString value, HeaderEntry<ByteString> next) { protected Http2HeaderEntry(int hash, CharSequence key, CharSequence value,
HeaderEntry<CharSequence, CharSequence> next) {
super(hash, key); super(hash, key);
this.value = value; this.value = value;
this.next = next; this.next = next;
// Make sure the pseudo headers fields are first in iteration order // Make sure the pseudo headers fields are first in iteration order
if (!key.isEmpty() && key.byteAt(0) == ':') { if (key.length() != 0 && key.charAt(0) == ':') {
after = firstNonPseudo; after = firstNonPseudo;
before = firstNonPseudo.before(); before = firstNonPseudo.before();
} else { } else {

View File

@ -15,6 +15,15 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import com.twitter.hpack.Decoder;
import com.twitter.hpack.HeaderListener;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.util.AsciiString;
import java.io.IOException;
import java.io.InputStream;
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_TABLE_SIZE; import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_TABLE_SIZE;
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.Http2Error.COMPRESSION_ERROR; import static io.netty.handler.codec.http2.Http2Error.COMPRESSION_ERROR;
@ -22,15 +31,6 @@ import static io.netty.handler.codec.http2.Http2Error.ENHANCE_YOUR_CALM;
import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import static io.netty.handler.codec.http2.Http2Exception.connectionError; import static io.netty.handler.codec.http2.Http2Exception.connectionError;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.util.ByteString;
import java.io.IOException;
import java.io.InputStream;
import com.twitter.hpack.Decoder;
import com.twitter.hpack.HeaderListener;
public class DefaultHttp2HeadersDecoder implements Http2HeadersDecoder, Http2HeadersDecoder.Configuration { public class DefaultHttp2HeadersDecoder implements Http2HeadersDecoder, Http2HeadersDecoder.Configuration {
private final int maxHeaderSize; private final int maxHeaderSize;
@ -91,7 +91,7 @@ public class DefaultHttp2HeadersDecoder implements Http2HeadersDecoder, Http2Hea
HeaderListener listener = new HeaderListener() { HeaderListener listener = new HeaderListener() {
@Override @Override
public void addHeader(byte[] key, byte[] value, boolean sensitive) { public void addHeader(byte[] key, byte[] value, boolean sensitive) {
headers.add(new ByteString(key, false), new ByteString(value, false)); headers.add(new AsciiString(key, false), new AsciiString(value, false));
} }
}; };

View File

@ -15,22 +15,22 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_TABLE_SIZE; import com.twitter.hpack.Encoder;
import static io.netty.handler.codec.http2.Http2Error.COMPRESSION_ERROR;
import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream; import io.netty.buffer.ByteBufOutputStream;
import io.netty.util.ByteString; import io.netty.util.AsciiString;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Map.Entry; import java.util.Map.Entry;
import com.twitter.hpack.Encoder; import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_TABLE_SIZE;
import static io.netty.handler.codec.http2.Http2Error.COMPRESSION_ERROR;
import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2HeadersEncoder.Configuration { public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2HeadersEncoder.Configuration {
private final Encoder encoder; private final Encoder encoder;
@ -64,7 +64,7 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2Hea
tableSizeChangeOutput.reset(); tableSizeChangeOutput.reset();
} }
for (Entry<ByteString, ByteString> header : headers) { for (Entry<CharSequence, CharSequence> header : headers) {
encodeHeader(header.getKey(), header.getValue(), stream); encodeHeader(header.getKey(), header.getValue(), stream);
} }
} catch (Http2Exception e) { } catch (Http2Exception e) {
@ -90,11 +90,13 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2Hea
return this; return this;
} }
private void encodeHeader(ByteString key, ByteString value, OutputStream stream) throws IOException { private byte[] toBytes(CharSequence chars) {
encoder.encodeHeader(stream, AsciiString aString = AsciiString.of(chars);
key.isEntireArrayUsed() ? key.array() : new ByteString(key, true).array(), return aString.isEntireArrayUsed() ? aString.array() : aString.toByteArray();
value.isEntireArrayUsed() ? value.array() : new ByteString(value, true).array(), }
sensitivityDetector.isSensitive(key, value));
private void encodeHeader(CharSequence key, CharSequence value, OutputStream stream) throws IOException {
encoder.encodeHeader(stream, toBytes(key), toBytes(value), sensitivityDetector.isSensitive(key, value));
} }
/** /**

View File

@ -14,6 +14,14 @@
*/ */
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.compression.ZlibCodecFactory;
import io.netty.handler.codec.compression.ZlibWrapper;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_ENCODING; import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_ENCODING;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaderValues.DEFLATE; import static io.netty.handler.codec.http.HttpHeaderValues.DEFLATE;
@ -24,14 +32,6 @@ import static io.netty.handler.codec.http.HttpHeaderValues.X_GZIP;
import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR; import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
import static io.netty.handler.codec.http2.Http2Exception.streamError; import static io.netty.handler.codec.http2.Http2Exception.streamError;
import static io.netty.util.internal.ObjectUtil.checkNotNull; import static io.netty.util.internal.ObjectUtil.checkNotNull;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.compression.ZlibCodecFactory;
import io.netty.handler.codec.compression.ZlibWrapper;
import io.netty.util.ByteString;
/** /**
* A HTTP2 frame listener that will decompress data frames according to the {@code content-encoding} header for each * A HTTP2 frame listener that will decompress data frames according to the {@code content-encoding} header for each
@ -160,11 +160,11 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
* (alternatively, you can throw a {@link Http2Exception} to block unknown encoding). * (alternatively, you can throw a {@link Http2Exception} to block unknown encoding).
* @throws Http2Exception If the specified encoding is not not supported and warrants an exception * @throws Http2Exception If the specified encoding is not not supported and warrants an exception
*/ */
protected EmbeddedChannel newContentDecompressor(ByteString contentEncoding) throws Http2Exception { protected EmbeddedChannel newContentDecompressor(CharSequence contentEncoding) throws Http2Exception {
if (GZIP.equals(contentEncoding) || X_GZIP.equals(contentEncoding)) { if (GZIP.contentEqualsIgnoreCase(contentEncoding) || X_GZIP.contentEqualsIgnoreCase(contentEncoding)) {
return new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP)); return new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP));
} }
if (DEFLATE.equals(contentEncoding) || X_DEFLATE.equals(contentEncoding)) { if (DEFLATE.contentEqualsIgnoreCase(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));
@ -181,7 +181,7 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
* @return the expected content encoding of the new content. * @return the expected content encoding of the new content.
* @throws Http2Exception if the {@code contentEncoding} is not supported and warrants an exception * @throws Http2Exception if the {@code contentEncoding} is not supported and warrants an exception
*/ */
protected ByteString getTargetContentEncoding(@SuppressWarnings("UnusedParameters") ByteString contentEncoding) protected CharSequence getTargetContentEncoding(@SuppressWarnings("UnusedParameters") CharSequence contentEncoding)
throws Http2Exception { throws Http2Exception {
return IDENTITY; return IDENTITY;
} }
@ -204,7 +204,7 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
Http2Decompressor decompressor = decompressor(stream); Http2Decompressor decompressor = decompressor(stream);
if (decompressor == null && !endOfStream) { if (decompressor == null && !endOfStream) {
// Determine the content encoding. // Determine the content encoding.
ByteString contentEncoding = headers.get(CONTENT_ENCODING); CharSequence contentEncoding = headers.get(CONTENT_ENCODING);
if (contentEncoding == null) { if (contentEncoding == null) {
contentEncoding = IDENTITY; contentEncoding = IDENTITY;
} }
@ -214,8 +214,8 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
stream.setProperty(propertyKey, decompressor); stream.setProperty(propertyKey, decompressor);
// Decode the content and remove or replace the existing headers // Decode the content and remove or replace the existing headers
// so that the message looks like a decoded message. // so that the message looks like a decoded message.
ByteString targetContentEncoding = getTargetContentEncoding(contentEncoding); CharSequence targetContentEncoding = getTargetContentEncoding(contentEncoding);
if (IDENTITY.equals(targetContentEncoding)) { if (IDENTITY.contentEqualsIgnoreCase(targetContentEncoding)) {
headers.remove(CONTENT_ENCODING); headers.remove(CONTENT_ENCODING);
} else { } else {
headers.set(CONTENT_ENCODING, targetContentEncoding); headers.set(CONTENT_ENCODING, targetContentEncoding);

View File

@ -16,266 +16,61 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.handler.codec.EmptyHeaders; import io.netty.handler.codec.EmptyHeaders;
import io.netty.handler.codec.Headers;
import io.netty.util.ByteString;
public final class EmptyHttp2Headers extends EmptyHeaders<ByteString> implements Http2Headers { public final class EmptyHttp2Headers
extends EmptyHeaders<CharSequence, CharSequence, Http2Headers> implements Http2Headers {
public static final EmptyHttp2Headers INSTANCE = new EmptyHttp2Headers(); public static final EmptyHttp2Headers INSTANCE = new EmptyHttp2Headers();
private EmptyHttp2Headers() { private EmptyHttp2Headers() {
} }
@Override @Override
public Http2Headers add(ByteString name, ByteString value) { public EmptyHttp2Headers method(CharSequence method) {
super.add(name, value);
return this;
}
@Override
public Http2Headers add(ByteString name, Iterable<? extends ByteString> values) {
super.add(name, values);
return this;
}
@Override
public Http2Headers add(ByteString name, ByteString... values) {
super.add(name, values);
return this;
}
@Override
public Http2Headers addObject(ByteString name, Object value) {
super.addObject(name, value);
return this;
}
@Override
public Http2Headers addObject(ByteString name, Iterable<?> values) {
super.addObject(name, values);
return this;
}
@Override
public Http2Headers addObject(ByteString name, Object... values) {
super.addObject(name, values);
return this;
}
@Override
public Http2Headers addBoolean(ByteString name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public Http2Headers addChar(ByteString name, char value) {
super.addChar(name, value);
return this;
}
@Override
public Http2Headers addByte(ByteString name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public Http2Headers addShort(ByteString name, short value) {
super.addShort(name, value);
return this;
}
@Override
public Http2Headers addInt(ByteString name, int value) {
super.addInt(name, value);
return this;
}
@Override
public Http2Headers addLong(ByteString name, long value) {
super.addLong(name, value);
return this;
}
@Override
public Http2Headers addFloat(ByteString name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public Http2Headers addDouble(ByteString name, double value) {
super.addDouble(name, value);
return this;
}
@Override
public Http2Headers addTimeMillis(ByteString name, long value) {
super.addTimeMillis(name, value);
return this;
}
@Override
public Http2Headers add(Headers<? extends ByteString> headers) {
super.add(headers);
return this;
}
@Override
public Http2Headers set(ByteString name, ByteString value) {
super.set(name, value);
return this;
}
@Override
public Http2Headers set(ByteString name, Iterable<? extends ByteString> values) {
super.set(name, values);
return this;
}
@Override
public Http2Headers set(ByteString name, ByteString... values) {
super.set(name, values);
return this;
}
@Override
public Http2Headers setObject(ByteString name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public Http2Headers setObject(ByteString name, Iterable<?> values) {
super.setObject(name, values);
return this;
}
@Override
public Http2Headers setObject(ByteString name, Object... values) {
super.setObject(name, values);
return this;
}
@Override
public Http2Headers setBoolean(ByteString name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public Http2Headers setChar(ByteString name, char value) {
super.setChar(name, value);
return this;
}
@Override
public Http2Headers setByte(ByteString name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public Http2Headers setShort(ByteString name, short value) {
super.setShort(name, value);
return this;
}
@Override
public Http2Headers setInt(ByteString name, int value) {
super.setInt(name, value);
return this;
}
@Override
public Http2Headers setLong(ByteString name, long value) {
super.setLong(name, value);
return this;
}
@Override
public Http2Headers setFloat(ByteString name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public Http2Headers setDouble(ByteString name, double value) {
super.setDouble(name, value);
return this;
}
@Override
public Http2Headers setTimeMillis(ByteString name, long value) {
super.setTimeMillis(name, value);
return this;
}
@Override
public Http2Headers set(Headers<? extends ByteString> headers) {
super.set(headers);
return this;
}
@Override
public Http2Headers setAll(Headers<? extends ByteString> headers) {
super.setAll(headers);
return this;
}
@Override
public Http2Headers clear() {
super.clear();
return this;
}
@Override
public EmptyHttp2Headers method(ByteString method) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public EmptyHttp2Headers scheme(ByteString status) { public EmptyHttp2Headers scheme(CharSequence status) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public EmptyHttp2Headers authority(ByteString authority) { public EmptyHttp2Headers authority(CharSequence authority) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public EmptyHttp2Headers path(ByteString path) { public EmptyHttp2Headers path(CharSequence path) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public EmptyHttp2Headers status(ByteString status) { public EmptyHttp2Headers status(CharSequence status) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public ByteString method() { public CharSequence method() {
return get(PseudoHeaderName.METHOD.value()); return get(PseudoHeaderName.METHOD.value());
} }
@Override @Override
public ByteString scheme() { public CharSequence scheme() {
return get(PseudoHeaderName.SCHEME.value()); return get(PseudoHeaderName.SCHEME.value());
} }
@Override @Override
public ByteString authority() { public CharSequence authority() {
return get(PseudoHeaderName.AUTHORITY.value()); return get(PseudoHeaderName.AUTHORITY.value());
} }
@Override @Override
public ByteString path() { public CharSequence path() {
return get(PseudoHeaderName.PATH.value()); return get(PseudoHeaderName.PATH.value());
} }
@Override @Override
public ByteString status() { public CharSequence status() {
return get(PseudoHeaderName.STATUS.value()); return get(PseudoHeaderName.STATUS.value());
} }
} }

View File

@ -15,19 +15,16 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import java.util.HashSet; import io.netty.handler.codec.Headers;
import io.netty.util.AsciiString;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set;
import io.netty.handler.codec.Headers;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil;
/** /**
* A collection of headers sent or received via HTTP/2. * A collection of headers sent or received via HTTP/2.
*/ */
public interface Http2Headers extends Headers<ByteString> { public interface Http2Headers extends Headers<CharSequence, CharSequence, Http2Headers> {
/** /**
* HTTP/2 pseudo-headers names. * HTTP/2 pseudo-headers names.
@ -58,19 +55,19 @@ public interface Http2Headers extends Headers<ByteString> {
*/ */
STATUS(":status"); STATUS(":status");
private final ByteString value; private final AsciiString value;
private static final Set<ByteString> PSEUDO_HEADERS = new HashSet<ByteString>(); private static final CharSequenceMap<AsciiString> PSEUDO_HEADERS = new CharSequenceMap<AsciiString>();
static { static {
for (PseudoHeaderName pseudoHeader : PseudoHeaderName.values()) { for (PseudoHeaderName pseudoHeader : PseudoHeaderName.values()) {
PSEUDO_HEADERS.add(pseudoHeader.value()); PSEUDO_HEADERS.add(pseudoHeader.value(), AsciiString.EMPTY_STRING);
} }
} }
PseudoHeaderName(String value) { PseudoHeaderName(String value) {
this.value = new ByteString(value, CharsetUtil.UTF_8); this.value = new AsciiString(value);
} }
public ByteString value() { public AsciiString value() {
// Return a slice so that the buffer gets its own reader index. // Return a slice so that the buffer gets its own reader index.
return value; return value;
} }
@ -78,168 +75,66 @@ public interface Http2Headers extends Headers<ByteString> {
/** /**
* Indicates whether the given header name is a valid HTTP/2 pseudo header. * Indicates whether the given header name is a valid HTTP/2 pseudo header.
*/ */
public static boolean isPseudoHeader(ByteString header) { public static boolean isPseudoHeader(CharSequence header) {
return PSEUDO_HEADERS.contains(header); return PSEUDO_HEADERS.contains(header);
} }
} }
@Override
Http2Headers add(ByteString name, ByteString value);
@Override
Http2Headers add(ByteString name, Iterable<? extends ByteString> values);
@Override
Http2Headers add(ByteString name, ByteString... values);
@Override
Http2Headers addObject(ByteString name, Object value);
@Override
Http2Headers addObject(ByteString name, Iterable<?> values);
@Override
Http2Headers addObject(ByteString name, Object... values);
@Override
Http2Headers addBoolean(ByteString name, boolean value);
@Override
Http2Headers addByte(ByteString name, byte value);
@Override
Http2Headers addChar(ByteString name, char value);
@Override
Http2Headers addShort(ByteString name, short value);
@Override
Http2Headers addInt(ByteString name, int value);
@Override
Http2Headers addLong(ByteString name, long value);
@Override
Http2Headers addFloat(ByteString name, float value);
@Override
Http2Headers addDouble(ByteString name, double value);
@Override
Http2Headers addTimeMillis(ByteString name, long value);
@Override
Http2Headers add(Headers<? extends ByteString> headers);
@Override
Http2Headers set(ByteString name, ByteString value);
@Override
Http2Headers set(ByteString name, Iterable<? extends ByteString> values);
@Override
Http2Headers set(ByteString name, ByteString... values);
@Override
Http2Headers setObject(ByteString name, Object value);
@Override
Http2Headers setObject(ByteString name, Iterable<?> values);
@Override
Http2Headers setObject(ByteString name, Object... values);
@Override
Http2Headers setBoolean(ByteString name, boolean value);
@Override
Http2Headers setByte(ByteString name, byte value);
@Override
Http2Headers setChar(ByteString name, char value);
@Override
Http2Headers setShort(ByteString name, short value);
@Override
Http2Headers setInt(ByteString name, int value);
@Override
Http2Headers setLong(ByteString name, long value);
@Override
Http2Headers setFloat(ByteString name, float value);
@Override
Http2Headers setDouble(ByteString name, double value);
@Override
Http2Headers setTimeMillis(ByteString name, long value);
@Override
Http2Headers set(Headers<? extends ByteString> headers);
@Override
Http2Headers setAll(Headers<? extends ByteString> headers);
@Override
Http2Headers clear();
/** /**
* Returns an iterator over all HTTP/2 headers. The iteration order is as follows: * Returns an iterator over all HTTP/2 headers. The iteration order is as follows:
* 1. All pseudo headers (order not specified). * 1. All pseudo headers (order not specified).
* 2. All non-pseudo headers (in insertion order). * 2. All non-pseudo headers (in insertion order).
*/ */
@Override @Override
Iterator<Entry<ByteString, ByteString>> iterator(); Iterator<Entry<CharSequence, CharSequence>> iterator();
/** /**
* Sets the {@link PseudoHeaderName#METHOD} header or {@code null} if there is no such header * Sets the {@link PseudoHeaderName#METHOD} header or {@code null} if there is no such header
*/ */
Http2Headers method(ByteString value); Http2Headers method(CharSequence value);
/** /**
* Sets the {@link PseudoHeaderName#SCHEME} header if there is no such header * Sets the {@link PseudoHeaderName#SCHEME} header if there is no such header
*/ */
Http2Headers scheme(ByteString value); Http2Headers scheme(CharSequence value);
/** /**
* Sets the {@link PseudoHeaderName#AUTHORITY} header or {@code null} if there is no such header * Sets the {@link PseudoHeaderName#AUTHORITY} header or {@code null} if there is no such header
*/ */
Http2Headers authority(ByteString value); Http2Headers authority(CharSequence value);
/** /**
* Sets the {@link PseudoHeaderName#PATH} header or {@code null} if there is no such header * Sets the {@link PseudoHeaderName#PATH} header or {@code null} if there is no such header
*/ */
Http2Headers path(ByteString value); Http2Headers path(CharSequence value);
/** /**
* Sets the {@link PseudoHeaderName#STATUS} header or {@code null} if there is no such header * Sets the {@link PseudoHeaderName#STATUS} header or {@code null} if there is no such header
*/ */
Http2Headers status(ByteString value); Http2Headers status(CharSequence value);
/** /**
* Gets the {@link PseudoHeaderName#METHOD} header or {@code null} if there is no such header * Gets the {@link PseudoHeaderName#METHOD} header or {@code null} if there is no such header
*/ */
ByteString method(); CharSequence method();
/** /**
* Gets the {@link PseudoHeaderName#SCHEME} header or {@code null} if there is no such header * Gets the {@link PseudoHeaderName#SCHEME} header or {@code null} if there is no such header
*/ */
ByteString scheme(); CharSequence scheme();
/** /**
* Gets the {@link PseudoHeaderName#AUTHORITY} header or {@code null} if there is no such header * Gets the {@link PseudoHeaderName#AUTHORITY} header or {@code null} if there is no such header
*/ */
ByteString authority(); CharSequence authority();
/** /**
* Gets the {@link PseudoHeaderName#PATH} header or {@code null} if there is no such header * Gets the {@link PseudoHeaderName#PATH} header or {@code null} if there is no such header
*/ */
ByteString path(); CharSequence path();
/** /**
* Gets the {@link PseudoHeaderName#STATUS} header or {@code null} if there is no such header * Gets the {@link PseudoHeaderName#STATUS} header or {@code null} if there is no such header
*/ */
ByteString status(); CharSequence status();
} }

View File

@ -16,7 +16,6 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.util.ByteString;
/** /**
* Encodes {@link Http2Headers} into HPACK-encoded headers blocks. * Encodes {@link Http2Headers} into HPACK-encoded headers blocks.
@ -47,7 +46,7 @@ public interface Http2HeadersEncoder {
* <a href="http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-7.1.3">sensitive</a>. * <a href="http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12#section-7.1.3">sensitive</a>.
* {@code false} otherwise. * {@code false} otherwise.
*/ */
boolean isSensitive(ByteString name, ByteString value); boolean isSensitive(CharSequence name, CharSequence value);
} }
/** /**
@ -68,7 +67,7 @@ public interface Http2HeadersEncoder {
*/ */
SensitivityDetector NEVER_SENSITIVE = new SensitivityDetector() { SensitivityDetector NEVER_SENSITIVE = new SensitivityDetector() {
@Override @Override
public boolean isSensitive(ByteString name, ByteString value) { public boolean isSensitive(CharSequence name, CharSequence value) {
return false; return false;
} }
}; };

View File

@ -20,7 +20,6 @@ import io.netty.handler.codec.http.FullHttpMessage;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMessage; import io.netty.handler.codec.http.HttpMessage;
@ -28,17 +27,13 @@ import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.ByteString;
import java.net.URI; import java.net.URI;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set;
import static io.netty.handler.codec.http.HttpScheme.HTTP; import static io.netty.handler.codec.http.HttpScheme.HTTP;
import static io.netty.handler.codec.http.HttpScheme.HTTPS; import static io.netty.handler.codec.http.HttpScheme.HTTPS;
@ -47,6 +42,7 @@ import static io.netty.handler.codec.http.HttpUtil.isOriginForm;
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import static io.netty.handler.codec.http2.Http2Exception.connectionError; import static io.netty.handler.codec.http2.Http2Exception.connectionError;
import static io.netty.handler.codec.http2.Http2Exception.streamError; import static io.netty.handler.codec.http2.Http2Exception.streamError;
import static io.netty.util.AsciiString.EMPTY_STRING;
import static io.netty.util.internal.ObjectUtil.checkNotNull; import static io.netty.util.internal.ObjectUtil.checkNotNull;
import static io.netty.util.internal.StringUtil.isNullOrEmpty; import static io.netty.util.internal.StringUtil.isNullOrEmpty;
import static io.netty.util.internal.StringUtil.length; import static io.netty.util.internal.StringUtil.length;
@ -58,21 +54,23 @@ public final class HttpConversionUtil {
/** /**
* The set of headers that should not be directly copied when converting headers from HTTP to HTTP/2. * The set of headers that should not be directly copied when converting headers from HTTP to HTTP/2.
*/ */
private static final CharSequenceMap<AsciiString> HTTP_TO_HTTP2_HEADER_BLACKLIST =
new CharSequenceMap<AsciiString>();
static {
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.CONNECTION, EMPTY_STRING);
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private static final Set<CharSequence> HTTP_TO_HTTP2_HEADER_BLACKLIST = new HashSet<CharSequence>() { AsciiString keepAlive = HttpHeaderNames.KEEP_ALIVE;
private static final long serialVersionUID = -5678614530214167043L; HTTP_TO_HTTP2_HEADER_BLACKLIST.add(keepAlive, EMPTY_STRING);
{ @SuppressWarnings("deprecation")
add(HttpHeaderNames.CONNECTION); AsciiString proxyConnection = HttpHeaderNames.PROXY_CONNECTION;
add(HttpHeaderNames.KEEP_ALIVE); HTTP_TO_HTTP2_HEADER_BLACKLIST.add(proxyConnection, EMPTY_STRING);
add(HttpHeaderNames.PROXY_CONNECTION); HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.TRANSFER_ENCODING, EMPTY_STRING);
add(HttpHeaderNames.TRANSFER_ENCODING); HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.HOST, EMPTY_STRING);
add(HttpHeaderNames.HOST); HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.UPGRADE, EMPTY_STRING);
add(HttpHeaderNames.UPGRADE); HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.STREAM_ID.text(), EMPTY_STRING);
add(ExtensionHeaderNames.STREAM_ID.text()); HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.SCHEME.text(), EMPTY_STRING);
add(ExtensionHeaderNames.SCHEME.text()); HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.PATH.text(), EMPTY_STRING);
add(ExtensionHeaderNames.PATH.text());
} }
};
/** /**
* This will be the method used for {@link HttpRequest} objects generated out of the HTTP message flow defined in <a * This will be the method used for {@link HttpRequest} objects generated out of the HTTP message flow defined in <a
@ -166,7 +164,7 @@ public final class HttpConversionUtil {
* @return The HTTP/1.x status * @return The HTTP/1.x status
* @throws Http2Exception If there is a problem translating from HTTP/2 to HTTP/1.x * @throws Http2Exception If there is a problem translating from HTTP/2 to HTTP/1.x
*/ */
public static HttpResponseStatus parseStatus(ByteString status) throws Http2Exception { public static HttpResponseStatus parseStatus(CharSequence status) throws Http2Exception {
HttpResponseStatus result; HttpResponseStatus result;
try { try {
result = HttpResponseStatus.parseLine(status); result = HttpResponseStatus.parseLine(status);
@ -219,9 +217,9 @@ public final class HttpConversionUtil {
public static FullHttpRequest toHttpRequest(int streamId, Http2Headers http2Headers, boolean validateHttpHeaders) public static FullHttpRequest toHttpRequest(int streamId, Http2Headers http2Headers, boolean validateHttpHeaders)
throws Http2Exception { throws Http2Exception {
// HTTP/2 does not define a way to carry the version identifier that is included in the HTTP/1.1 request line. // HTTP/2 does not define a way to carry the version identifier that is included in the HTTP/1.1 request line.
final ByteString method = checkNotNull(http2Headers.method(), final CharSequence method = checkNotNull(http2Headers.method(),
"method header cannot be null in conversion to HTTP/1.x"); "method header cannot be null in conversion to HTTP/1.x");
final ByteString path = checkNotNull(http2Headers.path(), final CharSequence path = checkNotNull(http2Headers.path(),
"path header cannot be null in conversion to HTTP/1.x"); "path header cannot be null in conversion to HTTP/1.x");
FullHttpRequest msg = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(method FullHttpRequest msg = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(method
.toString()), path.toString(), validateHttpHeaders); .toString()), path.toString(), validateHttpHeaders);
@ -263,7 +261,7 @@ public final class HttpConversionUtil {
HttpVersion httpVersion, boolean isTrailer, boolean isRequest) throws Http2Exception { HttpVersion httpVersion, boolean isTrailer, boolean isRequest) throws Http2Exception {
Http2ToHttpHeaderTranslator translator = new Http2ToHttpHeaderTranslator(streamId, outputHeaders, isRequest); Http2ToHttpHeaderTranslator translator = new Http2ToHttpHeaderTranslator(streamId, outputHeaders, isRequest);
try { try {
for (Entry<ByteString, ByteString> entry : inputHeaders) { for (Entry<CharSequence, CharSequence> entry : inputHeaders) {
translator.translate(entry); translator.translate(entry);
} }
} catch (Http2Exception ex) { } catch (Http2Exception ex) {
@ -326,12 +324,10 @@ public final class HttpConversionUtil {
Entry<CharSequence, CharSequence> entry = iter.next(); Entry<CharSequence, CharSequence> entry = iter.next();
final AsciiString aName = AsciiString.of(entry.getKey()).toLowerCase(); final AsciiString aName = AsciiString.of(entry.getKey()).toLowerCase();
if (!HTTP_TO_HTTP2_HEADER_BLACKLIST.contains(aName)) { if (!HTTP_TO_HTTP2_HEADER_BLACKLIST.contains(aName)) {
AsciiString aValue = AsciiString.of(entry.getValue()); // https://tools.ietf.org/html/rfc7540#section-8.1.2.2 makes a special exception for TE
// https://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-8.1.2.2
// makes a special exception for TE
if (!aName.contentEqualsIgnoreCase(HttpHeaderNames.TE) || if (!aName.contentEqualsIgnoreCase(HttpHeaderNames.TE) ||
aValue.contentEqualsIgnoreCase(HttpHeaderValues.TRAILERS)) { AsciiString.contentEqualsIgnoreCase(entry.getValue(), HttpHeaderValues.TRAILERS)) {
out.add(aName, aValue); out.add(aName, AsciiString.of(entry.getValue()));
} }
} }
} }
@ -405,23 +401,23 @@ public final class HttpConversionUtil {
/** /**
* Translations from HTTP/2 header name to the HTTP/1.x equivalent. * Translations from HTTP/2 header name to the HTTP/1.x equivalent.
*/ */
private static final Map<ByteString, ByteString> private static final CharSequenceMap<AsciiString>
REQUEST_HEADER_TRANSLATIONS = new HashMap<ByteString, ByteString>(); REQUEST_HEADER_TRANSLATIONS = new CharSequenceMap<AsciiString>();
private static final Map<ByteString, ByteString> private static final CharSequenceMap<AsciiString>
RESPONSE_HEADER_TRANSLATIONS = new HashMap<ByteString, ByteString>(); RESPONSE_HEADER_TRANSLATIONS = new CharSequenceMap<AsciiString>();
static { static {
RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.AUTHORITY.value(), RESPONSE_HEADER_TRANSLATIONS.add(Http2Headers.PseudoHeaderName.AUTHORITY.value(),
HttpHeaderNames.HOST); HttpHeaderNames.HOST);
RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.SCHEME.value(), RESPONSE_HEADER_TRANSLATIONS.add(Http2Headers.PseudoHeaderName.SCHEME.value(),
ExtensionHeaderNames.SCHEME.text()); ExtensionHeaderNames.SCHEME.text());
REQUEST_HEADER_TRANSLATIONS.putAll(RESPONSE_HEADER_TRANSLATIONS); REQUEST_HEADER_TRANSLATIONS.add(RESPONSE_HEADER_TRANSLATIONS);
RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.PATH.value(), RESPONSE_HEADER_TRANSLATIONS.add(Http2Headers.PseudoHeaderName.PATH.value(),
ExtensionHeaderNames.PATH.text()); ExtensionHeaderNames.PATH.text());
} }
private final int streamId; private final int streamId;
private final HttpHeaders output; private final HttpHeaders output;
private final Map<ByteString, ByteString> translations; private final CharSequenceMap<AsciiString> translations;
/** /**
* Create a new instance * Create a new instance
@ -436,23 +432,20 @@ public final class HttpConversionUtil {
translations = request ? REQUEST_HEADER_TRANSLATIONS : RESPONSE_HEADER_TRANSLATIONS; translations = request ? REQUEST_HEADER_TRANSLATIONS : RESPONSE_HEADER_TRANSLATIONS;
} }
public void translate(Entry<ByteString, ByteString> entry) throws Http2Exception { public void translate(Entry<CharSequence, CharSequence> entry) throws Http2Exception {
final ByteString name = entry.getKey(); final CharSequence name = entry.getKey();
final ByteString value = entry.getValue(); final CharSequence value = entry.getValue();
ByteString translatedName = translations.get(name); AsciiString translatedName = translations.get(name);
if (translatedName != null || !Http2Headers.PseudoHeaderName.isPseudoHeader(name)) { if (translatedName != null) {
if (translatedName == null) { output.add(translatedName, AsciiString.of(value));
translatedName = name; } else if (!Http2Headers.PseudoHeaderName.isPseudoHeader(name)) {
} // https://tools.ietf.org/html/rfc7540#section-8.1.2.3
// http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-8.1.2.3
// All headers that start with ':' are only valid in HTTP/2 context // All headers that start with ':' are only valid in HTTP/2 context
if (translatedName.isEmpty() || translatedName.byteAt(0) == ':') { if (name.length() == 0 || name.charAt(0) == ':') {
throw streamError(streamId, PROTOCOL_ERROR, throw streamError(streamId, PROTOCOL_ERROR,
"Invalid HTTP/2 header '%s' encountered in translation to HTTP/1.x", translatedName); "Invalid HTTP/2 header '%s' encountered in translation to HTTP/1.x", name);
} else { }
output.add(new AsciiString(translatedName, false), new AsciiString(value, false)); output.add(AsciiString.of(name), AsciiString.of(value));
}
} }
} }
} }

View File

@ -15,16 +15,11 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import static io.netty.handler.codec.http2.Http2CodecUtil.SMALLEST_MAX_CONCURRENT_STREAMS;
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.util.ByteString;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import java.util.ArrayDeque; import java.util.ArrayDeque;
@ -33,6 +28,10 @@ import java.util.Map;
import java.util.Queue; import java.util.Queue;
import java.util.TreeMap; import java.util.TreeMap;
import static io.netty.handler.codec.http2.Http2CodecUtil.SMALLEST_MAX_CONCURRENT_STREAMS;
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
/** /**
* Implementation of a {@link Http2ConnectionEncoder} that dispatches all method call to another * Implementation of a {@link Http2ConnectionEncoder} that dispatches all method call to another
* {@link Http2ConnectionEncoder}, until {@code SETTINGS_MAX_CONCURRENT_STREAMS} is reached. * {@link Http2ConnectionEncoder}, until {@code SETTINGS_MAX_CONCURRENT_STREAMS} is reached.
@ -72,9 +71,9 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
private static final long serialVersionUID = 1326785622777291198L; private static final long serialVersionUID = 1326785622777291198L;
private final int lastStreamId; private final int lastStreamId;
private final long errorCode; private final long errorCode;
private final ByteString debugData; private final byte[] debugData;
public Http2GoAwayException(int lastStreamId, long errorCode, ByteString debugData) { public Http2GoAwayException(int lastStreamId, long errorCode, byte[] debugData) {
super(Http2Error.STREAM_CLOSED); super(Http2Error.STREAM_CLOSED);
this.lastStreamId = lastStreamId; this.lastStreamId = lastStreamId;
this.errorCode = errorCode; this.errorCode = errorCode;
@ -89,7 +88,7 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
return errorCode; return errorCode;
} }
public ByteString debugData() { public byte[] debugData() {
return debugData; return debugData;
} }
} }
@ -241,8 +240,7 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
private void cancelGoAwayStreams(int lastStreamId, long errorCode, ByteBuf debugData) { private void cancelGoAwayStreams(int lastStreamId, long errorCode, ByteBuf debugData) {
Iterator<PendingStream> iter = pendingStreams.values().iterator(); Iterator<PendingStream> iter = pendingStreams.values().iterator();
Exception e = new Http2GoAwayException(lastStreamId, errorCode, Exception e = new Http2GoAwayException(lastStreamId, errorCode, ByteBufUtil.getBytes(debugData));
new ByteString(ByteBufUtil.getBytes(debugData), false));
while (iter.hasNext()) { while (iter.hasNext()) {
PendingStream stream = iter.next(); PendingStream stream = iter.next();
if (stream.streamId > lastStreamId) { if (stream.streamId > lastStreamId) {

View File

@ -25,7 +25,6 @@ import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import org.junit.After; import org.junit.After;
@ -381,7 +380,7 @@ public class DefaultHttp2FrameIOTest {
} }
private Http2Headers headersOfSize(final int minSize) { private Http2Headers headersOfSize(final int minSize) {
final ByteString singleByte = new ByteString(new byte[]{0}); final AsciiString singleByte = new AsciiString(new byte[]{0}, false);
DefaultHttp2Headers headers = new DefaultHttp2Headers(false); DefaultHttp2Headers headers = new DefaultHttp2Headers(false);
for (int size = 0; size < minSize; size += 2) { for (int size = 0; size < minSize; size += 2) {
headers.add(singleByte, singleByte); headers.add(singleByte, singleByte);

View File

@ -17,17 +17,16 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName; import io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName;
import io.netty.util.ByteString; import io.netty.util.AsciiString;
import org.junit.Test; import org.junit.Test;
import java.util.HashSet;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set;
import static io.netty.util.ByteString.fromAscii; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static io.netty.util.AsciiString.of;
public class DefaultHttp2HeadersTest { public class DefaultHttp2HeadersTest {
@ -43,23 +42,26 @@ public class DefaultHttp2HeadersTest {
public void pseudoHeadersWithRemovePreservesPseudoIterationOrder() { public void pseudoHeadersWithRemovePreservesPseudoIterationOrder() {
Http2Headers headers = newHeaders(); Http2Headers headers = newHeaders();
Set<ByteString> nonPseudoHeaders = new HashSet<ByteString>(headers.size()); Http2Headers nonPseudoHeaders = new DefaultHttp2Headers();
for (Entry<ByteString, ByteString> entry : headers) { for (Entry<CharSequence, CharSequence> entry : headers) {
if (entry.getKey().isEmpty() || entry.getKey().byteAt(0) != ':') { if (entry.getKey().length() == 0 || entry.getKey().charAt(0) != ':' &&
nonPseudoHeaders.add(entry.getKey()); !nonPseudoHeaders.contains(entry.getKey())) {
nonPseudoHeaders.add(entry.getKey(), entry.getValue());
} }
} }
assertFalse(nonPseudoHeaders.isEmpty());
// Remove all the non-pseudo headers and verify // Remove all the non-pseudo headers and verify
for (ByteString nonPseudoHeader : nonPseudoHeaders) { for (Entry<CharSequence, CharSequence> nonPseudoHeaderEntry : nonPseudoHeaders) {
assertTrue(headers.remove(nonPseudoHeader)); assertTrue(headers.remove(nonPseudoHeaderEntry.getKey()));
verifyPseudoHeadersFirst(headers); verifyPseudoHeadersFirst(headers);
verifyAllPseudoHeadersPresent(headers); verifyAllPseudoHeadersPresent(headers);
} }
// Add back all non-pseudo headers // Add back all non-pseudo headers
for (ByteString nonPseudoHeader : nonPseudoHeaders) { for (Entry<CharSequence, CharSequence> nonPseudoHeaderEntry : nonPseudoHeaders) {
headers.add(nonPseudoHeader, fromAscii("goo")); headers.add(nonPseudoHeaderEntry.getKey(), of("goo"));
verifyPseudoHeadersFirst(headers); verifyPseudoHeadersFirst(headers);
verifyAllPseudoHeadersPresent(headers); verifyAllPseudoHeadersPresent(headers);
} }
@ -69,7 +71,7 @@ public class DefaultHttp2HeadersTest {
public void testHeaderNameValidation() { public void testHeaderNameValidation() {
Http2Headers headers = newHeaders(); Http2Headers headers = newHeaders();
headers.add(fromAscii("Foo"), fromAscii("foo")); headers.add(of("Foo"), of("foo"));
} }
private static void verifyAllPseudoHeadersPresent(Http2Headers headers) { private static void verifyAllPseudoHeadersPresent(Http2Headers headers) {
@ -79,9 +81,9 @@ public class DefaultHttp2HeadersTest {
} }
private static void verifyPseudoHeadersFirst(Http2Headers headers) { private static void verifyPseudoHeadersFirst(Http2Headers headers) {
ByteString lastNonPseudoName = null; CharSequence lastNonPseudoName = null;
for (Entry<ByteString, ByteString> entry: headers) { for (Entry<CharSequence, CharSequence> entry: headers) {
if (entry.getKey().isEmpty() || entry.getKey().byteAt(0) != ':') { if (entry.getKey().length() == 0 || entry.getKey().charAt(0) != ':') {
lastNonPseudoName = entry.getKey(); lastNonPseudoName = entry.getKey();
} else if (lastNonPseudoName != null) { } else if (lastNonPseudoName != null) {
fail("All pseudo headers must be fist in iteration. Pseudo header " + entry.getKey() + fail("All pseudo headers must be fist in iteration. Pseudo header " + entry.getKey() +
@ -92,14 +94,14 @@ public class DefaultHttp2HeadersTest {
private static Http2Headers newHeaders() { private static Http2Headers newHeaders() {
Http2Headers headers = new DefaultHttp2Headers(); Http2Headers headers = new DefaultHttp2Headers();
headers.add(fromAscii("name1"), fromAscii("value1"), fromAscii("value2")); headers.add(of("name1"), of("value1"), of("value2"));
headers.method(fromAscii("POST")); headers.method(of("POST"));
headers.add(fromAscii("2name"), fromAscii("value3")); headers.add(of("2name"), of("value3"));
headers.path(fromAscii("/index.html")); headers.path(of("/index.html"));
headers.status(fromAscii("200")); headers.status(of("200"));
headers.authority(fromAscii("netty.io")); headers.authority(of("netty.io"));
headers.add(fromAscii("name3"), fromAscii("value4")); headers.add(of("name3"), of("value4"));
headers.scheme(fromAscii("https")); headers.scheme(of("https"));
return headers; return headers;
} }
} }

View File

@ -19,7 +19,6 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.ByteString;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
@ -71,8 +70,8 @@ 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 AsciiString randomString() {
return new ByteString(randomBytes()); return new AsciiString(randomBytes());
} }
public static CharSequence of(String s) { public static CharSequence of(String s) {

View File

@ -18,7 +18,6 @@ package io.netty.handler.codec.stomp;
import io.netty.handler.codec.CharSequenceValueConverter; import io.netty.handler.codec.CharSequenceValueConverter;
import io.netty.handler.codec.DefaultHeaders; import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.Headers;
import io.netty.handler.codec.HeadersUtils; import io.netty.handler.codec.HeadersUtils;
import java.util.Iterator; import java.util.Iterator;
@ -28,215 +27,12 @@ import java.util.Map.Entry;
import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER; import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER;
import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER; import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER;
public class DefaultStompHeaders extends DefaultHeaders<CharSequence> implements StompHeaders { public class DefaultStompHeaders
extends DefaultHeaders<CharSequence, CharSequence, StompHeaders> implements StompHeaders {
public DefaultStompHeaders() { public DefaultStompHeaders() {
super(CASE_SENSITIVE_HASHER, CharSequenceValueConverter.INSTANCE); super(CASE_SENSITIVE_HASHER, CharSequenceValueConverter.INSTANCE);
} }
@Override
public StompHeaders add(CharSequence name, CharSequence value) {
super.add(name, value);
return this;
}
@Override
public StompHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
super.add(name, values);
return this;
}
@Override
public StompHeaders add(CharSequence name, CharSequence... values) {
super.add(name, values);
return this;
}
@Override
public StompHeaders addObject(CharSequence name, Object value) {
super.addObject(name, value);
return this;
}
@Override
public StompHeaders addObject(CharSequence name, Iterable<?> values) {
super.addObject(name, values);
return this;
}
@Override
public StompHeaders addObject(CharSequence name, Object... values) {
super.addObject(name, values);
return this;
}
@Override
public StompHeaders addBoolean(CharSequence name, boolean value) {
super.addBoolean(name, value);
return this;
}
@Override
public StompHeaders addChar(CharSequence name, char value) {
super.addChar(name, value);
return this;
}
@Override
public StompHeaders addByte(CharSequence name, byte value) {
super.addByte(name, value);
return this;
}
@Override
public StompHeaders addShort(CharSequence name, short value) {
super.addShort(name, value);
return this;
}
@Override
public StompHeaders addInt(CharSequence name, int value) {
super.addInt(name, value);
return this;
}
@Override
public StompHeaders addLong(CharSequence name, long value) {
super.addLong(name, value);
return this;
}
@Override
public StompHeaders addFloat(CharSequence name, float value) {
super.addFloat(name, value);
return this;
}
@Override
public StompHeaders addDouble(CharSequence name, double value) {
super.addDouble(name, value);
return this;
}
@Override
public StompHeaders addTimeMillis(CharSequence name, long value) {
super.addTimeMillis(name, value);
return this;
}
@Override
public StompHeaders add(Headers<? extends CharSequence> headers) {
super.add(headers);
return this;
}
@Override
public StompHeaders set(CharSequence name, CharSequence value) {
super.set(name, value);
return this;
}
@Override
public StompHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
super.set(name, values);
return this;
}
@Override
public StompHeaders set(CharSequence name, CharSequence... values) {
super.set(name, values);
return this;
}
@Override
public StompHeaders setObject(CharSequence name, Object value) {
super.setObject(name, value);
return this;
}
@Override
public StompHeaders setObject(CharSequence name, Iterable<?> values) {
super.setObject(name, values);
return this;
}
@Override
public StompHeaders setObject(CharSequence name, Object... values) {
super.setObject(name, values);
return this;
}
@Override
public StompHeaders setBoolean(CharSequence name, boolean value) {
super.setBoolean(name, value);
return this;
}
@Override
public StompHeaders setChar(CharSequence name, char value) {
super.setChar(name, value);
return this;
}
@Override
public StompHeaders setByte(CharSequence name, byte value) {
super.setByte(name, value);
return this;
}
@Override
public StompHeaders setShort(CharSequence name, short value) {
super.setShort(name, value);
return this;
}
@Override
public StompHeaders setInt(CharSequence name, int value) {
super.setInt(name, value);
return this;
}
@Override
public StompHeaders setLong(CharSequence name, long value) {
super.setLong(name, value);
return this;
}
@Override
public StompHeaders setFloat(CharSequence name, float value) {
super.setFloat(name, value);
return this;
}
@Override
public StompHeaders setDouble(CharSequence name, double value) {
super.setDouble(name, value);
return this;
}
@Override
public StompHeaders setTimeMillis(CharSequence name, long value) {
super.setTimeMillis(name, value);
return this;
}
@Override
public StompHeaders set(Headers<? extends CharSequence> headers) {
super.set(headers);
return this;
}
@Override
public StompHeaders setAll(Headers<? extends CharSequence> headers) {
super.setAll(headers);
return this;
}
@Override
public StompHeaders clear() {
super.clear();
return this;
}
@Override @Override
public String getAsString(CharSequence name) { public String getAsString(CharSequence name) {
return HeadersUtils.getAsString(this, name); return HeadersUtils.getAsString(this, name);

View File

@ -26,7 +26,7 @@ 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 Headers<CharSequence> { public interface StompHeaders extends Headers<CharSequence, CharSequence, StompHeaders> {
AsciiString ACCEPT_VERSION = new AsciiString("accept-version"); AsciiString ACCEPT_VERSION = new AsciiString("accept-version");
AsciiString HOST = new AsciiString("host"); AsciiString HOST = new AsciiString("host");
@ -48,108 +48,6 @@ public interface StompHeaders extends Headers<CharSequence> {
AsciiString CONTENT_LENGTH = new AsciiString("content-length"); AsciiString CONTENT_LENGTH = new AsciiString("content-length");
AsciiString CONTENT_TYPE = new AsciiString("content-type"); AsciiString CONTENT_TYPE = new AsciiString("content-type");
@Override
StompHeaders add(CharSequence name, CharSequence value);
@Override
StompHeaders add(CharSequence name, Iterable<? extends CharSequence> values);
@Override
StompHeaders add(CharSequence name, CharSequence... values);
@Override
StompHeaders addObject(CharSequence name, Object value);
@Override
StompHeaders addObject(CharSequence name, Iterable<?> values);
@Override
StompHeaders addObject(CharSequence name, Object... values);
@Override
StompHeaders addBoolean(CharSequence name, boolean value);
@Override
StompHeaders addByte(CharSequence name, byte value);
@Override
StompHeaders addChar(CharSequence name, char value);
@Override
StompHeaders addShort(CharSequence name, short value);
@Override
StompHeaders addInt(CharSequence name, int value);
@Override
StompHeaders addLong(CharSequence name, long value);
@Override
StompHeaders addFloat(CharSequence name, float value);
@Override
StompHeaders addDouble(CharSequence name, double value);
@Override
StompHeaders addTimeMillis(CharSequence name, long value);
@Override
StompHeaders add(Headers<? extends CharSequence> headers);
@Override
StompHeaders set(CharSequence name, CharSequence value);
@Override
StompHeaders set(CharSequence name, Iterable<? extends CharSequence> values);
@Override
StompHeaders set(CharSequence name, CharSequence... values);
@Override
StompHeaders setObject(CharSequence name, Object value);
@Override
StompHeaders setObject(CharSequence name, Iterable<?> values);
@Override
StompHeaders setObject(CharSequence name, Object... values);
@Override
StompHeaders setBoolean(CharSequence name, boolean value);
@Override
StompHeaders setByte(CharSequence name, byte value);
@Override
StompHeaders setChar(CharSequence name, char value);
@Override
StompHeaders setShort(CharSequence name, short value);
@Override
StompHeaders setInt(CharSequence name, int value);
@Override
StompHeaders setLong(CharSequence name, long value);
@Override
StompHeaders setFloat(CharSequence name, float value);
@Override
StompHeaders setDouble(CharSequence name, double value);
@Override
StompHeaders setTimeMillis(CharSequence name, long value);
@Override
StompHeaders set(Headers<? extends CharSequence> headers);
@Override
StompHeaders setAll(Headers<? extends CharSequence> headers);
@Override
StompHeaders clear();
/** /**
* {@link Headers#get(Object)} and convert the result to a {@link String}. * {@link Headers#get(Object)} and convert the result to a {@link String}.
* @param name the name of the header to retrieve * @param name the name of the header to retrieve

View File

@ -1,140 +0,0 @@
/*
* 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

@ -15,6 +15,7 @@
package io.netty.handler.codec; package io.netty.handler.codec;
import io.netty.handler.codec.DefaultHeaders.HeaderDateFormat; import io.netty.handler.codec.DefaultHeaders.HeaderDateFormat;
import io.netty.util.AsciiString;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
import java.text.ParseException; import java.text.ParseException;
@ -65,6 +66,9 @@ public class CharSequenceValueConverter implements ValueConverter<CharSequence>
@Override @Override
public boolean convertToBoolean(CharSequence value) { public boolean convertToBoolean(CharSequence value) {
if (value instanceof AsciiString) {
return ((AsciiString) value).parseBoolean();
}
return Boolean.parseBoolean(value.toString()); return Boolean.parseBoolean(value.toString());
} }
@ -75,14 +79,14 @@ public class CharSequenceValueConverter implements ValueConverter<CharSequence>
@Override @Override
public byte convertToByte(CharSequence value) { public byte convertToByte(CharSequence value) {
if (value instanceof AsciiString) {
return ((AsciiString) value).byteAt(0);
}
return Byte.valueOf(value.toString()); return Byte.valueOf(value.toString());
} }
@Override @Override
public char convertToChar(CharSequence value) { public char convertToChar(CharSequence value) {
if (value.length() == 0) {
throw new IllegalArgumentException("'value' is empty.");
}
return value.charAt(0); return value.charAt(0);
} }
@ -93,16 +97,25 @@ public class CharSequenceValueConverter implements ValueConverter<CharSequence>
@Override @Override
public short convertToShort(CharSequence value) { public short convertToShort(CharSequence value) {
if (value instanceof AsciiString) {
return ((AsciiString) value).parseShort();
}
return Short.valueOf(value.toString()); return Short.valueOf(value.toString());
} }
@Override @Override
public int convertToInt(CharSequence value) { public int convertToInt(CharSequence value) {
if (value instanceof AsciiString) {
return ((AsciiString) value).parseInt();
}
return Integer.parseInt(value.toString()); return Integer.parseInt(value.toString());
} }
@Override @Override
public long convertToLong(CharSequence value) { public long convertToLong(CharSequence value) {
if (value instanceof AsciiString) {
return ((AsciiString) value).parseLong();
}
return Long.parseLong(value.toString()); return Long.parseLong(value.toString());
} }
@ -117,17 +130,23 @@ public class CharSequenceValueConverter implements ValueConverter<CharSequence>
return HeaderDateFormat.get().parse(value.toString()); return HeaderDateFormat.get().parse(value.toString());
} catch (ParseException e) { } catch (ParseException e) {
PlatformDependent.throwException(e); PlatformDependent.throwException(e);
}
return 0; return 0;
} }
}
@Override @Override
public float convertToFloat(CharSequence value) { public float convertToFloat(CharSequence value) {
if (value instanceof AsciiString) {
return ((AsciiString) value).parseFloat();
}
return Float.valueOf(value.toString()); return Float.valueOf(value.toString());
} }
@Override @Override
public double convertToDouble(CharSequence value) { public double convertToDouble(CharSequence value) {
if (value instanceof AsciiString) {
return ((AsciiString) value).parseDouble();
}
return Double.valueOf(value.toString()); return Double.valueOf(value.toString());
} }
} }

View File

@ -0,0 +1,29 @@
/*
* 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.util.HashingStrategy;
/**
* A concrete implementation of {@link DefaultHeaders} that allows for direct instantiation.
* @param <K> the type of the header name.
* @param <V> the type of the header value.
*/
public final class DefaultHeadersImpl<K, V> extends DefaultHeaders<K, V, DefaultHeadersImpl<K, V>> {
public DefaultHeadersImpl(HashingStrategy<K> nameHashingStrategy,
ValueConverter<V> valueConverter, NameValidator<K> nameValidator) {
super(nameHashingStrategy, valueConverter, nameValidator);
}
}

View File

@ -20,274 +20,276 @@ import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
public class EmptyHeaders<T> implements Headers<T> { import static io.netty.handler.codec.DefaultHeaders.HASH_CODE_SEED;
public class EmptyHeaders<K, V, T extends Headers<K, V, T>> implements Headers<K, V, T> {
@Override @Override
public T get(T name) { public V get(K name) {
return null; return null;
} }
@Override @Override
public T get(T name, T defaultValue) { public V get(K name, V defaultValue) {
return null; return null;
} }
@Override @Override
public T getAndRemove(T name) { public V getAndRemove(K name) {
return null; return null;
} }
@Override @Override
public T getAndRemove(T name, T defaultValue) { public V getAndRemove(K name, V defaultValue) {
return null; return null;
} }
@Override @Override
public List<T> getAll(T name) { public List<V> getAll(K name) {
return Collections.emptyList(); return Collections.emptyList();
} }
@Override @Override
public List<T> getAllAndRemove(T name) { public List<V> getAllAndRemove(K name) {
return Collections.emptyList(); return Collections.emptyList();
} }
@Override @Override
public Boolean getBoolean(T name) { public Boolean getBoolean(K name) {
return null; return null;
} }
@Override @Override
public boolean getBoolean(T name, boolean defaultValue) { public boolean getBoolean(K name, boolean defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Byte getByte(T name) { public Byte getByte(K name) {
return null; return null;
} }
@Override @Override
public byte getByte(T name, byte defaultValue) { public byte getByte(K name, byte defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Character getChar(T name) { public Character getChar(K name) {
return null; return null;
} }
@Override @Override
public char getChar(T name, char defaultValue) { public char getChar(K name, char defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Short getShort(T name) { public Short getShort(K name) {
return null; return null;
} }
@Override @Override
public short getShort(T name, short defaultValue) { public short getShort(K name, short defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Integer getInt(T name) { public Integer getInt(K name) {
return null; return null;
} }
@Override @Override
public int getInt(T name, int defaultValue) { public int getInt(K name, int defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Long getLong(T name) { public Long getLong(K name) {
return null; return null;
} }
@Override @Override
public long getLong(T name, long defaultValue) { public long getLong(K name, long defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Float getFloat(T name) { public Float getFloat(K name) {
return null; return null;
} }
@Override @Override
public float getFloat(T name, float defaultValue) { public float getFloat(K name, float defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Double getDouble(T name) { public Double getDouble(K name) {
return null; return null;
} }
@Override @Override
public double getDouble(T name, double defaultValue) { public double getDouble(K name, double defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Long getTimeMillis(T name) { public Long getTimeMillis(K name) {
return null; return null;
} }
@Override @Override
public long getTimeMillis(T name, long defaultValue) { public long getTimeMillis(K name, long defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Boolean getBooleanAndRemove(T name) { public Boolean getBooleanAndRemove(K name) {
return null; return null;
} }
@Override @Override
public boolean getBooleanAndRemove(T name, boolean defaultValue) { public boolean getBooleanAndRemove(K name, boolean defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Byte getByteAndRemove(T name) { public Byte getByteAndRemove(K name) {
return null; return null;
} }
@Override @Override
public byte getByteAndRemove(T name, byte defaultValue) { public byte getByteAndRemove(K name, byte defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Character getCharAndRemove(T name) { public Character getCharAndRemove(K name) {
return null; return null;
} }
@Override @Override
public char getCharAndRemove(T name, char defaultValue) { public char getCharAndRemove(K name, char defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Short getShortAndRemove(T name) { public Short getShortAndRemove(K name) {
return null; return null;
} }
@Override @Override
public short getShortAndRemove(T name, short defaultValue) { public short getShortAndRemove(K name, short defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Integer getIntAndRemove(T name) { public Integer getIntAndRemove(K name) {
return null; return null;
} }
@Override @Override
public int getIntAndRemove(T name, int defaultValue) { public int getIntAndRemove(K name, int defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Long getLongAndRemove(T name) { public Long getLongAndRemove(K name) {
return null; return null;
} }
@Override @Override
public long getLongAndRemove(T name, long defaultValue) { public long getLongAndRemove(K name, long defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Float getFloatAndRemove(T name) { public Float getFloatAndRemove(K name) {
return null; return null;
} }
@Override @Override
public float getFloatAndRemove(T name, float defaultValue) { public float getFloatAndRemove(K name, float defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Double getDoubleAndRemove(T name) { public Double getDoubleAndRemove(K name) {
return null; return null;
} }
@Override @Override
public double getDoubleAndRemove(T name, double defaultValue) { public double getDoubleAndRemove(K name, double defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public Long getTimeMillisAndRemove(T name) { public Long getTimeMillisAndRemove(K name) {
return null; return null;
} }
@Override @Override
public long getTimeMillisAndRemove(T name, long defaultValue) { public long getTimeMillisAndRemove(K name, long defaultValue) {
return defaultValue; return defaultValue;
} }
@Override @Override
public boolean contains(T name) { public boolean contains(K name) {
return false; return false;
} }
@Override @Override
public boolean contains(T name, T value) { public boolean contains(K name, V value) {
return false; return false;
} }
@Override @Override
public boolean containsObject(T name, Object value) { public boolean containsObject(K name, Object value) {
return false; return false;
} }
@Override @Override
public boolean containsBoolean(T name, boolean value) { public boolean containsBoolean(K name, boolean value) {
return false; return false;
} }
@Override @Override
public boolean containsByte(T name, byte value) { public boolean containsByte(K name, byte value) {
return false; return false;
} }
@Override @Override
public boolean containsChar(T name, char value) { public boolean containsChar(K name, char value) {
return false; return false;
} }
@Override @Override
public boolean containsShort(T name, short value) { public boolean containsShort(K name, short value) {
return false; return false;
} }
@Override @Override
public boolean containsInt(T name, int value) { public boolean containsInt(K name, int value) {
return false; return false;
} }
@Override @Override
public boolean containsLong(T name, long value) { public boolean containsLong(K name, long value) {
return false; return false;
} }
@Override @Override
public boolean containsFloat(T name, float value) { public boolean containsFloat(K name, float value) {
return false; return false;
} }
@Override @Override
public boolean containsDouble(T name, double value) { public boolean containsDouble(K name, double value) {
return false; return false;
} }
@Override @Override
public boolean containsTimeMillis(T name, long value) { public boolean containsTimeMillis(K name, long value) {
return false; return false;
} }
@ -302,188 +304,188 @@ public class EmptyHeaders<T> implements Headers<T> {
} }
@Override @Override
public Set<T> names() { public Set<K> names() {
return Collections.emptySet(); return Collections.emptySet();
} }
@Override @Override
public Headers<T> add(T name, T value) { public T add(K name, V value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> add(T name, Iterable<? extends T> values) { public T add(K name, Iterable<? extends V> values) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> add(T name, T... values) { public T add(K name, V... values) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> addObject(T name, Object value) { public T addObject(K name, Object value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> addObject(T name, Iterable<?> values) { public T addObject(K name, Iterable<?> values) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> addObject(T name, Object... values) { public T addObject(K name, Object... values) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> addBoolean(T name, boolean value) { public T addBoolean(K name, boolean value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> addByte(T name, byte value) { public T addByte(K name, byte value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> addChar(T name, char value) { public T addChar(K name, char value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> addShort(T name, short value) { public T addShort(K name, short value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> addInt(T name, int value) { public T addInt(K name, int value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> addLong(T name, long value) { public T addLong(K name, long value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> addFloat(T name, float value) { public T addFloat(K name, float value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> addDouble(T name, double value) { public T addDouble(K name, double value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> addTimeMillis(T name, long value) { public T addTimeMillis(K name, long value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> add(Headers<? extends T> headers) { public T add(Headers<? extends K, ? extends V, ?> headers) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> set(T name, T value) { public T set(K name, V value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> set(T name, Iterable<? extends T> values) { public T set(K name, Iterable<? extends V> values) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> set(T name, T... values) { public T set(K name, V... values) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> setObject(T name, Object value) { public T setObject(K name, Object value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> setObject(T name, Iterable<?> values) { public T setObject(K name, Iterable<?> values) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> setObject(T name, Object... values) { public T setObject(K name, Object... values) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> setBoolean(T name, boolean value) { public T setBoolean(K name, boolean value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> setByte(T name, byte value) { public T setByte(K name, byte value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> setChar(T name, char value) { public T setChar(K name, char value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> setShort(T name, short value) { public T setShort(K name, short value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> setInt(T name, int value) { public T setInt(K name, int value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> setLong(T name, long value) { public T setLong(K name, long value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> setFloat(T name, float value) { public T setFloat(K name, float value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> setDouble(T name, double value) { public T setDouble(K name, double value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> setTimeMillis(T name, long value) { public T setTimeMillis(K name, long value) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> set(Headers<? extends T> headers) { public T set(Headers<? extends K, ? extends V, ?> headers) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public Headers<T> setAll(Headers<? extends T> headers) { public T setAll(Headers<? extends K, ? extends V, ?> headers) {
throw new UnsupportedOperationException("read only"); throw new UnsupportedOperationException("read only");
} }
@Override @Override
public boolean remove(T name) { public boolean remove(K name) {
return false; return false;
} }
@Override @Override
public Headers<T> clear() { public T clear() {
return this; return thisT();
} }
@Override @Override
public Iterator<Entry<T, T>> iterator() { public Iterator<Entry<K, V>> iterator() {
List<Entry<T, T>> empty = Collections.emptyList(); List<Entry<K, V>> empty = Collections.emptyList();
return empty.iterator(); return empty.iterator();
} }
@ -493,17 +495,22 @@ public class EmptyHeaders<T> implements Headers<T> {
return false; return false;
} }
Headers<?> rhs = (Headers<?>) o; Headers<?, ?, ?> rhs = (Headers<?, ?, ?>) o;
return isEmpty() && rhs.isEmpty(); return isEmpty() && rhs.isEmpty();
} }
@Override @Override
public int hashCode() { public int hashCode() {
return 1; return HASH_CODE_SEED;
} }
@Override @Override
public String toString() { public String toString() {
return new StringBuilder(getClass().getSimpleName()).append('[').append(']').toString(); return new StringBuilder(getClass().getSimpleName()).append('[').append(']').toString();
} }
@SuppressWarnings("unchecked")
private T thisT() {
return (T) this;
}
} }

View File

@ -19,7 +19,15 @@ 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>> { /**
* Common interface for {@link Headers} which represents a mapping of key to value.
* Duplicate keys may be allowed by implementations.
*
* @param <K> the type of the header name.
* @param <V> the type of the header value.
* @param <T> the type to use for return values when the intention is to return {@code this} object.
*/
public interface Headers<K, V, T extends Headers<K, V, T>> extends Iterable<Entry<K, V>> {
/** /**
* 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.
@ -27,7 +35,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param name the name of the header to retrieve * @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 * @return the first header value if the header is found. {@code null} if there's no such header
*/ */
T get(T name); V get(K name);
/** /**
* 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,
@ -37,7 +45,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param defaultValue the default value * @param defaultValue the default value
* @return the first header value or {@code defaultValue} if there is no such header * @return the first header value or {@code defaultValue} if there is no such header
*/ */
T get(T name, T defaultValue); V get(K name, V defaultValue);
/** /**
* Returns the value of a header with the specified name and removes it from this object. If there is more than * Returns the value of a header with the specified name and removes it from this object. If there is more than
@ -46,7 +54,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param name the name of the header to retrieve * @param name the name of the header to retrieve
* @return the first header value or {@code null} if there is no such header * @return the first header value or {@code null} if there is no such header
*/ */
T getAndRemove(T name); V getAndRemove(K name);
/** /**
* Returns the value of a header with the specified name and removes it from this object. If there is more than * Returns the value of a header with the specified name and removes it from this object. If there is more than
@ -56,7 +64,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param defaultValue the default value * @param defaultValue the default value
* @return the first header value or {@code defaultValue} if there is no such header * @return the first header value or {@code defaultValue} if there is no such header
*/ */
T getAndRemove(T name, T defaultValue); V getAndRemove(K name, V defaultValue);
/** /**
* Returns all values for the header with the specified name. The returned {@link List} can't be modified. * Returns all values for the header with the specified name. The returned {@link List} can't be modified.
@ -64,7 +72,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param name the name of the header to retrieve * @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. * @return a {@link List} of header values or an empty {@link List} if no values are found.
*/ */
List<T> getAll(T name); List<V> getAll(K name);
/** /**
* Returns all values for the header with the specified name and removes them from this object. * Returns all values for the header with the specified name and removes them from this object.
@ -73,7 +81,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param name the name of the header to retrieve * @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. * @return a {@link List} of header values or an empty {@link List} if no values are found.
*/ */
List<T> getAllAndRemove(T name); List<V> getAllAndRemove(K name);
/** /**
* Returns the {@code boolean} value of a header with the specified name. If there is more than one value for the * Returns the {@code boolean} value of a header with the specified name. If there is more than one value for the
@ -83,7 +91,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @return the {@code boolean} value of the first value in insertion order or {@code null} if there is no such * @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}. * value or it can't be converted to {@code boolean}.
*/ */
Boolean getBoolean(T name); Boolean getBoolean(K name);
/** /**
* Returns the {@code boolean} value of a header with the specified name. If there is more than one value for the * Returns the {@code boolean} value of a header with the specified name. If there is more than one value for the
@ -94,7 +102,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* such value or it can't be converted to {@code boolean}. * such value or it can't be converted to {@code boolean}.
*/ */
boolean getBoolean(T name, boolean defaultValue); boolean getBoolean(K name, boolean defaultValue);
/** /**
* Returns the {@code byte} value of a header with the specified name. If there is more than one value for the * Returns the {@code byte} value of a header with the specified name. If there is more than one value for the
@ -104,7 +112,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @return the {@code byte} value of the first value in insertion order or {@code null} if there is no such * @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}. * value or it can't be converted to {@code byte}.
*/ */
Byte getByte(T name); Byte getByte(K name);
/** /**
* Returns the {@code byte} value of a header with the specified name. If there is more than one value for the * Returns the {@code byte} value of a header with the specified name. If there is more than one value for the
@ -115,7 +123,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* such value or it can't be converted to {@code byte}. * such value or it can't be converted to {@code byte}.
*/ */
byte getByte(T name, byte defaultValue); byte getByte(K name, byte defaultValue);
/** /**
* Returns the {@code char} value of a header with the specified name. If there is more than one value for the * Returns the {@code char} value of a header with the specified name. If there is more than one value for the
@ -125,7 +133,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @return the {@code char} value of the first value in insertion order or {@code null} if there is no such * @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}. * value or it can't be converted to {@code char}.
*/ */
Character getChar(T name); Character getChar(K name);
/** /**
* Returns the {@code char} value of a header with the specified name. If there is more than one value for the * Returns the {@code char} value of a header with the specified name. If there is more than one value for the
@ -136,7 +144,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* such value or it can't be converted to {@code char}. * such value or it can't be converted to {@code char}.
*/ */
char getChar(T name, char defaultValue); char getChar(K name, char defaultValue);
/** /**
* Returns the {@code short} value of a header with the specified name. If there is more than one value for the * Returns the {@code short} value of a header with the specified name. If there is more than one value for the
@ -146,7 +154,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @return the {@code short} value of the first value in insertion order or {@code null} if there is no such * @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}. * value or it can't be converted to {@code short}.
*/ */
Short getShort(T name); Short getShort(K name);
/** /**
* Returns the {@code short} value of a header with the specified name. If there is more than one value for the * Returns the {@code short} value of a header with the specified name. If there is more than one value for the
@ -157,7 +165,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* such value or it can't be converted to {@code short}. * such value or it can't be converted to {@code short}.
*/ */
short getShort(T name, short defaultValue); short getShort(K name, short defaultValue);
/** /**
* Returns the {@code int} value of a header with the specified name. If there is more than one value for the * Returns the {@code int} value of a header with the specified name. If there is more than one value for the
@ -167,7 +175,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @return the {@code int} value of the first value in insertion order or {@code null} if there is no such * @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}. * value or it can't be converted to {@code int}.
*/ */
Integer getInt(T name); Integer getInt(K name);
/** /**
* Returns the {@code int} value of a header with the specified name. If there is more than one value for the * Returns the {@code int} value of a header with the specified name. If there is more than one value for the
@ -178,7 +186,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* such value or it can't be converted to {@code int}. * such value or it can't be converted to {@code int}.
*/ */
int getInt(T name, int defaultValue); int getInt(K name, int defaultValue);
/** /**
* Returns the {@code long} value of a header with the specified name. If there is more than one value for the * Returns the {@code long} value of a header with the specified name. If there is more than one value for the
@ -188,7 +196,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @return the {@code long} value of the first value in insertion order or {@code null} if there is no such * @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}. * value or it can't be converted to {@code long}.
*/ */
Long getLong(T name); Long getLong(K name);
/** /**
* Returns the {@code long} value of a header with the specified name. If there is more than one value for the * Returns the {@code long} value of a header with the specified name. If there is more than one value for the
@ -199,7 +207,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* such value or it can't be converted to {@code long}. * such value or it can't be converted to {@code long}.
*/ */
long getLong(T name, long defaultValue); long getLong(K name, long defaultValue);
/** /**
* Returns the {@code float} value of a header with the specified name. If there is more than one value for the * Returns the {@code float} value of a header with the specified name. If there is more than one value for the
@ -209,7 +217,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @return the {@code float} value of the first value in insertion order or {@code null} if there is no such * @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}. * value or it can't be converted to {@code float}.
*/ */
Float getFloat(T name); Float getFloat(K name);
/** /**
* Returns the {@code float} value of a header with the specified name. If there is more than one value for the * Returns the {@code float} value of a header with the specified name. If there is more than one value for the
@ -220,7 +228,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* such value or it can't be converted to {@code float}. * such value or it can't be converted to {@code float}.
*/ */
float getFloat(T name, float defaultValue); float getFloat(K name, float defaultValue);
/** /**
* Returns the {@code double} value of a header with the specified name. If there is more than one value for the * Returns the {@code double} value of a header with the specified name. If there is more than one value for the
@ -230,7 +238,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @return the {@code double} value of the first value in insertion order or {@code null} if there is no such * @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}. * value or it can't be converted to {@code double}.
*/ */
Double getDouble(T name); Double getDouble(K name);
/** /**
* Returns the {@code double} value of a header with the specified name. If there is more than one value for the * Returns the {@code double} value of a header with the specified name. If there is more than one value for the
@ -241,7 +249,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* such value or it can't be converted to {@code double}. * such value or it can't be converted to {@code double}.
*/ */
double getDouble(T name, double defaultValue); double getDouble(K name, double defaultValue);
/** /**
* Returns the value of a header with the specified name in milliseconds. If there is more than one value for the * Returns the value of a header with the specified name in milliseconds. If there is more than one value for the
@ -251,7 +259,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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.
*/ */
Long getTimeMillis(T name); Long getTimeMillis(K name);
/** /**
* Returns the value of a header with the specified name in milliseconds. If there is more than one value for the * Returns the value of a header with the specified name in milliseconds. If there is more than one value for the
@ -262,7 +270,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* value or it can't be converted to milliseconds. * value or it can't be converted to milliseconds.
*/ */
long getTimeMillis(T name, long defaultValue); long getTimeMillis(K name, long defaultValue);
/** /**
* 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
@ -275,7 +283,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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}.
*/ */
Boolean getBooleanAndRemove(T name); Boolean getBooleanAndRemove(K name);
/** /**
* 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
@ -289,7 +297,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* such value or it can't be converted to {@code boolean}. * such value or it can't be converted to {@code boolean}.
*/ */
boolean getBooleanAndRemove(T name, boolean defaultValue); boolean getBooleanAndRemove(K name, boolean defaultValue);
/** /**
* 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
@ -302,7 +310,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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}.
*/ */
Byte getByteAndRemove(T name); Byte getByteAndRemove(K name);
/** /**
* 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
@ -316,7 +324,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* such value or it can't be converted to {@code byte}. * such value or it can't be converted to {@code byte}.
*/ */
byte getByteAndRemove(T name, byte defaultValue); byte getByteAndRemove(K name, byte defaultValue);
/** /**
* 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
@ -329,7 +337,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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}.
*/ */
Character getCharAndRemove(T name); Character getCharAndRemove(K name);
/** /**
* 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
@ -343,7 +351,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* such value or it can't be converted to {@code char}. * such value or it can't be converted to {@code char}.
*/ */
char getCharAndRemove(T name, char defaultValue); char getCharAndRemove(K name, char defaultValue);
/** /**
* 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
@ -356,7 +364,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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}.
*/ */
Short getShortAndRemove(T name); Short getShortAndRemove(K name);
/** /**
* 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
@ -370,7 +378,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* such value or it can't be converted to {@code short}. * such value or it can't be converted to {@code short}.
*/ */
short getShortAndRemove(T name, short defaultValue); short getShortAndRemove(K name, short defaultValue);
/** /**
* 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
@ -383,7 +391,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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}.
*/ */
Integer getIntAndRemove(T name); Integer getIntAndRemove(K name);
/** /**
* 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
@ -397,7 +405,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* such value or it can't be converted to {@code int}. * such value or it can't be converted to {@code int}.
*/ */
int getIntAndRemove(T name, int defaultValue); int getIntAndRemove(K name, int defaultValue);
/** /**
* 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
@ -410,7 +418,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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}.
*/ */
Long getLongAndRemove(T name); Long getLongAndRemove(K name);
/** /**
* 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
@ -424,7 +432,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* such value or it can't be converted to {@code long}. * such value or it can't be converted to {@code long}.
*/ */
long getLongAndRemove(T name, long defaultValue); long getLongAndRemove(K name, long defaultValue);
/** /**
* 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
@ -437,7 +445,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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}.
*/ */
Float getFloatAndRemove(T name); Float getFloatAndRemove(K name);
/** /**
* 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
@ -451,7 +459,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* such value or it can't be converted to {@code float}. * such value or it can't be converted to {@code float}.
*/ */
float getFloatAndRemove(T name, float defaultValue); float getFloatAndRemove(K name, float defaultValue);
/** /**
* 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
@ -464,7 +472,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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}.
*/ */
Double getDoubleAndRemove(T name); Double getDoubleAndRemove(K name);
/** /**
* 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
@ -478,7 +486,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* such value or it can't be converted to {@code double}. * such value or it can't be converted to {@code double}.
*/ */
double getDoubleAndRemove(T name, double defaultValue); double getDoubleAndRemove(K name, double defaultValue);
/** /**
* 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
@ -491,7 +499,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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.
*/ */
Long getTimeMillisAndRemove(T name); Long getTimeMillisAndRemove(K name);
/** /**
* 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
@ -505,14 +513,14 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @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
* value or it can't be converted to milliseconds. * value or it can't be converted to milliseconds.
*/ */
long getTimeMillisAndRemove(T name, long defaultValue); long getTimeMillisAndRemove(K name, long defaultValue);
/** /**
* Returns {@code true} if a header with the {@code name} exists, {@code false} otherwise. * Returns {@code true} if a header with the {@code name} exists, {@code false} otherwise.
* *
* @param name the header name * @param name the header name
*/ */
boolean contains(T name); boolean contains(K name);
/** /**
* Returns {@code true} if a header with the {@code name} and {@code value} exists, {@code false} otherwise. * Returns {@code true} if a header with the {@code name} and {@code value} exists, {@code false} otherwise.
@ -522,7 +530,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param name the header name * @param name the header name
* @param value the header value of the header to find * @param value the header value of the header to find
*/ */
boolean contains(T name, T value); boolean contains(K name, V value);
/** /**
* Returns {@code true} if a header with the name and value exists. * Returns {@code true} if a header with the name and value exists.
@ -531,7 +539,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value * @param value the header value
* @return {@code true} if it contains it {@code false} otherwise * @return {@code true} if it contains it {@code false} otherwise
*/ */
boolean containsObject(T name, Object value); boolean containsObject(K name, Object value);
/** /**
* Returns {@code true} if a header with the name and value exists. * Returns {@code true} if a header with the name and value exists.
@ -540,7 +548,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value * @param value the header value
* @return {@code true} if it contains it {@code false} otherwise * @return {@code true} if it contains it {@code false} otherwise
*/ */
boolean containsBoolean(T name, boolean value); boolean containsBoolean(K name, boolean value);
/** /**
* Returns {@code true} if a header with the name and value exists. * Returns {@code true} if a header with the name and value exists.
@ -549,7 +557,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value * @param value the header value
* @return {@code true} if it contains it {@code false} otherwise * @return {@code true} if it contains it {@code false} otherwise
*/ */
boolean containsByte(T name, byte value); boolean containsByte(K name, byte value);
/** /**
* Returns {@code true} if a header with the name and value exists. * Returns {@code true} if a header with the name and value exists.
@ -558,7 +566,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value * @param value the header value
* @return {@code true} if it contains it {@code false} otherwise * @return {@code true} if it contains it {@code false} otherwise
*/ */
boolean containsChar(T name, char value); boolean containsChar(K name, char value);
/** /**
* Returns {@code true} if a header with the name and value exists. * Returns {@code true} if a header with the name and value exists.
@ -567,7 +575,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value * @param value the header value
* @return {@code true} if it contains it {@code false} otherwise * @return {@code true} if it contains it {@code false} otherwise
*/ */
boolean containsShort(T name, short value); boolean containsShort(K name, short value);
/** /**
* Returns {@code true} if a header with the name and value exists. * Returns {@code true} if a header with the name and value exists.
@ -576,7 +584,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value * @param value the header value
* @return {@code true} if it contains it {@code false} otherwise * @return {@code true} if it contains it {@code false} otherwise
*/ */
boolean containsInt(T name, int value); boolean containsInt(K name, int value);
/** /**
* Returns {@code true} if a header with the name and value exists. * Returns {@code true} if a header with the name and value exists.
@ -585,7 +593,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value * @param value the header value
* @return {@code true} if it contains it {@code false} otherwise * @return {@code true} if it contains it {@code false} otherwise
*/ */
boolean containsLong(T name, long value); boolean containsLong(K name, long value);
/** /**
* Returns {@code true} if a header with the name and value exists. * Returns {@code true} if a header with the name and value exists.
@ -594,7 +602,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value * @param value the header value
* @return {@code true} if it contains it {@code false} otherwise * @return {@code true} if it contains it {@code false} otherwise
*/ */
boolean containsFloat(T name, float value); boolean containsFloat(K name, float value);
/** /**
* Returns {@code true} if a header with the name and value exists. * Returns {@code true} if a header with the name and value exists.
@ -603,7 +611,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value * @param value the header value
* @return {@code true} if it contains it {@code false} otherwise * @return {@code true} if it contains it {@code false} otherwise
*/ */
boolean containsDouble(T name, double value); boolean containsDouble(K name, double value);
/** /**
* Returns {@code true} if a header with the name and value exists. * Returns {@code true} if a header with the name and value exists.
@ -612,7 +620,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value * @param value the header value
* @return {@code true} if it contains it {@code false} otherwise * @return {@code true} if it contains it {@code false} otherwise
*/ */
boolean containsTimeMillis(T name, long value); boolean containsTimeMillis(K name, long value);
/** /**
* Returns the number of headers in this object. * Returns the number of headers in this object.
@ -627,7 +635,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
/** /**
* Returns a {@link Set} of all header names in this object. The returned {@link Set} cannot be modified. * Returns a {@link Set} of all header names in this object. The returned {@link Set} cannot be modified.
*/ */
Set<T> names(); Set<K> names();
/** /**
* Adds a new header with the specified {@code name} and {@code value}. * Adds a new header with the specified {@code name} and {@code value}.
@ -636,7 +644,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header * @param value the value of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> add(T name, T value); T add(K name, V value);
/** /**
* Adds new headers with the specified {@code name} and {@code values}. This method is semantically equivalent to * Adds new headers with the specified {@code name} and {@code values}. This method is semantically equivalent to
@ -651,7 +659,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param values the values of the header * @param values the values of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> add(T name, Iterable<? extends T> values); T add(K name, Iterable<? extends V> values);
/** /**
* Adds new headers with the specified {@code name} and {@code values}. This method is semantically equivalent to * Adds new headers with the specified {@code name} and {@code values}. This method is semantically equivalent to
@ -666,7 +674,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param values the values of the header * @param values the values of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> add(T name, T... values); T add(K name, V... values);
/** /**
* Adds a new header. Before the {@code value} is added, it's converted to type {@code T}. * Adds a new header. Before the {@code value} is added, it's converted to type {@code T}.
@ -675,7 +683,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header * @param value the value of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> addObject(T name, Object value); T addObject(K name, Object value);
/** /**
* Adds a new header with the specified name and values. This method is equivalent to * Adds a new header with the specified name and values. This method is equivalent to
@ -690,7 +698,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param values the value of the header * @param values the value of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> addObject(T name, Iterable<?> values); T addObject(K name, Iterable<?> values);
/** /**
* Adds a new header with the specified name and values. This method is equivalent to * Adds a new header with the specified name and values. This method is equivalent to
@ -705,7 +713,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param values the value of the header * @param values the value of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> addObject(T name, Object... values); T addObject(K name, Object... values);
/** /**
* Adds a new header. * Adds a new header.
@ -714,7 +722,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header * @param value the value of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> addBoolean(T name, boolean value); T addBoolean(K name, boolean value);
/** /**
* Adds a new header. * Adds a new header.
@ -723,7 +731,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header * @param value the value of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> addByte(T name, byte value); T addByte(K name, byte value);
/** /**
* Adds a new header. * Adds a new header.
@ -732,7 +740,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header * @param value the value of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> addChar(T name, char value); T addChar(K name, char value);
/** /**
* Adds a new header. * Adds a new header.
@ -741,7 +749,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header * @param value the value of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> addShort(T name, short value); T addShort(K name, short value);
/** /**
* Adds a new header. * Adds a new header.
@ -750,7 +758,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header * @param value the value of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> addInt(T name, int value); T addInt(K name, int value);
/** /**
* Adds a new header. * Adds a new header.
@ -759,7 +767,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header * @param value the value of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> addLong(T name, long value); T addLong(K name, long value);
/** /**
* Adds a new header. * Adds a new header.
@ -768,7 +776,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header * @param value the value of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> addFloat(T name, float value); T addFloat(K name, float value);
/** /**
* Adds a new header. * Adds a new header.
@ -777,7 +785,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header * @param value the value of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> addDouble(T name, double value); T addDouble(K name, double value);
/** /**
* Adds a new header. * Adds a new header.
@ -786,7 +794,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header * @param value the value of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> addTimeMillis(T name, long value); T addTimeMillis(K name, long value);
/** /**
* Adds all header names and values of {@code headers} to this object. * Adds all header names and values of {@code headers} to this object.
@ -794,7 +802,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @throws IllegalArgumentException if {@code headers == this}. * @throws IllegalArgumentException if {@code headers == this}.
* @return {@code this} * @return {@code this}
*/ */
Headers<T> add(Headers<? extends T> headers); T add(Headers<? extends K, ? extends V, ?> headers);
/** /**
* Sets a header with the specified name and value. Any existing headers with the same name are overwritten. * Sets a header with the specified name and value. Any existing headers with the same name are overwritten.
@ -803,7 +811,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header * @param value the value of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> set(T name, T value); T set(K name, V value);
/** /**
* Sets a new header with the specified name and values. This method is equivalent to * Sets a new header with the specified name and values. This method is equivalent to
@ -818,7 +826,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param values the value of the header * @param values the value of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> set(T name, Iterable<? extends T> values); T set(K name, Iterable<? extends V> values);
/** /**
* Sets a header with the specified name and values. Any existing headers with this name are removed. This method * Sets a header with the specified name and values. Any existing headers with this name are removed. This method
@ -835,7 +843,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param values the value of the header * @param values the value of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> set(T name, T... values); T set(K name, V... values);
/** /**
* 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
@ -847,7 +855,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* {@code null}. * {@code null}.
* @return {@code this} * @return {@code this}
*/ */
Headers<T> setObject(T name, Object value); T setObject(K name, Object value);
/** /**
* Sets a header with the specified name and values. Any existing headers with this name are removed. This method * Sets a header with the specified name and values. Any existing headers with this name are removed. This method
@ -864,7 +872,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param values the values of the header * @param values the values of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> setObject(T name, Iterable<?> values); T setObject(K name, Iterable<?> values);
/** /**
* Sets a header with the specified name and values. Any existing headers with this name are removed. This method * Sets a header with the specified name and values. Any existing headers with this name are removed. This method
@ -881,7 +889,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param values the values of the header * @param values the values of the header
* @return {@code this} * @return {@code this}
*/ */
Headers<T> setObject(T name, Object... values); T setObject(K name, Object... values);
/** /**
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
@ -889,7 +897,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value * @param value The value
* @return {@code this} * @return {@code this}
*/ */
Headers<T> setBoolean(T name, boolean value); T setBoolean(K name, boolean value);
/** /**
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
@ -897,7 +905,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value * @param value The value
* @return {@code this} * @return {@code this}
*/ */
Headers<T> setByte(T name, byte value); T setByte(K name, byte value);
/** /**
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
@ -905,7 +913,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value * @param value The value
* @return {@code this} * @return {@code this}
*/ */
Headers<T> setChar(T name, char value); T setChar(K name, char value);
/** /**
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
@ -913,7 +921,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value * @param value The value
* @return {@code this} * @return {@code this}
*/ */
Headers<T> setShort(T name, short value); T setShort(K name, short value);
/** /**
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
@ -921,7 +929,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value * @param value The value
* @return {@code this} * @return {@code this}
*/ */
Headers<T> setInt(T name, int value); T setInt(K name, int value);
/** /**
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
@ -929,7 +937,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value * @param value The value
* @return {@code this} * @return {@code this}
*/ */
Headers<T> setLong(T name, long value); T setLong(K name, long value);
/** /**
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
@ -937,7 +945,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value * @param value The value
* @return {@code this} * @return {@code this}
*/ */
Headers<T> setFloat(T name, float value); T setFloat(K name, float value);
/** /**
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
@ -945,7 +953,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value * @param value The value
* @return {@code this} * @return {@code this}
*/ */
Headers<T> setDouble(T name, double value); T setDouble(K name, double value);
/** /**
* Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}.
@ -953,14 +961,14 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value * @param value The value
* @return {@code this} * @return {@code this}
*/ */
Headers<T> setTimeMillis(T name, long value); T setTimeMillis(K name, long value);
/** /**
* Clears the current header entries and copies all header entries of the specified {@code headers}. * Clears the current header entries and copies all header entries of the specified {@code headers}.
* *
* @return {@code this} * @return {@code this}
*/ */
Headers<T> set(Headers<? extends T> headers); T set(Headers<? extends K, ? extends V, ?> headers);
/** /**
* Retains all current headers but calls {@link #set(T, T)} for each entry in {@code headers}. * Retains all current headers but calls {@link #set(T, T)} for each entry in {@code headers}.
@ -968,7 +976,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param headers The headers used to {@link #set(T, T)} values in this instance * @param headers The headers used to {@link #set(T, T)} values in this instance
* @return {@code this} * @return {@code this}
*/ */
Headers<T> setAll(Headers<? extends T> headers); T setAll(Headers<? extends K, ? extends V, ?> headers);
/** /**
* Removes all headers with the specified {@code name}. * Removes all headers with the specified {@code name}.
@ -976,15 +984,15 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param name the header name * @param name the header name
* @return {@code true} if at least one entry has been removed. * @return {@code true} if at least one entry has been removed.
*/ */
boolean remove(T name); boolean remove(K name);
/** /**
* Removes all headers. After a call to this method {@link #size()} equals {@code 0}. * Removes all headers. After a call to this method {@link #size()} equals {@code 0}.
* *
* @return {@code this} * @return {@code this}
*/ */
Headers<T> clear(); T clear();
@Override @Override
Iterator<Entry<T, T>> iterator(); Iterator<Entry<K, V>> iterator();
} }

View File

@ -37,12 +37,12 @@ public final class HeadersUtils {
* @param name the name of the header to retrieve * @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. * @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) { public static <K, V> List<String> getAllAsString(Headers<K, V, ?> headers, K name) {
final List<T> allNames = headers.getAll(name); final List<V> allNames = headers.getAll(name);
return new AbstractList<String>() { return new AbstractList<String>() {
@Override @Override
public String get(int index) { public String get(int index) {
T value = allNames.get(index); V value = allNames.get(index);
return value != null ? value.toString() : null; return value != null ? value.toString() : null;
} }
@ -59,8 +59,8 @@ public final class HeadersUtils {
* @param name the name of the header to retrieve * @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. * @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) { public static <K, V> String getAsString(Headers<K, V, ?> headers, K name) {
T orig = headers.get(name); V orig = headers.get(name);
return orig != null ? orig.toString() : null; return orig != null ? orig.toString() : null;
} }
@ -77,7 +77,7 @@ public final class HeadersUtils {
* @param headers the headers to get the names from * @param headers the headers to get the names from
* @return a {@link Set} of header values or an empty {@link Set} if no values are found. * @return a {@link Set} of header values or an empty {@link Set} if no values are found.
*/ */
public static Set<String> namesAsString(Headers<CharSequence> headers) { public static Set<String> namesAsString(Headers<CharSequence, CharSequence, ?> headers) {
return new CharSequenceDelegatingStringSet(headers.names()); return new CharSequenceDelegatingStringSet(headers.names());
} }

View File

@ -0,0 +1,125 @@
/*
* 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;
/**
* {@link UnsupportedOperationException} will be thrown from all {@link ValueConverter} methods.
*/
public final class UnsupportedValueConverter<V> implements ValueConverter<V> {
@SuppressWarnings("rawtypes")
private static final UnsupportedValueConverter INSTANCE = new UnsupportedValueConverter();
private UnsupportedValueConverter() { }
@SuppressWarnings("unchecked")
public static <V> UnsupportedValueConverter<V> instance() {
return (UnsupportedValueConverter<V>) INSTANCE;
}
@Override
public V convertObject(Object value) {
throw new UnsupportedOperationException();
}
@Override
public V convertBoolean(boolean value) {
throw new UnsupportedOperationException();
}
@Override
public boolean convertToBoolean(V value) {
throw new UnsupportedOperationException();
}
@Override
public V convertByte(byte value) {
throw new UnsupportedOperationException();
}
@Override
public byte convertToByte(V value) {
throw new UnsupportedOperationException();
}
@Override
public V convertChar(char value) {
throw new UnsupportedOperationException();
}
@Override
public char convertToChar(V value) {
throw new UnsupportedOperationException();
}
@Override
public V convertShort(short value) {
throw new UnsupportedOperationException();
}
@Override
public short convertToShort(V value) {
throw new UnsupportedOperationException();
}
@Override
public V convertInt(int value) {
throw new UnsupportedOperationException();
}
@Override
public int convertToInt(V value) {
throw new UnsupportedOperationException();
}
@Override
public V convertLong(long value) {
throw new UnsupportedOperationException();
}
@Override
public long convertToLong(V value) {
throw new UnsupportedOperationException();
}
@Override
public V convertTimeMillis(long value) {
throw new UnsupportedOperationException();
}
@Override
public long convertToTimeMillis(V value) {
throw new UnsupportedOperationException();
}
@Override
public V convertFloat(float value) {
throw new UnsupportedOperationException();
}
@Override
public float convertToFloat(V value) {
throw new UnsupportedOperationException();
}
@Override
public V convertDouble(double value) {
throw new UnsupportedOperationException();
}
@Override
public double convertToDouble(V value) {
throw new UnsupportedOperationException();
}
}

View File

@ -14,6 +14,15 @@
*/ */
package io.netty.handler.codec; package io.netty.handler.codec;
import org.junit.Test;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import static io.netty.util.AsciiString.of;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@ -22,207 +31,203 @@ 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 java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import org.junit.Test;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil;
/** /**
* Tests for {@link DefaultHeaders}. * Tests for {@link DefaultHeaders}.
*/ */
public class DefaultHeadersTest { public class DefaultHeadersTest {
private Headers<ByteString> newInstance() { private static final class TestDefaultHeaders extends
return new DefaultHeaders<ByteString>(ByteStringValueConverter.INSTANCE); DefaultHeaders<CharSequence, CharSequence, TestDefaultHeaders> {
public TestDefaultHeaders() {
super(CharSequenceValueConverter.INSTANCE);
}
}
private TestDefaultHeaders newInstance() {
return new TestDefaultHeaders();
} }
@Test @Test
public void addShouldIncreaseAndRemoveShouldDecreaseTheSize() { public void addShouldIncreaseAndRemoveShouldDecreaseTheSize() {
Headers<ByteString> headers = newInstance(); TestDefaultHeaders headers = newInstance();
assertEquals(0, headers.size()); assertEquals(0, headers.size());
headers.add(bs("name1"), bs("value1"), bs("value2")); headers.add(of("name1"), of("value1"), of("value2"));
assertEquals(2, headers.size()); assertEquals(2, headers.size());
headers.add(bs("name2"), bs("value3"), bs("value4")); headers.add(of("name2"), of("value3"), of("value4"));
assertEquals(4, headers.size()); assertEquals(4, headers.size());
headers.add(bs("name3"), bs("value5")); headers.add(of("name3"), of("value5"));
assertEquals(5, headers.size()); assertEquals(5, headers.size());
headers.remove(bs("name3")); headers.remove(of("name3"));
assertEquals(4, headers.size()); assertEquals(4, headers.size());
headers.remove(bs("name1")); headers.remove(of("name1"));
assertEquals(2, headers.size()); assertEquals(2, headers.size());
headers.remove(bs("name2")); headers.remove(of("name2"));
assertEquals(0, headers.size()); assertEquals(0, headers.size());
assertTrue(headers.isEmpty()); assertTrue(headers.isEmpty());
} }
@Test @Test
public void afterClearHeadersShouldBeEmpty() { public void afterClearHeadersShouldBeEmpty() {
Headers<ByteString> headers = newInstance(); TestDefaultHeaders headers = newInstance();
headers.add(bs("name1"), bs("value1")); headers.add(of("name1"), of("value1"));
headers.add(bs("name2"), bs("value2")); headers.add(of("name2"), of("value2"));
assertEquals(2, headers.size()); assertEquals(2, headers.size());
headers.clear(); headers.clear();
assertEquals(0, headers.size()); assertEquals(0, headers.size());
assertTrue(headers.isEmpty()); assertTrue(headers.isEmpty());
assertFalse(headers.contains(bs("name1"))); assertFalse(headers.contains(of("name1")));
assertFalse(headers.contains(bs("name2"))); assertFalse(headers.contains(of("name2")));
} }
@Test @Test
public void removingANameForASecondTimeShouldReturnFalse() { public void removingANameForASecondTimeShouldReturnFalse() {
Headers<ByteString> headers = newInstance(); TestDefaultHeaders headers = newInstance();
headers.add(bs("name1"), bs("value1")); headers.add(of("name1"), of("value1"));
headers.add(bs("name2"), bs("value2")); headers.add(of("name2"), of("value2"));
assertTrue(headers.remove(bs("name2"))); assertTrue(headers.remove(of("name2")));
assertFalse(headers.remove(bs("name2"))); assertFalse(headers.remove(of("name2")));
} }
@Test @Test
public void multipleValuesPerNameShouldBeAllowed() { public void multipleValuesPerNameShouldBeAllowed() {
Headers<ByteString> headers = newInstance(); TestDefaultHeaders headers = newInstance();
headers.add(bs("name"), bs("value1")); headers.add(of("name"), of("value1"));
headers.add(bs("name"), bs("value2")); headers.add(of("name"), of("value2"));
headers.add(bs("name"), bs("value3")); headers.add(of("name"), of("value3"));
assertEquals(3, headers.size()); assertEquals(3, headers.size());
List<ByteString> values = headers.getAll(bs("name")); List<CharSequence> values = headers.getAll(of("name"));
assertEquals(3, values.size()); assertEquals(3, values.size());
assertTrue(values.containsAll(asList(bs("value1"), bs("value2"), bs("value3")))); assertTrue(values.containsAll(asList(of("value1"), of("value2"), of("value3"))));
} }
@Test @Test
public void testContains() { public void testContains() {
Headers<ByteString> headers = newInstance(); TestDefaultHeaders headers = newInstance();
headers.addBoolean(bs("boolean"), true); headers.addBoolean(of("boolean"), true);
assertTrue(headers.containsBoolean(bs("boolean"), true)); assertTrue(headers.containsBoolean(of("boolean"), true));
assertFalse(headers.containsBoolean(bs("boolean"), false)); assertFalse(headers.containsBoolean(of("boolean"), false));
headers.addLong(bs("long"), Long.MAX_VALUE); headers.addLong(of("long"), Long.MAX_VALUE);
assertTrue(headers.containsLong(bs("long"), Long.MAX_VALUE)); assertTrue(headers.containsLong(of("long"), Long.MAX_VALUE));
assertFalse(headers.containsLong(bs("long"), Long.MIN_VALUE)); assertFalse(headers.containsLong(of("long"), Long.MIN_VALUE));
headers.addInt(bs("int"), Integer.MIN_VALUE); headers.addInt(of("int"), Integer.MIN_VALUE);
assertTrue(headers.containsInt(bs("int"), Integer.MIN_VALUE)); assertTrue(headers.containsInt(of("int"), Integer.MIN_VALUE));
assertFalse(headers.containsInt(bs("int"), Integer.MAX_VALUE)); assertFalse(headers.containsInt(of("int"), Integer.MAX_VALUE));
headers.addShort(bs("short"), Short.MAX_VALUE); headers.addShort(of("short"), Short.MAX_VALUE);
assertTrue(headers.containsShort(bs("short"), Short.MAX_VALUE)); assertTrue(headers.containsShort(of("short"), Short.MAX_VALUE));
assertFalse(headers.containsShort(bs("short"), Short.MIN_VALUE)); assertFalse(headers.containsShort(of("short"), Short.MIN_VALUE));
headers.addChar(bs("char"), Character.MAX_VALUE); headers.addChar(of("char"), Character.MAX_VALUE);
assertTrue(headers.containsChar(bs("char"), Character.MAX_VALUE)); assertTrue(headers.containsChar(of("char"), Character.MAX_VALUE));
assertFalse(headers.containsChar(bs("char"), Character.MIN_VALUE)); assertFalse(headers.containsChar(of("char"), Character.MIN_VALUE));
headers.addByte(bs("byte"), Byte.MAX_VALUE); headers.addByte(of("byte"), Byte.MAX_VALUE);
assertTrue(headers.containsByte(bs("byte"), Byte.MAX_VALUE)); assertTrue(headers.containsByte(of("byte"), Byte.MAX_VALUE));
assertFalse(headers.containsLong(bs("byte"), Byte.MIN_VALUE)); assertFalse(headers.containsLong(of("byte"), Byte.MIN_VALUE));
headers.addDouble(bs("double"), Double.MAX_VALUE); headers.addDouble(of("double"), Double.MAX_VALUE);
assertTrue(headers.containsDouble(bs("double"), Double.MAX_VALUE)); assertTrue(headers.containsDouble(of("double"), Double.MAX_VALUE));
assertFalse(headers.containsDouble(bs("double"), Double.MIN_VALUE)); assertFalse(headers.containsDouble(of("double"), Double.MIN_VALUE));
headers.addFloat(bs("float"), Float.MAX_VALUE); headers.addFloat(of("float"), Float.MAX_VALUE);
assertTrue(headers.containsFloat(bs("float"), Float.MAX_VALUE)); assertTrue(headers.containsFloat(of("float"), Float.MAX_VALUE));
assertFalse(headers.containsFloat(bs("float"), Float.MIN_VALUE)); assertFalse(headers.containsFloat(of("float"), Float.MIN_VALUE));
long millis = System.currentTimeMillis(); long millis = System.currentTimeMillis();
headers.addTimeMillis(bs("millis"), millis); headers.addTimeMillis(of("millis"), millis);
assertTrue(headers.containsTimeMillis(bs("millis"), millis)); assertTrue(headers.containsTimeMillis(of("millis"), millis));
// This test doesn't work on midnight, January 1, 1970 UTC // This test doesn't work on midnight, January 1, 1970 UTC
assertFalse(headers.containsTimeMillis(bs("millis"), 0)); assertFalse(headers.containsTimeMillis(of("millis"), 0));
headers.addObject(bs("object"), "Hello World"); headers.addObject(of("object"), "Hello World");
assertTrue(headers.containsObject(bs("object"), "Hello World")); assertTrue(headers.containsObject(of("object"), "Hello World"));
assertFalse(headers.containsObject(bs("object"), "")); assertFalse(headers.containsObject(of("object"), ""));
headers.add(bs("name"), bs("value")); headers.add(of("name"), of("value"));
assertTrue(headers.contains(bs("name"), bs("value"))); assertTrue(headers.contains(of("name"), of("value")));
assertFalse(headers.contains(bs("name"), bs("value1"))); assertFalse(headers.contains(of("name"), of("value1")));
} }
@Test @Test
public void canMixConvertedAndNormalValues() { public void canMixConvertedAndNormalValues() {
Headers<ByteString> headers = newInstance(); TestDefaultHeaders headers = newInstance();
headers.add(bs("name"), bs("value")); headers.add(of("name"), of("value"));
headers.addInt(bs("name"), 100); headers.addInt(of("name"), 100);
headers.addBoolean(bs("name"), false); headers.addBoolean(of("name"), false);
assertEquals(3, headers.size()); assertEquals(3, headers.size());
assertTrue(headers.contains(bs("name"))); assertTrue(headers.contains(of("name")));
assertTrue(headers.contains(bs("name"), bs("value"))); assertTrue(headers.contains(of("name"), of("value")));
assertTrue(headers.containsInt(bs("name"), 100)); assertTrue(headers.containsInt(of("name"), 100));
assertTrue(headers.containsBoolean(bs("name"), false)); assertTrue(headers.containsBoolean(of("name"), false));
} }
@Test @Test
public void testGetAndRemove() { public void testGetAndRemove() {
Headers<ByteString> headers = newInstance(); TestDefaultHeaders headers = newInstance();
headers.add(bs("name1"), bs("value1")); headers.add(of("name1"), of("value1"));
headers.add(bs("name2"), bs("value2"), bs("value3")); headers.add(of("name2"), of("value2"), of("value3"));
headers.add(bs("name3"), bs("value4"), bs("value5"), bs("value6")); headers.add(of("name3"), of("value4"), of("value5"), of("value6"));
assertEquals(bs("value1"), headers.getAndRemove(bs("name1"), bs("defaultvalue"))); assertEquals(of("value1"), headers.getAndRemove(of("name1"), of("defaultvalue")));
assertEquals(bs("value2"), headers.getAndRemove(bs("name2"))); assertEquals(of("value2"), headers.getAndRemove(of("name2")));
assertNull(headers.getAndRemove(bs("name2"))); assertNull(headers.getAndRemove(of("name2")));
assertEquals(asList(bs("value4"), bs("value5"), bs("value6")), headers.getAllAndRemove(bs("name3"))); assertEquals(asList(of("value4"), of("value5"), of("value6")), headers.getAllAndRemove(of("name3")));
assertEquals(0, headers.size()); assertEquals(0, headers.size());
assertNull(headers.getAndRemove(bs("noname"))); assertNull(headers.getAndRemove(of("noname")));
assertEquals(bs("defaultvalue"), headers.getAndRemove(bs("noname"), bs("defaultvalue"))); assertEquals(of("defaultvalue"), headers.getAndRemove(of("noname"), of("defaultvalue")));
} }
@Test @Test
public void whenNameContainsMultipleValuesGetShouldReturnTheFirst() { public void whenNameContainsMultipleValuesGetShouldReturnTheFirst() {
Headers<ByteString> headers = newInstance(); TestDefaultHeaders headers = newInstance();
headers.add(bs("name1"), bs("value1"), bs("value2")); headers.add(of("name1"), of("value1"), of("value2"));
assertEquals(bs("value1"), headers.get(bs("name1"))); assertEquals(of("value1"), headers.get(of("name1")));
} }
@Test @Test
public void getWithDefaultValueWorks() { public void getWithDefaultValueWorks() {
Headers<ByteString> headers = newInstance(); TestDefaultHeaders headers = newInstance();
headers.add(bs("name1"), bs("value1")); headers.add(of("name1"), of("value1"));
assertEquals(bs("value1"), headers.get(bs("name1"), bs("defaultvalue"))); assertEquals(of("value1"), headers.get(of("name1"), of("defaultvalue")));
assertEquals(bs("defaultvalue"), headers.get(bs("noname"), bs("defaultvalue"))); assertEquals(of("defaultvalue"), headers.get(of("noname"), of("defaultvalue")));
} }
@Test @Test
public void setShouldOverWritePreviousValue() { public void setShouldOverWritePreviousValue() {
Headers<ByteString> headers = newInstance(); TestDefaultHeaders headers = newInstance();
headers.set(bs("name"), bs("value1")); headers.set(of("name"), of("value1"));
headers.set(bs("name"), bs("value2")); headers.set(of("name"), of("value2"));
assertEquals(1, headers.size()); assertEquals(1, headers.size());
assertEquals(1, headers.getAll(bs("name")).size()); assertEquals(1, headers.getAll(of("name")).size());
assertEquals(bs("value2"), headers.getAll(bs("name")).get(0)); assertEquals(of("value2"), headers.getAll(of("name")).get(0));
assertEquals(bs("value2"), headers.get(bs("name"))); assertEquals(of("value2"), headers.get(of("name")));
} }
@Test @Test
public void setAllShouldOverwriteSomeAndLeaveOthersUntouched() { public void setAllShouldOverwriteSomeAndLeaveOthersUntouched() {
Headers<ByteString> h1 = newInstance(); TestDefaultHeaders h1 = newInstance();
h1.add(bs("name1"), bs("value1")); h1.add(of("name1"), of("value1"));
h1.add(bs("name2"), bs("value2")); h1.add(of("name2"), of("value2"));
h1.add(bs("name2"), bs("value3")); h1.add(of("name2"), of("value3"));
h1.add(bs("name3"), bs("value4")); h1.add(of("name3"), of("value4"));
Headers<ByteString> h2 = newInstance(); TestDefaultHeaders h2 = newInstance();
h2.add(bs("name1"), bs("value5")); h2.add(of("name1"), of("value5"));
h2.add(bs("name2"), bs("value6")); h2.add(of("name2"), of("value6"));
h2.add(bs("name1"), bs("value7")); h2.add(of("name1"), of("value7"));
Headers<ByteString> expected = newInstance(); TestDefaultHeaders expected = newInstance();
expected.add(bs("name1"), bs("value5")); expected.add(of("name1"), of("value5"));
expected.add(bs("name2"), bs("value6")); expected.add(of("name2"), of("value6"));
expected.add(bs("name1"), bs("value7")); expected.add(of("name1"), of("value7"));
expected.add(bs("name3"), bs("value4")); expected.add(of("name3"), of("value4"));
h1.setAll(h2); h1.setAll(h2);
@ -231,15 +236,15 @@ public class DefaultHeadersTest {
@Test @Test
public void headersWithSameNamesAndValuesShouldBeEquivalent() { public void headersWithSameNamesAndValuesShouldBeEquivalent() {
Headers<ByteString> headers1 = newInstance(); TestDefaultHeaders headers1 = newInstance();
headers1.add(bs("name1"), bs("value1")); headers1.add(of("name1"), of("value1"));
headers1.add(bs("name2"), bs("value2")); headers1.add(of("name2"), of("value2"));
headers1.add(bs("name2"), bs("value3")); headers1.add(of("name2"), of("value3"));
Headers<ByteString> headers2 = newInstance(); TestDefaultHeaders headers2 = newInstance();
headers2.add(bs("name1"), bs("value1")); headers2.add(of("name1"), of("value1"));
headers2.add(bs("name2"), bs("value2")); headers2.add(of("name2"), of("value2"));
headers2.add(bs("name2"), bs("value3")); headers2.add(of("name2"), of("value3"));
assertEquals(headers1, headers2); assertEquals(headers1, headers2);
assertEquals(headers2, headers1); assertEquals(headers2, headers1);
@ -252,8 +257,8 @@ public class DefaultHeadersTest {
@Test @Test
public void emptyHeadersShouldBeEqual() { public void emptyHeadersShouldBeEqual() {
Headers<ByteString> headers1 = newInstance(); TestDefaultHeaders headers1 = newInstance();
Headers<ByteString> headers2 = newInstance(); TestDefaultHeaders headers2 = newInstance();
assertNotSame(headers1, headers2); assertNotSame(headers1, headers2);
assertEquals(headers1, headers2); assertEquals(headers1, headers2);
assertEquals(headers1.hashCode(), headers2.hashCode()); assertEquals(headers1.hashCode(), headers2.hashCode());
@ -261,29 +266,29 @@ public class DefaultHeadersTest {
@Test @Test
public void headersWithSameNamesButDifferentValuesShouldNotBeEquivalent() { public void headersWithSameNamesButDifferentValuesShouldNotBeEquivalent() {
Headers<ByteString> headers1 = newInstance(); TestDefaultHeaders headers1 = newInstance();
headers1.add(bs("name1"), bs("value1")); headers1.add(of("name1"), of("value1"));
Headers<ByteString> headers2 = newInstance(); TestDefaultHeaders headers2 = newInstance();
headers1.add(bs("name1"), bs("value2")); headers1.add(of("name1"), of("value2"));
assertNotEquals(headers1, headers2); assertNotEquals(headers1, headers2);
} }
@Test @Test
public void subsetOfHeadersShouldNotBeEquivalent() { public void subsetOfHeadersShouldNotBeEquivalent() {
Headers<ByteString> headers1 = newInstance(); TestDefaultHeaders headers1 = newInstance();
headers1.add(bs("name1"), bs("value1")); headers1.add(of("name1"), of("value1"));
headers1.add(bs("name2"), bs("value2")); headers1.add(of("name2"), of("value2"));
Headers<ByteString> headers2 = newInstance(); TestDefaultHeaders headers2 = newInstance();
headers1.add(bs("name1"), bs("value1")); headers1.add(of("name1"), of("value1"));
assertNotEquals(headers1, headers2); assertNotEquals(headers1, headers2);
} }
@Test @Test
public void headersWithDifferentNamesAndValuesShouldNotBeEquivalent() { public void headersWithDifferentNamesAndValuesShouldNotBeEquivalent() {
Headers<ByteString> h1 = newInstance(); TestDefaultHeaders h1 = newInstance();
h1.set(bs("name1"), bs("value1")); h1.set(of("name1"), of("value1"));
Headers<ByteString> h2 = newInstance(); TestDefaultHeaders h2 = newInstance();
h2.set(bs("name2"), bs("value2")); h2.set(of("name2"), of("value2"));
assertNotEquals(h1, h2); assertNotEquals(h1, h2);
assertNotEquals(h2, h1); assertNotEquals(h2, h1);
assertEquals(h1, h1); assertEquals(h1, h1);
@ -292,22 +297,22 @@ public class DefaultHeadersTest {
@Test(expected = NoSuchElementException.class) @Test(expected = NoSuchElementException.class)
public void iterateEmptyHeadersShouldThrow() { public void iterateEmptyHeadersShouldThrow() {
Iterator<Map.Entry<ByteString, ByteString>> iterator = newInstance().iterator(); Iterator<Map.Entry<CharSequence, CharSequence>> iterator = newInstance().iterator();
assertFalse(iterator.hasNext()); assertFalse(iterator.hasNext());
iterator.next(); iterator.next();
} }
@Test @Test
public void iteratorShouldReturnAllNameValuePairs() { public void iteratorShouldReturnAllNameValuePairs() {
Headers<ByteString> headers1 = newInstance(); TestDefaultHeaders headers1 = newInstance();
headers1.add(bs("name1"), bs("value1"), bs("value2")); headers1.add(of("name1"), of("value1"), of("value2"));
headers1.add(bs("name2"), bs("value3")); headers1.add(of("name2"), of("value3"));
headers1.add(bs("name3"), bs("value4"), bs("value5"), bs("value6")); headers1.add(of("name3"), of("value4"), of("value5"), of("value6"));
headers1.add(bs("name1"), bs("value7"), bs("value8")); headers1.add(of("name1"), of("value7"), of("value8"));
assertEquals(8, headers1.size()); assertEquals(8, headers1.size());
Headers<ByteString> headers2 = newInstance(); TestDefaultHeaders headers2 = newInstance();
for (Entry<ByteString, ByteString> entry : headers1) { for (Entry<CharSequence, CharSequence> entry : headers1) {
headers2.add(entry.getKey(), entry.getValue()); headers2.add(entry.getKey(), entry.getValue());
} }
@ -316,45 +321,45 @@ public class DefaultHeadersTest {
@Test @Test
public void iteratorSetValueShouldChangeHeaderValue() { public void iteratorSetValueShouldChangeHeaderValue() {
Headers<ByteString> headers = newInstance(); TestDefaultHeaders headers = newInstance();
headers.add(bs("name1"), bs("value1"), bs("value2"), bs("value3")); headers.add(of("name1"), of("value1"), of("value2"), of("value3"));
headers.add(bs("name2"), bs("value4")); headers.add(of("name2"), of("value4"));
assertEquals(4, headers.size()); assertEquals(4, headers.size());
Iterator<Entry<ByteString, ByteString>> iter = headers.iterator(); Iterator<Entry<CharSequence, CharSequence>> iter = headers.iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
Entry<ByteString, ByteString> header = iter.next(); Entry<CharSequence, CharSequence> header = iter.next();
if (bs("name1").equals(header.getKey()) && bs("value2").equals(header.getValue())) { if (of("name1").equals(header.getKey()) && of("value2").equals(header.getValue())) {
header.setValue(bs("updatedvalue2")); header.setValue(of("updatedvalue2"));
assertEquals(bs("updatedvalue2"), header.getValue()); assertEquals(of("updatedvalue2"), header.getValue());
} }
if (bs("name1").equals(header.getKey()) && bs("value3").equals(header.getValue())) { if (of("name1").equals(header.getKey()) && of("value3").equals(header.getValue())) {
header.setValue(bs("updatedvalue3")); header.setValue(of("updatedvalue3"));
assertEquals(bs("updatedvalue3"), header.getValue()); assertEquals(of("updatedvalue3"), header.getValue());
} }
} }
assertEquals(4, headers.size()); assertEquals(4, headers.size());
assertTrue(headers.contains(bs("name1"), bs("updatedvalue2"))); assertTrue(headers.contains(of("name1"), of("updatedvalue2")));
assertFalse(headers.contains(bs("name1"), bs("value2"))); assertFalse(headers.contains(of("name1"), of("value2")));
assertTrue(headers.contains(bs("name1"), bs("updatedvalue3"))); assertTrue(headers.contains(of("name1"), of("updatedvalue3")));
assertFalse(headers.contains(bs("name1"), bs("value3"))); assertFalse(headers.contains(of("name1"), of("value3")));
} }
@Test @Test
public void getAllReturnsEmptyListForUnknownName() { public void getAllReturnsEmptyListForUnknownName() {
Headers<ByteString> headers = newInstance(); TestDefaultHeaders headers = newInstance();
assertEquals(0, headers.getAll(bs("noname")).size()); assertEquals(0, headers.getAll(of("noname")).size());
} }
@Test @Test
public void setHeadersShouldClearAndOverwrite() { public void setHeadersShouldClearAndOverwrite() {
Headers<ByteString> headers1 = newInstance(); TestDefaultHeaders headers1 = newInstance();
headers1.add(bs("name"), bs("value")); headers1.add(of("name"), of("value"));
Headers<ByteString> headers2 = newInstance(); TestDefaultHeaders headers2 = newInstance();
headers2.add(bs("name"), bs("newvalue")); headers2.add(of("name"), of("newvalue"));
headers2.add(bs("name1"), bs("value1")); headers2.add(of("name1"), of("value1"));
headers1.set(headers2); headers1.set(headers2);
assertEquals(headers1, headers2); assertEquals(headers1, headers2);
@ -362,18 +367,18 @@ public class DefaultHeadersTest {
@Test @Test
public void setAllHeadersShouldOnlyOverwriteHeaders() { public void setAllHeadersShouldOnlyOverwriteHeaders() {
Headers<ByteString> headers1 = newInstance(); TestDefaultHeaders headers1 = newInstance();
headers1.add(bs("name"), bs("value")); headers1.add(of("name"), of("value"));
headers1.add(bs("name1"), bs("value1")); headers1.add(of("name1"), of("value1"));
Headers<ByteString> headers2 = newInstance(); TestDefaultHeaders headers2 = newInstance();
headers2.add(bs("name"), bs("newvalue")); headers2.add(of("name"), of("newvalue"));
headers2.add(bs("name2"), bs("value2")); headers2.add(of("name2"), of("value2"));
Headers<ByteString> expected = newInstance(); TestDefaultHeaders expected = newInstance();
expected.add(bs("name"), bs("newvalue")); expected.add(of("name"), of("newvalue"));
expected.add(bs("name1"), bs("value1")); expected.add(of("name1"), of("value1"));
expected.add(bs("name2"), bs("value2")); expected.add(of("name2"), of("value2"));
headers1.setAll(headers2); headers1.setAll(headers2);
assertEquals(headers1, expected); assertEquals(headers1, expected);
@ -381,17 +386,13 @@ public class DefaultHeadersTest {
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void testAddSelf() { public void testAddSelf() {
Headers<ByteString> headers = newInstance(); TestDefaultHeaders headers = newInstance();
headers.add(headers); headers.add(headers);
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void testSetSelf() { public void testSetSelf() {
Headers<ByteString> headers = newInstance(); TestDefaultHeaders headers = newInstance();
headers.set(headers); headers.set(headers);
} }
private ByteString bs(String value) {
return new ByteString(value, CharsetUtil.US_ASCII);
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,705 +0,0 @@
/*
* 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 static io.netty.util.internal.ObjectUtil.checkNotNull;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Arrays;
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
* class supports sharing memory with external arrays, and also direct access to the underlying memory via
* {@link #array()}. Care must be taken when directly accessing the memory as that may invalidate assumptions that
* this object is immutable.
*/
public class ByteString {
/**
* Allows sub classes to take advantage of {@link ByteString} operations which need to generate new
* ByteString objects.
*/
protected interface ByteStringFactory {
ByteString newInstance(byte[] value, int start, int length, boolean copy);
}
/**
* Factory which uses the {@link #ByteString(byte[], int, int, boolean)} constructor.
*/
private static final ByteStringFactory DEFAULT_FACTORY = new ByteStringFactory() {
@Override
public ByteString newInstance(byte[] value, int start, int length, boolean copy) {
return new ByteString(value, start, length, copy);
}
};
public static final ByteString EMPTY_STRING = new ByteString(0);
protected static final int HASH_CODE_PRIME = 31;
/**
* If this value is modified outside the constructor then call {@link #arrayChanged()}.
*/
protected final byte[] value;
/**
* Offset into {@link #value} that all operations should use when acting upon {@link #value}.
*/
private final int offset;
/**
* Length in bytes for {@link #value} that we care about. This is independent from {@code value.length}
* because we may be looking at a subsection of the array.
*/
private final int length;
/**
* The hash code is cached after it is first computed. It can be reset with {@link #arrayChanged()}.
*/
private int hash;
/**
* Used for classes which extend this class and want to initialize the {@link #value} array by them selves.
*/
ByteString(int length) {
value = new byte[length];
offset = 0;
this.length = length;
}
/**
* Initialize this byte string based upon a byte array. A copy will be made.
*/
public ByteString(byte[] value) {
this(value, true);
}
/**
* Initialize this byte string based upon a byte array.
* {@code copy} determines if a copy is made or the array is shared.
*/
public ByteString(byte[] value, boolean copy) {
this(value, 0, checkNotNull(value, "value").length, copy);
}
/**
* Construct a new {@link BinaryString} object from a {@code byte[]} array.
* @param copy {@code true} then a copy of the memory will be made. {@code false} the underlying memory
* will be shared.
*/
public ByteString(byte[] value, int start, int length, boolean copy) {
if (copy) {
this.value = Arrays.copyOfRange(value, start, start + length);
this.offset = 0;
this.length = length;
} else {
if (start < 0 || start > value.length - length) {
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" +
length + ") <= " + "value.length(" + value.length + ')');
}
this.value = value;
this.offset = start;
this.length = length;
}
}
/**
* Create a new object which is equal to {@code value}.
* @param value The object to replicate.
* @param copy {@code true} mean the underlying storage will be copied.
* {@code false} means the underlying storage will be shared.
*/
public ByteString(ByteString value, boolean copy) {
checkNotNull(value, "value");
this.length = value.length();
this.hash = value.hash;
if (copy) {
this.value = new byte[length];
System.arraycopy(value.array(), value.arrayOffset(), this.value, 0, length);
this.offset = 0;
} else {
this.value = value.array();
this.offset = value.offset;
}
}
/**
* Create a copy of the underlying storage from {@link value}.
* The copy will start at {@link ByteBuffer#position()} and copy {@link ByteBuffer#remaining()} bytes.
*/
public ByteString(ByteBuffer value) {
this(value, true);
}
/**
* Initialize a {@link ByteString} based upon the underlying storage from {@link value}.
* There is a potential to share the underlying array storage if {@link ByteBuffer#hasArray()} is {@code true}.
* if {@code copy} is {@code true} a copy will be made of the memory.
* if {@code copy} is {@code false} the underlying storage will be shared, if possible.
*/
public ByteString(ByteBuffer value, boolean copy) {
this(value, value.position(), checkNotNull(value, "value").remaining(), copy);
}
/**
* Initialize a {@link ByteString} based upon the underlying storage from {@link value}.
* There is a potential to share the underlying array storage if {@link ByteBuffer#hasArray()} is {@code true}.
* if {@code copy} is {@code true} a copy will be made of the memory.
* if {@code copy} is {@code false} the underlying storage will be shared, if possible.
*/
public ByteString(ByteBuffer value, int start, int length, boolean copy) {
if (start < 0 || length > checkNotNull(value, "value").capacity() - start) {
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
+ ") <= " + "value.capacity(" + value.capacity() + ')');
}
if (value.hasArray()) {
if (copy) {
final int bufferOffset = value.arrayOffset() + start;
this.value = Arrays.copyOfRange(value.array(), bufferOffset, bufferOffset + length);
offset = 0;
this.length = length;
} else {
this.value = value.array();
this.offset = start;
this.length = length;
}
} else {
this.value = new byte[length];
int oldPos = value.position();
value.get(this.value, 0, length);
value.position(oldPos);
this.offset = 0;
this.length = length;
}
}
/**
* Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}.
*/
public ByteString(char[] value, Charset charset) {
this(value, charset, 0, checkNotNull(value, "value").length);
}
/**
* Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}.
* The copy will start at index {@code start} and copy {@code length} bytes.
*/
public ByteString(char[] value, Charset charset, int start, int length) {
if (start < 0 || length > checkNotNull(value, "value").length - start) {
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
+ ") <= " + "length(" + length + ')');
}
CharBuffer cbuf = CharBuffer.wrap(value, start, start + length);
CharsetEncoder encoder = CharsetUtil.getEncoder(charset);
ByteBuffer nativeBuffer = ByteBuffer.allocate((int) (encoder.maxBytesPerChar() * length));
encoder.encode(cbuf, nativeBuffer, true);
final int bufferOffset = nativeBuffer.arrayOffset();
this.value = Arrays.copyOfRange(nativeBuffer.array(), bufferOffset, bufferOffset + nativeBuffer.position());
this.offset = 0;
this.length = this.value.length;
}
/**
* Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}.
*/
public ByteString(CharSequence value, Charset charset) {
this(value, charset, 0, checkNotNull(value, "value").length());
}
/**
* Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}.
* The copy will start at index {@code start} and copy {@code length} bytes.
*/
public ByteString(CharSequence value, Charset charset, int start, int length) {
if (start < 0 || length > checkNotNull(value, "value").length() - start) {
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
+ ") <= " + "length(" + value.length() + ')');
}
CharBuffer cbuf = CharBuffer.wrap(value, start, start + length);
CharsetEncoder encoder = CharsetUtil.getEncoder(charset);
ByteBuffer nativeBuffer = ByteBuffer.allocate((int) (encoder.maxBytesPerChar() * length));
encoder.encode(cbuf, nativeBuffer, true);
final int offset = nativeBuffer.arrayOffset();
this.value = Arrays.copyOfRange(nativeBuffer.array(), offset, offset + nativeBuffer.position());
this.offset = 0;
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.
*
* @return {@code -1} if the processor iterated to or beyond the end of the readable bytes.
* The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
*/
public final int forEachByte(ByteProcessor visitor) throws Exception {
return forEachByte0(0, length(), visitor);
}
/**
* Iterates over the specified area of this buffer with the specified {@code processor} in ascending order.
* (i.e. {@code index}, {@code (index + 1)}, .. {@code (index + length - 1)}).
*
* @return {@code -1} if the processor iterated to or beyond the end of the specified area.
* The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
*/
public final int forEachByte(int index, int length, ByteProcessor visitor) throws Exception {
if (index < 0 || length > length() - index) {
throw new IndexOutOfBoundsException("expected: " + "0 <= index(" + index + ") <= start + 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;
for (int i = offset + index; i < len; ++i) {
if (!visitor.process(value[i])) {
return i - offset;
}
}
return -1;
}
/**
* Iterates over the readable bytes of this buffer with the specified {@code processor} in descending order.
*
* @return {@code -1} if the processor iterated to or beyond the beginning of the readable bytes.
* The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
*/
public final int forEachByteDesc(ByteProcessor visitor) throws Exception {
return forEachByteDesc0(0, length(), visitor);
}
/**
* Iterates over the specified area of this buffer with the specified {@code processor} in descending order.
* (i.e. {@code (index + length - 1)}, {@code (index + length - 2)}, ... {@code index}).
*
* @return {@code -1} if the processor iterated to or beyond the beginning of the specified area.
* The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
*/
public final int forEachByteDesc(int index, int length, ByteProcessor visitor) throws Exception {
if (index < 0 || length > length() - index) {
throw new IndexOutOfBoundsException("expected: " + "0 <= index(" + index + ") <= start + 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;
for (int i = offset + index + length - 1; i >= end; --i) {
if (!visitor.process(value[i])) {
return i - offset;
}
}
return -1;
}
public final byte byteAt(int index) {
// We must do a range check here to enforce the access does not go outside our sub region of the array.
// We rely on the array access itself to pick up the array out of bounds conditions
if (index < 0 || index >= length) {
throw new IndexOutOfBoundsException("index: " + index + " must be in the range [0," + length + ")");
}
return value[index + offset];
}
public final boolean isEmpty() {
return length == 0;
}
public final int length() {
return length;
}
/**
* During normal use cases the {@link ByteString} should be immutable, but if the underlying array is shared,
* and changes then this needs to be called.
*/
public void arrayChanged() {
hash = 0;
}
/**
* This gives direct access to the underlying storage array.
* The {@link #toByteArray()} should be preferred over this method.
* If the return value is changed then {@link #arrayChanged()} must be called.
* @see #arrayOffset()
* @see #isEntireArrayUsed()
*/
public final byte[] array() {
return value;
}
/**
* The offset into {@link #array()} for which data for this ByteString begins.
* @see #array()
* @see #isEntireArrayUsed()
*/
public final int arrayOffset() {
return offset;
}
/**
* Determine if the storage represented by {@link #array()} is entirely used.
* @see #array()
*/
public final boolean isEntireArrayUsed() {
return offset == 0 && length == value.length;
}
/**
* Converts this string to a byte array.
*/
public final byte[] toByteArray() {
return toByteArray(0, length());
}
/**
* Converts a subset of this string to a byte array.
* The subset is defined by the range [{@code start}, {@code end}).
*/
public final byte[] toByteArray(int start, int end) {
return Arrays.copyOfRange(value, start + offset, end + offset);
}
/**
* Copies the content of this string to a byte array.
*
* @param srcIdx the starting offset of characters to copy.
* @param dst the destination byte array.
* @param dstIdx the starting offset in the destination byte array.
* @param length the number of characters to copy.
*/
public final void copy(int srcIdx, byte[] dst, int dstIdx, int length) {
if (srcIdx < 0 || length > length() - srcIdx) {
throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
+ length + ") <= srcLen(" + length() + ')');
}
System.arraycopy(value, srcIdx + offset, checkNotNull(dst, "dst"), dstIdx, length);
}
@Override
public int hashCode() {
int h = hash;
if (h == 0) {
final int end = offset + length;
for (int i = offset; i < end; ++i) {
h = h * HASH_CODE_PRIME + value[i];
}
hash = h;
}
return hash;
}
/**
* Copies a range of characters into a new string.
* @param start the offset of the first character (inclusive).
* @return a new string containing the characters from start to the end of the string.
* @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
*/
public ByteString subSequence(int start) {
return subSequence(start, length());
}
/**
* Copies a range of characters into a new string.
* @param start the offset of the first character (inclusive).
* @param end The index to stop at (exclusive).
* @return a new string containing the characters from start to the end of the string.
* @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
*/
public ByteString subSequence(int start, int end) {
return subSequence(start, end, true);
}
/**
* Either copy or share a subset of underlying sub-sequence of bytes.
* @param start the offset of the first character (inclusive).
* @param end The index to stop at (exclusive).
* @param copy If {@code true} then a copy of the underlying storage will be made.
* If {@code false} then the underlying storage will be shared.
* @return a new string containing the characters from start to the end of the string.
* @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
*/
public ByteString subSequence(int start, int end, boolean copy) {
return subSequence(start, end, copy, DEFAULT_FACTORY);
}
/**
* Either copy or share a subset of underlying sub-sequence of bytes.
* @param start the offset of the first character (inclusive).
* @param end The index to stop at (exclusive).
* @param copy If {@code true} then a copy of the underlying storage will be made.
* If {@code false} then the underlying storage will be shared.
* @param factory The factory used to generate a new {@link ByteString} object.
* @return a new string containing the characters from start to the end of the string.
* @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
*/
protected ByteString subSequence(int start, int end, boolean copy, ByteStringFactory factory) {
if (start < 0 || start > end || end > length()) {
throw new IndexOutOfBoundsException("expected: 0 <= start(" + start + ") <= end (" + end + ") <= length("
+ length() + ')');
}
if (start == 0 && end == length()) {
return this;
}
if (end == start) {
return EMPTY_STRING;
}
return factory.newInstance(value, start + offset, end - start, copy);
}
public final int parseAsciiInt() {
return parseAsciiInt(0, length(), 10);
}
public final int parseAsciiInt(int radix) {
return parseAsciiInt(0, length(), radix);
}
public final int parseAsciiInt(int start, int end) {
return parseAsciiInt(start, end, 10);
}
public final int parseAsciiInt(int start, int end, int radix) {
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
throw new NumberFormatException();
}
if (start == end) {
throw new NumberFormatException();
}
int i = start;
boolean negative = byteAt(i) == '-';
if (negative && ++i == end) {
throw new NumberFormatException(subSequence(start, end, false).toString());
}
return parseAsciiInt(i, end, radix, negative);
}
private int parseAsciiInt(int start, int end, int radix, boolean negative) {
int max = Integer.MIN_VALUE / radix;
int result = 0;
int currOffset = start;
while (currOffset < end) {
int digit = Character.digit((char) (value[currOffset++ + offset] & 0xFF), radix);
if (digit == -1) {
throw new NumberFormatException(subSequence(start, end, false).toString());
}
if (max > result) {
throw new NumberFormatException(subSequence(start, end, false).toString());
}
int next = result * radix - digit;
if (next > result) {
throw new NumberFormatException(subSequence(start, end, false).toString());
}
result = next;
}
if (!negative) {
result = -result;
if (result < 0) {
throw new NumberFormatException(subSequence(start, end, false).toString());
}
}
return result;
}
public final long parseAsciiLong() {
return parseAsciiLong(0, length(), 10);
}
public final long parseAsciiLong(int radix) {
return parseAsciiLong(0, length(), radix);
}
public final long parseAsciiLong(int start, int end) {
return parseAsciiLong(start, end, 10);
}
public final long parseAsciiLong(int start, int end, int radix) {
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
throw new NumberFormatException();
}
if (start == end) {
throw new NumberFormatException();
}
int i = start;
boolean negative = byteAt(i) == '-';
if (negative && ++i == end) {
throw new NumberFormatException(subSequence(start, end, false).toString());
}
return parseAsciiLong(i, end, radix, negative);
}
private long parseAsciiLong(int start, int end, int radix, boolean negative) {
long max = Long.MIN_VALUE / radix;
long result = 0;
int currOffset = start;
while (currOffset < end) {
int digit = Character.digit((char) (value[currOffset++ + offset] & 0xFF), radix);
if (digit == -1) {
throw new NumberFormatException(subSequence(start, end, false).toString());
}
if (max > result) {
throw new NumberFormatException(subSequence(start, end, false).toString());
}
long next = result * radix - digit;
if (next > result) {
throw new NumberFormatException(subSequence(start, end, false).toString());
}
result = next;
}
if (!negative) {
result = -result;
if (result < 0) {
throw new NumberFormatException(subSequence(start, end, false).toString());
}
}
return result;
}
public final char parseChar() {
return parseChar(0);
}
public char parseChar(int start) {
if (start + 1 >= length()) {
throw new IndexOutOfBoundsException("2 bytes required to convert to character. index " +
start + " would go out of bounds.");
}
final int startWithOffset = start + offset;
return (char) (((value[startWithOffset] & 0xFF) << 8) | (value[startWithOffset + 1] & 0xFF));
}
public final short parseAsciiShort() {
return parseAsciiShort(0, length(), 10);
}
public final short parseAsciiShort(int radix) {
return parseAsciiShort(0, length(), radix);
}
public final short parseAsciiShort(int start, int end) {
return parseAsciiShort(start, end, 10);
}
public final short parseAsciiShort(int start, int end, int radix) {
int intValue = parseAsciiInt(start, end, radix);
short result = (short) intValue;
if (result != intValue) {
throw new NumberFormatException(subSequence(start, end, false).toString());
}
return result;
}
public final float parseAsciiFloat() {
return parseAsciiFloat(0, length());
}
public final float parseAsciiFloat(int start, int end) {
return Float.parseFloat(toString(start, end));
}
public final double parseAsciiDouble() {
return parseAsciiDouble(0, length());
}
public final double parseAsciiDouble(int start, int end) {
return Double.parseDouble(toString(start, end));
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ByteString)) {
return false;
}
if (this == obj) {
return true;
}
ByteString other = (ByteString) obj;
return hashCode() == other.hashCode() &&
PlatformDependent.equals(array(), arrayOffset(), arrayOffset() + length(),
other.array(), other.arrayOffset(), other.arrayOffset() + other.length());
}
/**
* Translates the entire byte string to a {@link String}.
* @see {@link #toString(int, int)}
*/
@Override
public String toString() {
return toString(0, length());
}
/**
* Translates the entire byte string to a {@link String} using the {@code charset} encoding.
* @see {@link #toString(Charset, int, int)}
*/
public final String toString(Charset charset) {
return toString(charset, 0, length());
}
/**
* Translates the [{@code start}, {@code end}) range of this byte string to a {@link String}.
* @see {@link #toString(Charset, int, int)}
*/
public final String toString(int start, int end) {
return toString(CharsetUtil.ISO_8859_1, start, end);
}
/**
* Translates the [{@code start}, {@code end}) range of this byte string to a {@link String}
* using the {@code charset} encoding.
*/
public String toString(Charset charset, int start, int end) {
int length = end - start;
if (length == 0) {
return StringUtil.EMPTY_STRING;
}
if (start < 0 || length > length() - start) {
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= srcIdx + length("
+ length + ") <= srcLen(" + length() + ')');
}
return new String(value, start + offset, length, charset);
}
}

View File

@ -35,4 +35,16 @@ public final class MathUtil {
assert value > Integer.MIN_VALUE && value < 0x40000000; assert value > Integer.MIN_VALUE && value < 0x40000000;
return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); return 1 << (32 - Integer.numberOfLeadingZeros(value - 1));
} }
/**
* Determine if the requested {@code index} and {@code length} will fit within {@code capacity}.
* @param index The starting index.
* @param length The length which will be utilized (starting from {@code index}).
* @param capacity The capacity that {@code index + length} is allowed to be within.
* @return {@code true} if the requested {@code index} and {@code length} will fit within {@code capacity}.
* {@code false} if this would result in an index out of bounds exception.
*/
public static boolean isOutOfBounds(int index, int length, int capacity) {
return (index | length | (index + length) | (capacity - (index + length))) < 0;
}
} }

View File

@ -87,7 +87,7 @@ public final class PlatformDependent {
private static final int BIT_MODE = bitMode0(); private static final int BIT_MODE = bitMode0();
private static final int ADDRESS_SIZE = addressSize0(); private static final int ADDRESS_SIZE = addressSize0();
private static final boolean NATIVE_ORDER = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; public static final boolean BIG_ENDIAN_NATIVE_ORDER = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
static { static {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {

View File

@ -22,17 +22,20 @@ import java.nio.CharBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Random; 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; import static io.netty.util.AsciiString.caseInsensitiveHashCode;
import static io.netty.util.AsciiString.contains; import static io.netty.util.AsciiString.contains;
import static io.netty.util.AsciiString.containsIgnoreCase; import static io.netty.util.AsciiString.containsIgnoreCase;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/** /**
* Test for the {@link AsciiString} class * Test character encoding and case insensitivity for the {@link AsciiString} class
*/ */
public class AsciiStringTest { public class AsciiStringCharacterTest {
private static final Random r = new Random();
@Test @Test
public void testGetBytesStringBuilder() { public void testGetBytesStringBuilder() {
final StringBuilder b = new StringBuilder(); final StringBuilder b = new StringBuilder();
@ -44,7 +47,7 @@ public class AsciiStringTest {
for (int i = 0; i < charsets.length; ++i) { for (int i = 0; i < charsets.length; ++i) {
final Charset charset = charsets[i]; final Charset charset = charsets[i];
byte[] expected = bString.getBytes(charset); byte[] expected = bString.getBytes(charset);
byte[] actual = new ByteString(b, charset).toByteArray(); byte[] actual = new AsciiString(b, charset).toByteArray();
assertArrayEquals("failure for " + charset, expected, actual); assertArrayEquals("failure for " + charset, expected, actual);
} }
} }
@ -60,7 +63,7 @@ public class AsciiStringTest {
for (int i = 0; i < charsets.length; ++i) { for (int i = 0; i < charsets.length; ++i) {
final Charset charset = charsets[i]; final Charset charset = charsets[i];
byte[] expected = bString.getBytes(charset); byte[] expected = bString.getBytes(charset);
byte[] actual = new ByteString(bString, charset).toByteArray(); byte[] actual = new AsciiString(bString, charset).toByteArray();
assertArrayEquals("failure for " + charset, expected, actual); assertArrayEquals("failure for " + charset, expected, actual);
} }
} }
@ -156,18 +159,17 @@ public class AsciiStringTest {
@Test @Test
public void testCaseSensitivity() { public void testCaseSensitivity() {
Random r = new Random();
int i = 0; int i = 0;
for (; i < 32; i++) { for (; i < 32; i++) {
doCaseSensitivity(r, i); doCaseSensitivity(i);
} }
final int min = i; final int min = i;
final int max = 4000; final int max = 4000;
final int len = r.nextInt((max - min) + 1) + min; final int len = r.nextInt((max - min) + 1) + min;
doCaseSensitivity(r, len); doCaseSensitivity(len);
} }
private static void doCaseSensitivity(Random r, int len) { private static void doCaseSensitivity(int len) {
// Build an upper case and lower case string // Build an upper case and lower case string
final int upperA = 'A'; final int upperA = 'A';
final int upperZ = 'Z'; final int upperZ = 'Z';
@ -183,22 +185,18 @@ public class AsciiStringTest {
String lowerCaseString = new String(lowerCaseBytes); String lowerCaseString = new String(lowerCaseBytes);
AsciiString lowerCaseAscii = new AsciiString(lowerCaseBytes, false); AsciiString lowerCaseAscii = new AsciiString(lowerCaseBytes, false);
AsciiString upperCaseAscii = new AsciiString(upperCaseString); AsciiString upperCaseAscii = new AsciiString(upperCaseString);
ByteString lowerCaseByteString = new ByteString(lowerCaseBytes);
ByteString upperCaseByteString = new ByteString(upperCaseString, CharsetUtil.US_ASCII);
final String errorString = "len: " + len; final String errorString = "len: " + len;
// Test upper case hash codes are equal // Test upper case hash codes are equal
final int upperCaseExpected = upperCaseAscii.hashCode(); final int upperCaseExpected = upperCaseAscii.hashCode();
assertEquals(errorString, upperCaseExpected, AsciiString.hashCode(upperCaseBuilder)); assertEquals(errorString, upperCaseExpected, AsciiString.hashCode(upperCaseBuilder));
assertEquals(errorString, upperCaseExpected, AsciiString.hashCode(upperCaseString)); assertEquals(errorString, upperCaseExpected, AsciiString.hashCode(upperCaseString));
assertEquals(errorString, upperCaseExpected, upperCaseAscii.hashCode()); assertEquals(errorString, upperCaseExpected, upperCaseAscii.hashCode());
assertEquals(errorString, upperCaseExpected, upperCaseByteString.hashCode());
// Test lower case hash codes are equal // Test lower case hash codes are equal
final int lowerCaseExpected = lowerCaseAscii.hashCode(); final int lowerCaseExpected = lowerCaseAscii.hashCode();
assertEquals(errorString, lowerCaseExpected, AsciiString.hashCode(lowerCaseAscii)); assertEquals(errorString, lowerCaseExpected, AsciiString.hashCode(lowerCaseAscii));
assertEquals(errorString, lowerCaseExpected, AsciiString.hashCode(lowerCaseString)); assertEquals(errorString, lowerCaseExpected, AsciiString.hashCode(lowerCaseString));
assertEquals(errorString, lowerCaseExpected, lowerCaseAscii.hashCode()); assertEquals(errorString, lowerCaseExpected, lowerCaseAscii.hashCode());
assertEquals(errorString, lowerCaseExpected, lowerCaseByteString.hashCode());
// Test case insensitive hash codes are equal // Test case insensitive hash codes are equal
final int expectedCaseInsensative = lowerCaseAscii.hashCodeCaseInsensitive(); final int expectedCaseInsensative = lowerCaseAscii.hashCodeCaseInsensitive();
@ -210,11 +208,9 @@ public class AsciiStringTest {
assertEquals(errorString, expectedCaseInsensative, lowerCaseAscii.hashCodeCaseInsensitive()); assertEquals(errorString, expectedCaseInsensative, lowerCaseAscii.hashCodeCaseInsensitive());
assertEquals(errorString, expectedCaseInsensative, upperCaseAscii.hashCodeCaseInsensitive()); assertEquals(errorString, expectedCaseInsensative, upperCaseAscii.hashCodeCaseInsensitive());
// Test that opposite cases are not equal // Test that opposite cases are equal
if (len != 0) { assertEquals(errorString, lowerCaseAscii.hashCode(), AsciiString.hashCode(upperCaseString));
assertNotEquals(errorString, lowerCaseAscii.hashCode(), AsciiString.hashCode(upperCaseString)); assertEquals(errorString, upperCaseAscii.hashCode(), AsciiString.hashCode(lowerCaseString));
assertNotEquals(errorString, upperCaseAscii.hashCode(), AsciiString.hashCode(lowerCaseString));
}
} }
@Test @Test
@ -228,4 +224,13 @@ public class AsciiStringTest {
CharBuffer buffer = CharBuffer.wrap(array, offset, s1.length()); CharBuffer buffer = CharBuffer.wrap(array, offset, s1.length());
assertEquals(caseInsensitiveHashCode(s1), caseInsensitiveHashCode(buffer)); assertEquals(caseInsensitiveHashCode(s1), caseInsensitiveHashCode(buffer));
} }
@Test
public void testBooleanUtilityMethods() {
assertTrue(new AsciiString(new byte[] { 1 }).parseBoolean());
assertFalse(AsciiString.EMPTY_STRING.parseBoolean());
assertFalse(new AsciiString(new byte[] { 0 }).parseBoolean());
assertTrue(new AsciiString(new byte[] { 5 }).parseBoolean());
assertTrue(new AsciiString(new byte[] { 2, 0 }).parseBoolean());
}
} }

View File

@ -25,16 +25,16 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
/** /**
* Test for the {@link ByteString} class. * Test the underlying memory methods for the {@link AsciiString} class.
*/ */
public class ByteStringTest { public class AsciiStringMemoryTest {
private byte[] a; private byte[] a;
private byte[] b; private byte[] b;
private int aOffset = 22; private int aOffset = 22;
private int bOffset = 53; private int bOffset = 53;
private int length = 100; private int length = 100;
private ByteString aByteString; private AsciiString aAsciiString;
private ByteString bByteString; private AsciiString bAsciiString;
private Random r = new Random(); private Random r = new Random();
@Before @Before
@ -47,32 +47,32 @@ public class ByteStringTest {
bOffset = 53; bOffset = 53;
length = 100; length = 100;
System.arraycopy(a, aOffset, b, bOffset, length); System.arraycopy(a, aOffset, b, bOffset, length);
aByteString = new ByteString(a, aOffset, length, false); aAsciiString = new AsciiString(a, aOffset, length, false);
bByteString = new ByteString(b, bOffset, length, false); bAsciiString = new AsciiString(b, bOffset, length, false);
} }
@Test @Test
public void testSharedMemory() { public void testSharedMemory() {
++a[aOffset]; ++a[aOffset];
ByteString aByteString1 = new ByteString(a, aOffset, length, true); AsciiString aAsciiString1 = new AsciiString(a, aOffset, length, true);
ByteString aByteString2 = new ByteString(a, aOffset, length, false); AsciiString aAsciiString2 = new AsciiString(a, aOffset, length, false);
assertEquals(aByteString, aByteString1); assertEquals(aAsciiString, aAsciiString1);
assertEquals(aByteString, aByteString2); assertEquals(aAsciiString, aAsciiString2);
for (int i = aOffset; i < length; ++i) { for (int i = aOffset; i < length; ++i) {
assertEquals(a[i], aByteString.byteAt(i - aOffset)); assertEquals(a[i], aAsciiString.byteAt(i - aOffset));
} }
} }
@Test @Test
public void testNotSharedMemory() { public void testNotSharedMemory() {
ByteString aByteString1 = new ByteString(a, aOffset, length, true); AsciiString aAsciiString1 = new AsciiString(a, aOffset, length, true);
++a[aOffset]; ++a[aOffset];
assertNotEquals(aByteString, aByteString1); assertNotEquals(aAsciiString, aAsciiString1);
int i = aOffset; int i = aOffset;
assertNotEquals(a[i], aByteString1.byteAt(i - aOffset)); assertNotEquals(a[i], aAsciiString1.byteAt(i - aOffset));
++i; ++i;
for (; i < length; ++i) { for (; i < length; ++i) {
assertEquals(a[i], aByteString1.byteAt(i - aOffset)); assertEquals(a[i], aAsciiString1.byteAt(i - aOffset));
} }
} }
@ -80,69 +80,69 @@ public class ByteStringTest {
public void forEachTest() throws Exception { public void forEachTest() throws Exception {
final AtomicReference<Integer> aCount = new AtomicReference<Integer>(0); final AtomicReference<Integer> aCount = new AtomicReference<Integer>(0);
final AtomicReference<Integer> bCount = new AtomicReference<Integer>(0); final AtomicReference<Integer> bCount = new AtomicReference<Integer>(0);
aByteString.forEachByte(new ByteProcessor() { aAsciiString.forEachByte(new ByteProcessor() {
int i; int i;
@Override @Override
public boolean process(byte value) throws Exception { public boolean process(byte value) throws Exception {
assertEquals("failed at index: " + i, value, bByteString.byteAt(i++)); assertEquals("failed at index: " + i, value, bAsciiString.byteAt(i++));
aCount.set(aCount.get() + 1); aCount.set(aCount.get() + 1);
return true; return true;
} }
}); });
bByteString.forEachByte(new ByteProcessor() { bAsciiString.forEachByte(new ByteProcessor() {
int i; int i;
@Override @Override
public boolean process(byte value) throws Exception { public boolean process(byte value) throws Exception {
assertEquals("failed at index: " + i, value, aByteString.byteAt(i++)); assertEquals("failed at index: " + i, value, aAsciiString.byteAt(i++));
bCount.set(bCount.get() + 1); bCount.set(bCount.get() + 1);
return true; return true;
} }
}); });
assertEquals(aByteString.length(), aCount.get().intValue()); assertEquals(aAsciiString.length(), aCount.get().intValue());
assertEquals(bByteString.length(), bCount.get().intValue()); assertEquals(bAsciiString.length(), bCount.get().intValue());
} }
@Test @Test
public void forEachDescTest() throws Exception { public void forEachDescTest() throws Exception {
final AtomicReference<Integer> aCount = new AtomicReference<Integer>(0); final AtomicReference<Integer> aCount = new AtomicReference<Integer>(0);
final AtomicReference<Integer> bCount = new AtomicReference<Integer>(0); final AtomicReference<Integer> bCount = new AtomicReference<Integer>(0);
aByteString.forEachByteDesc(new ByteProcessor() { aAsciiString.forEachByteDesc(new ByteProcessor() {
int i = 1; int i = 1;
@Override @Override
public boolean process(byte value) throws Exception { public boolean process(byte value) throws Exception {
assertEquals("failed at index: " + i, value, bByteString.byteAt(bByteString.length() - (i++))); assertEquals("failed at index: " + i, value, bAsciiString.byteAt(bAsciiString.length() - (i++)));
aCount.set(aCount.get() + 1); aCount.set(aCount.get() + 1);
return true; return true;
} }
}); });
bByteString.forEachByteDesc(new ByteProcessor() { bAsciiString.forEachByteDesc(new ByteProcessor() {
int i = 1; int i = 1;
@Override @Override
public boolean process(byte value) throws Exception { public boolean process(byte value) throws Exception {
assertEquals("failed at index: " + i, value, aByteString.byteAt(aByteString.length() - (i++))); assertEquals("failed at index: " + i, value, aAsciiString.byteAt(aAsciiString.length() - (i++)));
bCount.set(bCount.get() + 1); bCount.set(bCount.get() + 1);
return true; return true;
} }
}); });
assertEquals(aByteString.length(), aCount.get().intValue()); assertEquals(aAsciiString.length(), aCount.get().intValue());
assertEquals(bByteString.length(), bCount.get().intValue()); assertEquals(bAsciiString.length(), bCount.get().intValue());
} }
@Test @Test
public void subSequenceTest() { public void subSequenceTest() {
final int start = 12; final int start = 12;
final int end = aByteString.length(); final int end = aAsciiString.length();
ByteString aSubSequence = aByteString.subSequence(start, end, false); AsciiString aSubSequence = aAsciiString.subSequence(start, end, false);
ByteString bSubSequence = bByteString.subSequence(start, end, true); AsciiString bSubSequence = bAsciiString.subSequence(start, end, true);
assertEquals(aSubSequence, bSubSequence); assertEquals(aSubSequence, bSubSequence);
assertEquals(aSubSequence.hashCode(), bSubSequence.hashCode()); assertEquals(aSubSequence.hashCode(), bSubSequence.hashCode());
} }
@Test @Test
public void copyTest() { public void copyTest() {
byte[] aCopy = new byte[aByteString.length()]; byte[] aCopy = new byte[aAsciiString.length()];
aByteString.copy(0, aCopy, 0, aCopy.length); aAsciiString.copy(0, aCopy, 0, aCopy.length);
ByteString aByteStringCopy = new ByteString(aCopy, false); AsciiString aAsciiStringCopy = new AsciiString(aCopy, false);
assertEquals(aByteString, aByteStringCopy); assertEquals(aAsciiString, aAsciiStringCopy);
} }
} }

View File

@ -19,8 +19,6 @@ import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http2.DefaultHttp2Headers; import io.netty.handler.codec.http2.DefaultHttp2Headers;
import io.netty.microbench.util.AbstractMicrobenchmark; import io.netty.microbench.util.AbstractMicrobenchmark;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil;
import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level; import org.openjdk.jmh.annotations.Level;
@ -53,9 +51,6 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
AsciiString[] httpNames; AsciiString[] httpNames;
AsciiString[] httpValues; AsciiString[] httpValues;
ByteString[] http2Names;
ByteString[] http2Values;
DefaultHttpHeaders httpHeaders; DefaultHttpHeaders httpHeaders;
DefaultHttp2Headers http2Headers; DefaultHttp2Headers http2Headers;
@ -64,8 +59,6 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
Map<String, String> headers = ExampleHeaders.EXAMPLES.get(exampleHeader); Map<String, String> headers = ExampleHeaders.EXAMPLES.get(exampleHeader);
httpNames = new AsciiString[headers.size()]; httpNames = new AsciiString[headers.size()];
httpValues = new AsciiString[headers.size()]; httpValues = new AsciiString[headers.size()];
http2Names = new ByteString[headers.size()];
http2Values = new ByteString[headers.size()];
httpHeaders = new DefaultHttpHeaders(false); httpHeaders = new DefaultHttpHeaders(false);
http2Headers = new DefaultHttp2Headers(false); http2Headers = new DefaultHttp2Headers(false);
int idx = 0; int idx = 0;
@ -74,11 +67,9 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
String value = header.getValue(); String value = header.getValue();
httpNames[idx] = new AsciiString(name); httpNames[idx] = new AsciiString(name);
httpValues[idx] = new AsciiString(value); httpValues[idx] = new AsciiString(value);
http2Names[idx] = new ByteString(name, CharsetUtil.US_ASCII);
http2Values[idx] = new ByteString(value, CharsetUtil.US_ASCII);
idx++; idx++;
httpHeaders.add(new AsciiString(name), new AsciiString(value)); httpHeaders.add(new AsciiString(name), new AsciiString(value));
http2Headers.add(new ByteString(name, CharsetUtil.US_ASCII), new ByteString(value, CharsetUtil.US_ASCII)); http2Headers.add(new AsciiString(name), new AsciiString(value));
} }
} }
@ -120,7 +111,7 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
@Benchmark @Benchmark
@BenchmarkMode(Mode.AverageTime) @BenchmarkMode(Mode.AverageTime)
public void http2Remove(Blackhole bh) { public void http2Remove(Blackhole bh) {
for (ByteString name : http2Names) { for (AsciiString name : httpNames) {
bh.consume(http2Headers.remove(name)); bh.consume(http2Headers.remove(name));
} }
} }
@ -128,7 +119,7 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
@Benchmark @Benchmark
@BenchmarkMode(Mode.AverageTime) @BenchmarkMode(Mode.AverageTime)
public void http2Get(Blackhole bh) { public void http2Get(Blackhole bh) {
for (ByteString name : http2Names) { for (AsciiString name : httpNames) {
bh.consume(http2Headers.get(name)); bh.consume(http2Headers.get(name));
} }
} }
@ -146,7 +137,7 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
@Benchmark @Benchmark
@BenchmarkMode(Mode.AverageTime) @BenchmarkMode(Mode.AverageTime)
public void http2Iterate(Blackhole bh) { public void http2Iterate(Blackhole bh) {
for (Entry<ByteString, ByteString> entry : http2Headers) { for (Entry<CharSequence, CharSequence> entry : http2Headers) {
bh.consume(entry); bh.consume(entry);
} }
} }