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.charset.Charset;
import static io.netty.util.internal.MathUtil.isOutOfBounds;
/**
* A skeletal implementation of a buffer.
@ -1110,7 +1111,7 @@ public abstract class AbstractByteBuf extends ByteBuf {
}
final void checkIndex0(int index, int fieldLength) {
if (isInvalid(index, fieldLength, capacity())) {
if (isOutOfBounds(index, fieldLength, capacity())) {
throw new IndexOutOfBoundsException(String.format(
"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) {
checkIndex(index, length);
if (isInvalid(srcIndex, length, srcCapacity)) {
if (isOutOfBounds(srcIndex, length, srcCapacity)) {
throw new IndexOutOfBoundsException(String.format(
"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) {
checkIndex(index, length);
if (isInvalid(dstIndex, length, dstCapacity)) {
if (isOutOfBounds(dstIndex, length, dstCapacity)) {
throw new IndexOutOfBoundsException(String.format(
"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
* {@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 static io.netty.util.internal.PlatformDependent.BIG_ENDIAN_NATIVE_ORDER;
/**
* Special {@link SwappedByteBuf} for {@link ByteBuf}s that is using unsafe.
*/
@ -30,7 +32,7 @@ abstract class AbstractUnsafeSwappedByteBuf extends SwappedByteBuf {
super(buf);
assert PlatformDependent.isUnaligned();
wrapped = buf;
nativeByteOrder = UnsafeByteBufUtil.BIG_ENDIAN_NATIVE_ORDER == (order() == ByteOrder.BIG_ENDIAN);
nativeByteOrder = BIG_ENDIAN_NATIVE_ORDER == (order() == ByteOrder.BIG_ENDIAN);
}
@Override

View File

@ -15,11 +15,8 @@
*/
package io.netty.buffer;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import io.netty.util.AsciiString;
import io.netty.util.ByteProcessor;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil;
import io.netty.util.Recycler;
import io.netty.util.Recycler.Handle;
@ -41,6 +38,9 @@ import java.nio.charset.CoderResult;
import java.util.Arrays;
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},
* 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.
*/
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.
*/
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
+ ") <= " + "buf.capacity(" + buf.capacity() + ')');
}
@ -706,12 +706,10 @@ public final class ByteBufUtil {
* @param dstIdx the starting offset in the destination byte array.
* @param length the number of characters to copy.
*/
public static void copy(ByteString src, int srcIdx, ByteBuf dst, int dstIdx, int length) {
final int thisLen = src.length();
if (srcIdx < 0 || length > thisLen - srcIdx) {
public static void copy(AsciiString src, int srcIdx, ByteBuf dst, int dstIdx, int length) {
if (isOutOfBounds(srcIdx, length, src.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);
@ -724,12 +722,10 @@ public final class ByteBufUtil {
* @param dst the destination byte array.
* @param length the number of characters to copy.
*/
public static void copy(ByteString src, int srcIdx, ByteBuf dst, int length) {
final int thisLen = src.length();
if (srcIdx < 0 || length > thisLen - srcIdx) {
public static void copy(AsciiString src, int srcIdx, ByteBuf dst, int length) {
if (isOutOfBounds(srcIdx, length, src.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);
@ -771,7 +767,7 @@ public final class ByteBufUtil {
* the given {@code 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(
"expected: " + "0 <= offset(" + offset + ") <= offset + length(" + length
+ ") <= " + "buf.capacity(" + buf.capacity() + ')');

View File

@ -23,14 +23,14 @@ import java.io.OutputStream;
import java.nio.ByteBuffer;
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.PlatformDependent.BIG_ENDIAN_NATIVE_ORDER;
/**
* All operations get and set as {@link ByteOrder#BIG_ENDIAN}.
*/
final class UnsafeByteBufUtil {
static final boolean BIG_ENDIAN_NATIVE_ORDER = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
private static final boolean UNALIGNED = PlatformDependent.isUnaligned();
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) {
buf.checkIndex(index, length);
checkNotNull(dst, "dst");
if (AbstractByteBuf.isInvalid(dstIndex, length, dst.capacity())) {
if (isOutOfBounds(dstIndex, length, dst.capacity())) {
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) {
buf.checkIndex(index, length);
checkNotNull(dst, "dst");
if (AbstractByteBuf.isInvalid(dstIndex, length, dst.length)) {
if (isOutOfBounds(dstIndex, length, dst.length)) {
throw new IndexOutOfBoundsException("dstIndex: " + dstIndex);
}
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) {
buf.checkIndex(index, length);
checkNotNull(src, "src");
if (AbstractByteBuf.isInvalid(srcIndex, length, src.capacity())) {
if (isOutOfBounds(srcIndex, length, src.capacity())) {
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)));
}
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.
*/

View File

@ -18,6 +18,7 @@ package io.netty.handler.codec.http;
import io.netty.handler.codec.CharSequenceValueConverter;
import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.DefaultHeaders.NameValidator;
import io.netty.handler.codec.DefaultHeadersImpl;
import io.netty.handler.codec.HeadersUtils;
import io.netty.handler.codec.ValueConverter;
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_SENSITIVE_HASHER;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/**
* Default implementation of {@link HttpHeaders}.
@ -46,7 +46,7 @@ public class DefaultHttpHeaders extends HttpHeaders {
private static final ByteProcessor HEADER_NAME_VALIDATOR = new ByteProcessor() {
@Override
public boolean process(byte value) throws Exception {
validateChar((char) (value & 0xFF));
validateHeaderNameElement(value);
return true;
}
};
@ -60,16 +60,15 @@ public class DefaultHttpHeaders extends HttpHeaders {
PlatformDependent.throwException(e);
}
} else {
checkNotNull(name, "name");
// Go through each character in the name
for (int index = 0; index < name.length(); ++index) {
validateChar(name.charAt(index));
validateHeaderNameElement(name.charAt(index));
}
}
}
};
private final DefaultHeaders<CharSequence> headers;
private final DefaultHeaders<CharSequence, CharSequence, ?> headers;
public DefaultHttpHeaders() {
this(true);
@ -80,10 +79,12 @@ public class DefaultHttpHeaders extends HttpHeaders {
}
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;
}
@ -319,8 +320,9 @@ public class DefaultHttpHeaders extends HttpHeaders {
return headers.hashCode(CASE_SENSITIVE_HASHER);
}
private static void validateChar(char character) {
switch (character) {
private static void validateHeaderNameElement(byte value) {
switch (value) {
case 0x00:
case '\t':
case '\n':
case 0x0b:
@ -333,12 +335,37 @@ public class DefaultHttpHeaders extends HttpHeaders {
case '=':
throw new IllegalArgumentException(
"a header name cannot contain the following prohibited characters: =,;: \\t\\r\\n\\v\\f: " +
character);
value);
default:
// 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: " +
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
public CharSequence convertObject(Object value) {
checkNotNull(value, "value");
if (value instanceof CharSequence) {
return (CharSequence) value;
}
if (value instanceof Number) {
return value.toString();
}
if (value instanceof Date) {
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.ByteBufUtil;
import io.netty.util.AsciiString;
import static io.netty.util.AsciiString.c2b;
final class HttpHeadersEncoder {
@ -29,8 +30,8 @@ final class HttpHeadersEncoder {
final int nameLen = name.length();
final int valueLen = value.length();
final int entryLen = nameLen + valueLen + 4;
int offset = buf.writerIndex();
buf.ensureWritable(entryLen);
int offset = buf.writerIndex();
writeAscii(buf, offset, name, nameLen);
offset += nameLen;
buf.setByte(offset ++, ':');
@ -44,23 +45,15 @@ final class HttpHeadersEncoder {
private static void writeAscii(ByteBuf buf, int offset, CharSequence value, int valueLen) {
if (value instanceof AsciiString) {
writeAsciiString(buf, offset, (AsciiString) value, valueLen);
ByteBufUtil.copy((AsciiString) value, 0, buf, offset, valueLen);
} else {
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) {
for (int i = 0; i < valueLen; i ++) {
for (int i = 0; i < valueLen; ++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.util.AsciiString;
import io.netty.util.ByteProcessor;
import io.netty.util.ByteString;
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 byte ASCII_SPACE = (byte) ' ';
private final ByteString string;
private final AsciiString string;
private int i;
/**
* 0 = New or havn't seen {@link ASCII_SPACE}.
@ -482,7 +481,7 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
private int state;
private HttpResponseStatus status;
public HttpStatusLineProcessor(ByteString string) {
public HttpStatusLineProcessor(AsciiString string) {
this.string = string;
}
@ -506,7 +505,7 @@ public class HttpResponseStatus implements Comparable<HttpResponseStatus> {
}
private void parseStatus(int codeEnd) {
int code = string.parseAsciiInt(0, codeEnd);
int code = string.parseInt(0, codeEnd);
status = valueOf(code);
if (codeEnd < 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
*/
public static HttpResponseStatus parseLine(ByteString line) {
public static HttpResponseStatus parseLine(AsciiString line) {
try {
HttpStatusLineProcessor processor = new HttpStatusLineProcessor(line);
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.DefaultHeaders;
import io.netty.handler.codec.Headers;
import io.netty.handler.codec.HeadersUtils;
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_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>() {
@Override
public void validateName(CharSequence name) {
@ -42,214 +41,10 @@ public class DefaultSpdyHeaders extends DefaultHeaders<CharSequence> implements
@SuppressWarnings("unchecked")
public DefaultSpdyHeaders(boolean validate) {
super(CASE_INSENSITIVE_HASHER,
validate ? HeaderValueConverterAndValidator.INSTANCE : HeaderValueConverter.INSTANCE,
validate ? HeaderValueConverterAndValidator.INSTANCE : CharSequenceValueConverter.INSTANCE,
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
public String getAsString(CharSequence name) {
return HeadersUtils.getAsString(this, name);
@ -276,23 +71,7 @@ public class DefaultSpdyHeaders extends DefaultHeaders<CharSequence> implements
ignoreCase ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER);
}
private static class HeaderValueConverter 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 {
private static final class HeaderValueConverterAndValidator extends CharSequenceValueConverter {
public static final HeaderValueConverterAndValidator INSTANCE = new HeaderValueConverterAndValidator();
@Override

View File

@ -15,17 +15,15 @@
*/
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.ByteBufAllocator;
import io.netty.buffer.ByteBufUtil;
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 static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_MAX_NV_LENGTH;
public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder {
private final int version;
@ -59,18 +57,15 @@ public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder {
ByteBuf headerBlock = alloc.heapBuffer();
writeLengthField(headerBlock, numHeaders);
for (CharSequence name: names) {
final ByteString nameBytes = new ByteString(name, CharsetUtil.UTF_8);
int length = nameBytes.length();
writeLengthField(headerBlock, length);
ByteBufUtil.copy(nameBytes, 0, headerBlock, length);
writeLengthField(headerBlock, name.length());
ByteBufUtil.writeAscii(headerBlock, name);
int savedIndex = headerBlock.writerIndex();
int valueLength = 0;
writeLengthField(headerBlock, valueLength);
for (CharSequence value: frame.headers().getAll(name)) {
final ByteString valueBytes = new ByteString(value, CharsetUtil.UTF_8);
length = valueBytes.length();
int length = value.length();
if (length > 0) {
ByteBufUtil.copy(valueBytes, 0, headerBlock, length);
ByteBufUtil.writeAscii(headerBlock, value);
headerBlock.writeByte(0);
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
* 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
@ -60,108 +60,6 @@ public interface SpdyHeaders extends Headers<CharSequence> {
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}.
* @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;
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.Unpooled;
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.compression.ZlibCodecFactory;
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
@ -195,11 +195,11 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
* (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
*/
protected EmbeddedChannel newContentCompressor(ByteString contentEncoding) throws Http2Exception {
if (GZIP.equals(contentEncoding) || X_GZIP.equals(contentEncoding)) {
protected EmbeddedChannel newContentCompressor(CharSequence contentEncoding) throws Http2Exception {
if (GZIP.contentEqualsIgnoreCase(contentEncoding) || X_GZIP.contentEqualsIgnoreCase(contentEncoding)) {
return newCompressionChannel(ZlibWrapper.GZIP);
}
if (DEFLATE.equals(contentEncoding) || X_DEFLATE.equals(contentEncoding)) {
if (DEFLATE.contentEqualsIgnoreCase(contentEncoding) || X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) {
return newCompressionChannel(ZlibWrapper.ZLIB);
}
// 'identity' or unsupported
@ -214,7 +214,7 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
* @return the expected content encoding of the new content.
* @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;
}
@ -241,14 +241,14 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
return null;
}
ByteString encoding = headers.get(CONTENT_ENCODING);
CharSequence encoding = headers.get(CONTENT_ENCODING);
if (encoding == null) {
encoding = IDENTITY;
}
final EmbeddedChannel compressor = newContentCompressor(encoding);
if (compressor != null) {
ByteString targetContentEncoding = getTargetContentEncoding(encoding);
if (IDENTITY.equals(targetContentEncoding)) {
CharSequence targetContentEncoding = getTargetContentEncoding(encoding);
if (IDENTITY.contentEqualsIgnoreCase(targetContentEncoding)) {
headers.remove(CONTENT_ENCODING);
} else {
headers.set(CONTENT_ENCODING, targetContentEncoding);

View File

@ -14,9 +14,6 @@
*/
package io.netty.handler.codec.http2;
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
/**
* Provides common functionality for {@link Http2HeaderTable}
*/

View File

@ -14,43 +14,57 @@
*/
package io.netty.handler.codec.http2;
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import io.netty.handler.codec.ByteStringValueConverter;
import io.netty.handler.codec.CharSequenceValueConverter;
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.ByteString;
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() {
@Override
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
public void validateName(ByteString name) {
final int index;
try {
index = name.forEachByte(HTTP2_NAME_VALIDATOR_PROCESSOR);
} catch (Http2Exception e) {
PlatformDependent.throwException(e);
return;
} catch (Throwable t) {
PlatformDependent.throwException(connectionError(PROTOCOL_ERROR, t,
"unexpected error. invalid header name [%s]", name));
return;
}
public void validateName(CharSequence name) {
if (name instanceof AsciiString) {
final int index;
try {
index = ((AsciiString) name).forEachByte(HTTP2_NAME_VALIDATOR_PROCESSOR);
} catch (Http2Exception e) {
PlatformDependent.throwException(e);
return;
} catch (Throwable t) {
PlatformDependent.throwException(connectionError(PROTOCOL_ERROR, t,
"unexpected error. invalid header name [%s]", name));
return;
}
if (index != -1) {
PlatformDependent.throwException(connectionError(PROTOCOL_ERROR, "invalid header name [%s]", name));
if (index != -1) {
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.
@ -69,282 +83,83 @@ public class DefaultHttp2Headers extends DefaultHeaders<ByteString> implements H
*/
@SuppressWarnings("unchecked")
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
public Http2Headers add(ByteString name, ByteString 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) {
public Http2Headers method(CharSequence value) {
set(PseudoHeaderName.METHOD.value(), value);
return this;
}
@Override
public Http2Headers scheme(ByteString value) {
public Http2Headers scheme(CharSequence value) {
set(PseudoHeaderName.SCHEME.value(), value);
return this;
}
@Override
public Http2Headers authority(ByteString value) {
public Http2Headers authority(CharSequence value) {
set(PseudoHeaderName.AUTHORITY.value(), value);
return this;
}
@Override
public Http2Headers path(ByteString value) {
public Http2Headers path(CharSequence value) {
set(PseudoHeaderName.PATH.value(), value);
return this;
}
@Override
public Http2Headers status(ByteString value) {
public Http2Headers status(CharSequence value) {
set(PseudoHeaderName.STATUS.value(), value);
return this;
}
@Override
public ByteString method() {
public CharSequence method() {
return get(PseudoHeaderName.METHOD.value());
}
@Override
public ByteString scheme() {
public CharSequence scheme() {
return get(PseudoHeaderName.SCHEME.value());
}
@Override
public ByteString authority() {
public CharSequence authority() {
return get(PseudoHeaderName.AUTHORITY.value());
}
@Override
public ByteString path() {
public CharSequence path() {
return get(PseudoHeaderName.PATH.value());
}
@Override
public ByteString status() {
public CharSequence status() {
return get(PseudoHeaderName.STATUS.value());
}
@Override
protected final HeaderEntry<ByteString> newHeaderEntry(int h, ByteString name, ByteString value,
HeaderEntry<ByteString> next) {
protected final HeaderEntry<CharSequence, CharSequence> newHeaderEntry(int h, CharSequence name, CharSequence value,
HeaderEntry<CharSequence, CharSequence> next) {
return new Http2HeaderEntry(h, name, value, next);
}
private final class Http2HeaderEntry extends HeaderEntry<ByteString> {
protected Http2HeaderEntry(int hash, ByteString key, ByteString value, HeaderEntry<ByteString> next) {
private final class Http2HeaderEntry extends HeaderEntry<CharSequence, CharSequence> {
protected Http2HeaderEntry(int hash, CharSequence key, CharSequence value,
HeaderEntry<CharSequence, CharSequence> next) {
super(hash, key);
this.value = value;
this.next = next;
// 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;
before = firstNonPseudo.before();
} else {

View File

@ -15,6 +15,15 @@
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_MAX_HEADER_SIZE;
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.PROTOCOL_ERROR;
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 {
private final int maxHeaderSize;
@ -91,7 +91,7 @@ public class DefaultHttp2HeadersDecoder implements Http2HeadersDecoder, Http2Hea
HeaderListener listener = new HeaderListener() {
@Override
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;
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;
import com.twitter.hpack.Encoder;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.util.ByteString;
import io.netty.util.AsciiString;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
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 {
private final Encoder encoder;
@ -64,7 +64,7 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2Hea
tableSizeChangeOutput.reset();
}
for (Entry<ByteString, ByteString> header : headers) {
for (Entry<CharSequence, CharSequence> header : headers) {
encodeHeader(header.getKey(), header.getValue(), stream);
}
} catch (Http2Exception e) {
@ -90,11 +90,13 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2Hea
return this;
}
private void encodeHeader(ByteString key, ByteString value, OutputStream stream) throws IOException {
encoder.encodeHeader(stream,
key.isEntireArrayUsed() ? key.array() : new ByteString(key, true).array(),
value.isEntireArrayUsed() ? value.array() : new ByteString(value, true).array(),
sensitivityDetector.isSensitive(key, value));
private byte[] toBytes(CharSequence chars) {
AsciiString aString = AsciiString.of(chars);
return aString.isEntireArrayUsed() ? aString.array() : aString.toByteArray();
}
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;
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_LENGTH;
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.Http2Exception.streamError;
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
@ -160,11 +160,11 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
* (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
*/
protected EmbeddedChannel newContentDecompressor(ByteString contentEncoding) throws Http2Exception {
if (GZIP.equals(contentEncoding) || X_GZIP.equals(contentEncoding)) {
protected EmbeddedChannel newContentDecompressor(CharSequence contentEncoding) throws Http2Exception {
if (GZIP.contentEqualsIgnoreCase(contentEncoding) || X_GZIP.contentEqualsIgnoreCase(contentEncoding)) {
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;
// To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly.
return new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(wrapper));
@ -181,7 +181,7 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
* @return the expected content encoding of the new content.
* @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 {
return IDENTITY;
}
@ -204,7 +204,7 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
Http2Decompressor decompressor = decompressor(stream);
if (decompressor == null && !endOfStream) {
// Determine the content encoding.
ByteString contentEncoding = headers.get(CONTENT_ENCODING);
CharSequence contentEncoding = headers.get(CONTENT_ENCODING);
if (contentEncoding == null) {
contentEncoding = IDENTITY;
}
@ -214,8 +214,8 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor
stream.setProperty(propertyKey, decompressor);
// Decode the content and remove or replace the existing headers
// so that the message looks like a decoded message.
ByteString targetContentEncoding = getTargetContentEncoding(contentEncoding);
if (IDENTITY.equals(targetContentEncoding)) {
CharSequence targetContentEncoding = getTargetContentEncoding(contentEncoding);
if (IDENTITY.contentEqualsIgnoreCase(targetContentEncoding)) {
headers.remove(CONTENT_ENCODING);
} else {
headers.set(CONTENT_ENCODING, targetContentEncoding);

View File

@ -16,266 +16,61 @@
package io.netty.handler.codec.http2;
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();
private EmptyHttp2Headers() {
}
@Override
public Http2Headers add(ByteString name, ByteString 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 EmptyHttp2Headers method(ByteString method) {
public EmptyHttp2Headers method(CharSequence method) {
throw new UnsupportedOperationException();
}
@Override
public EmptyHttp2Headers scheme(ByteString status) {
public EmptyHttp2Headers scheme(CharSequence status) {
throw new UnsupportedOperationException();
}
@Override
public EmptyHttp2Headers authority(ByteString authority) {
public EmptyHttp2Headers authority(CharSequence authority) {
throw new UnsupportedOperationException();
}
@Override
public EmptyHttp2Headers path(ByteString path) {
public EmptyHttp2Headers path(CharSequence path) {
throw new UnsupportedOperationException();
}
@Override
public EmptyHttp2Headers status(ByteString status) {
public EmptyHttp2Headers status(CharSequence status) {
throw new UnsupportedOperationException();
}
@Override
public ByteString method() {
public CharSequence method() {
return get(PseudoHeaderName.METHOD.value());
}
@Override
public ByteString scheme() {
public CharSequence scheme() {
return get(PseudoHeaderName.SCHEME.value());
}
@Override
public ByteString authority() {
public CharSequence authority() {
return get(PseudoHeaderName.AUTHORITY.value());
}
@Override
public ByteString path() {
public CharSequence path() {
return get(PseudoHeaderName.PATH.value());
}
@Override
public ByteString status() {
public CharSequence status() {
return get(PseudoHeaderName.STATUS.value());
}
}

View File

@ -15,19 +15,16 @@
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.Map.Entry;
import java.util.Set;
import io.netty.handler.codec.Headers;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil;
/**
* A collection of headers sent or received via HTTP/2.
*/
public interface Http2Headers extends Headers<ByteString> {
public interface Http2Headers extends Headers<CharSequence, CharSequence, Http2Headers> {
/**
* HTTP/2 pseudo-headers names.
@ -58,19 +55,19 @@ public interface Http2Headers extends Headers<ByteString> {
*/
STATUS(":status");
private final ByteString value;
private static final Set<ByteString> PSEUDO_HEADERS = new HashSet<ByteString>();
private final AsciiString value;
private static final CharSequenceMap<AsciiString> PSEUDO_HEADERS = new CharSequenceMap<AsciiString>();
static {
for (PseudoHeaderName pseudoHeader : PseudoHeaderName.values()) {
PSEUDO_HEADERS.add(pseudoHeader.value());
PSEUDO_HEADERS.add(pseudoHeader.value(), AsciiString.EMPTY_STRING);
}
}
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 value;
}
@ -78,168 +75,66 @@ public interface Http2Headers extends Headers<ByteString> {
/**
* 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);
}
}
@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:
* 1. All pseudo headers (order not specified).
* 2. All non-pseudo headers (in insertion order).
*/
@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
*/
Http2Headers method(ByteString value);
Http2Headers method(CharSequence value);
/**
* 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
*/
Http2Headers authority(ByteString value);
Http2Headers authority(CharSequence value);
/**
* 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
*/
Http2Headers status(ByteString value);
Http2Headers status(CharSequence value);
/**
* 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
*/
ByteString scheme();
CharSequence scheme();
/**
* 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
*/
ByteString path();
CharSequence path();
/**
* 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;
import io.netty.buffer.ByteBuf;
import io.netty.util.ByteString;
/**
* 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>.
* {@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() {
@Override
public boolean isSensitive(ByteString name, ByteString value) {
public boolean isSensitive(CharSequence name, CharSequence value) {
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.FullHttpResponse;
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.HttpHeaders;
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.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.AsciiString;
import io.netty.util.ByteString;
import java.net.URI;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
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.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.Http2Exception.connectionError;
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.StringUtil.isNullOrEmpty;
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.
*/
@SuppressWarnings("deprecation")
private static final Set<CharSequence> HTTP_TO_HTTP2_HEADER_BLACKLIST = new HashSet<CharSequence>() {
private static final long serialVersionUID = -5678614530214167043L;
{
add(HttpHeaderNames.CONNECTION);
add(HttpHeaderNames.KEEP_ALIVE);
add(HttpHeaderNames.PROXY_CONNECTION);
add(HttpHeaderNames.TRANSFER_ENCODING);
add(HttpHeaderNames.HOST);
add(HttpHeaderNames.UPGRADE);
add(ExtensionHeaderNames.STREAM_ID.text());
add(ExtensionHeaderNames.SCHEME.text());
add(ExtensionHeaderNames.PATH.text());
}
};
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")
AsciiString keepAlive = HttpHeaderNames.KEEP_ALIVE;
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(keepAlive, EMPTY_STRING);
@SuppressWarnings("deprecation")
AsciiString proxyConnection = HttpHeaderNames.PROXY_CONNECTION;
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(proxyConnection, EMPTY_STRING);
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.TRANSFER_ENCODING, EMPTY_STRING);
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.HOST, EMPTY_STRING);
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.UPGRADE, EMPTY_STRING);
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.STREAM_ID.text(), EMPTY_STRING);
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.SCHEME.text(), EMPTY_STRING);
HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.PATH.text(), EMPTY_STRING);
}
/**
* 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
* @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;
try {
result = HttpResponseStatus.parseLine(status);
@ -219,9 +217,9 @@ public final class HttpConversionUtil {
public static FullHttpRequest toHttpRequest(int streamId, Http2Headers http2Headers, boolean validateHttpHeaders)
throws Http2Exception {
// 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");
final ByteString path = checkNotNull(http2Headers.path(),
final CharSequence path = checkNotNull(http2Headers.path(),
"path header cannot be null in conversion to HTTP/1.x");
FullHttpRequest msg = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(method
.toString()), path.toString(), validateHttpHeaders);
@ -263,7 +261,7 @@ public final class HttpConversionUtil {
HttpVersion httpVersion, boolean isTrailer, boolean isRequest) throws Http2Exception {
Http2ToHttpHeaderTranslator translator = new Http2ToHttpHeaderTranslator(streamId, outputHeaders, isRequest);
try {
for (Entry<ByteString, ByteString> entry : inputHeaders) {
for (Entry<CharSequence, CharSequence> entry : inputHeaders) {
translator.translate(entry);
}
} catch (Http2Exception ex) {
@ -326,12 +324,10 @@ public final class HttpConversionUtil {
Entry<CharSequence, CharSequence> entry = iter.next();
final AsciiString aName = AsciiString.of(entry.getKey()).toLowerCase();
if (!HTTP_TO_HTTP2_HEADER_BLACKLIST.contains(aName)) {
AsciiString aValue = AsciiString.of(entry.getValue());
// https://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-8.1.2.2
// makes a special exception for TE
// https://tools.ietf.org/html/rfc7540#section-8.1.2.2 makes a special exception for TE
if (!aName.contentEqualsIgnoreCase(HttpHeaderNames.TE) ||
aValue.contentEqualsIgnoreCase(HttpHeaderValues.TRAILERS)) {
out.add(aName, aValue);
AsciiString.contentEqualsIgnoreCase(entry.getValue(), HttpHeaderValues.TRAILERS)) {
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.
*/
private static final Map<ByteString, ByteString>
REQUEST_HEADER_TRANSLATIONS = new HashMap<ByteString, ByteString>();
private static final Map<ByteString, ByteString>
RESPONSE_HEADER_TRANSLATIONS = new HashMap<ByteString, ByteString>();
private static final CharSequenceMap<AsciiString>
REQUEST_HEADER_TRANSLATIONS = new CharSequenceMap<AsciiString>();
private static final CharSequenceMap<AsciiString>
RESPONSE_HEADER_TRANSLATIONS = new CharSequenceMap<AsciiString>();
static {
RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.AUTHORITY.value(),
RESPONSE_HEADER_TRANSLATIONS.add(Http2Headers.PseudoHeaderName.AUTHORITY.value(),
HttpHeaderNames.HOST);
RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.SCHEME.value(),
RESPONSE_HEADER_TRANSLATIONS.add(Http2Headers.PseudoHeaderName.SCHEME.value(),
ExtensionHeaderNames.SCHEME.text());
REQUEST_HEADER_TRANSLATIONS.putAll(RESPONSE_HEADER_TRANSLATIONS);
RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.PATH.value(),
REQUEST_HEADER_TRANSLATIONS.add(RESPONSE_HEADER_TRANSLATIONS);
RESPONSE_HEADER_TRANSLATIONS.add(Http2Headers.PseudoHeaderName.PATH.value(),
ExtensionHeaderNames.PATH.text());
}
private final int streamId;
private final HttpHeaders output;
private final Map<ByteString, ByteString> translations;
private final CharSequenceMap<AsciiString> translations;
/**
* Create a new instance
@ -436,23 +432,20 @@ public final class HttpConversionUtil {
translations = request ? REQUEST_HEADER_TRANSLATIONS : RESPONSE_HEADER_TRANSLATIONS;
}
public void translate(Entry<ByteString, ByteString> entry) throws Http2Exception {
final ByteString name = entry.getKey();
final ByteString value = entry.getValue();
ByteString translatedName = translations.get(name);
if (translatedName != null || !Http2Headers.PseudoHeaderName.isPseudoHeader(name)) {
if (translatedName == null) {
translatedName = name;
}
// http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-8.1.2.3
public void translate(Entry<CharSequence, CharSequence> entry) throws Http2Exception {
final CharSequence name = entry.getKey();
final CharSequence value = entry.getValue();
AsciiString translatedName = translations.get(name);
if (translatedName != null) {
output.add(translatedName, AsciiString.of(value));
} else if (!Http2Headers.PseudoHeaderName.isPseudoHeader(name)) {
// https://tools.ietf.org/html/rfc7540#section-8.1.2.3
// 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,
"Invalid HTTP/2 header '%s' encountered in translation to HTTP/1.x", translatedName);
} else {
output.add(new AsciiString(translatedName, false), new AsciiString(value, false));
"Invalid HTTP/2 header '%s' encountered in translation to HTTP/1.x", name);
}
output.add(AsciiString.of(name), AsciiString.of(value));
}
}
}

View File

@ -15,16 +15,11 @@
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.ByteBufUtil;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.ByteString;
import io.netty.util.ReferenceCountUtil;
import java.util.ArrayDeque;
@ -33,6 +28,10 @@ import java.util.Map;
import java.util.Queue;
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
* {@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 final int lastStreamId;
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);
this.lastStreamId = lastStreamId;
this.errorCode = errorCode;
@ -89,7 +88,7 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
return errorCode;
}
public ByteString debugData() {
public byte[] debugData() {
return debugData;
}
}
@ -241,8 +240,7 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
private void cancelGoAwayStreams(int lastStreamId, long errorCode, ByteBuf debugData) {
Iterator<PendingStream> iter = pendingStreams.values().iterator();
Exception e = new Http2GoAwayException(lastStreamId, errorCode,
new ByteString(ByteBufUtil.getBytes(debugData), false));
Exception e = new Http2GoAwayException(lastStreamId, errorCode, ByteBufUtil.getBytes(debugData));
while (iter.hasNext()) {
PendingStream stream = iter.next();
if (stream.streamId > lastStreamId) {

View File

@ -25,7 +25,6 @@ import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.AsciiString;
import io.netty.util.ByteString;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.EventExecutor;
import org.junit.After;
@ -381,7 +380,7 @@ public class DefaultHttp2FrameIOTest {
}
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);
for (int size = 0; size < minSize; size += 2) {
headers.add(singleByte, singleByte);

View File

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

View File

@ -19,7 +19,6 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.AsciiString;
import io.netty.util.ByteString;
import java.util.List;
import java.util.Random;
@ -71,8 +70,8 @@ final class Http2TestUtil {
/**
* Returns an {@link AsciiString} that wraps a randomly-filled byte array.
*/
public static ByteString randomString() {
return new ByteString(randomBytes());
public static AsciiString randomString() {
return new AsciiString(randomBytes());
}
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.DefaultHeaders;
import io.netty.handler.codec.Headers;
import io.netty.handler.codec.HeadersUtils;
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_SENSITIVE_HASHER;
public class DefaultStompHeaders extends DefaultHeaders<CharSequence> implements StompHeaders {
public class DefaultStompHeaders
extends DefaultHeaders<CharSequence, CharSequence, StompHeaders> implements StompHeaders {
public DefaultStompHeaders() {
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
public String getAsString(CharSequence 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
* 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 HOST = new AsciiString("host");
@ -48,108 +48,6 @@ public interface StompHeaders extends Headers<CharSequence> {
AsciiString CONTENT_LENGTH = new AsciiString("content-length");
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}.
* @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;
import io.netty.handler.codec.DefaultHeaders.HeaderDateFormat;
import io.netty.util.AsciiString;
import io.netty.util.internal.PlatformDependent;
import java.text.ParseException;
@ -65,6 +66,9 @@ public class CharSequenceValueConverter implements ValueConverter<CharSequence>
@Override
public boolean convertToBoolean(CharSequence value) {
if (value instanceof AsciiString) {
return ((AsciiString) value).parseBoolean();
}
return Boolean.parseBoolean(value.toString());
}
@ -75,14 +79,14 @@ public class CharSequenceValueConverter implements ValueConverter<CharSequence>
@Override
public byte convertToByte(CharSequence value) {
if (value instanceof AsciiString) {
return ((AsciiString) value).byteAt(0);
}
return Byte.valueOf(value.toString());
}
@Override
public char convertToChar(CharSequence value) {
if (value.length() == 0) {
throw new IllegalArgumentException("'value' is empty.");
}
return value.charAt(0);
}
@ -93,16 +97,25 @@ public class CharSequenceValueConverter implements ValueConverter<CharSequence>
@Override
public short convertToShort(CharSequence value) {
if (value instanceof AsciiString) {
return ((AsciiString) value).parseShort();
}
return Short.valueOf(value.toString());
}
@Override
public int convertToInt(CharSequence value) {
if (value instanceof AsciiString) {
return ((AsciiString) value).parseInt();
}
return Integer.parseInt(value.toString());
}
@Override
public long convertToLong(CharSequence value) {
if (value instanceof AsciiString) {
return ((AsciiString) value).parseLong();
}
return Long.parseLong(value.toString());
}
@ -117,17 +130,23 @@ public class CharSequenceValueConverter implements ValueConverter<CharSequence>
return HeaderDateFormat.get().parse(value.toString());
} catch (ParseException e) {
PlatformDependent.throwException(e);
return 0;
}
return 0;
}
@Override
public float convertToFloat(CharSequence value) {
if (value instanceof AsciiString) {
return ((AsciiString) value).parseFloat();
}
return Float.valueOf(value.toString());
}
@Override
public double convertToDouble(CharSequence value) {
if (value instanceof AsciiString) {
return ((AsciiString) value).parseDouble();
}
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.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
public T get(T name) {
public V get(K name) {
return null;
}
@Override
public T get(T name, T defaultValue) {
public V get(K name, V defaultValue) {
return null;
}
@Override
public T getAndRemove(T name) {
public V getAndRemove(K name) {
return null;
}
@Override
public T getAndRemove(T name, T defaultValue) {
public V getAndRemove(K name, V defaultValue) {
return null;
}
@Override
public List<T> getAll(T name) {
public List<V> getAll(K name) {
return Collections.emptyList();
}
@Override
public List<T> getAllAndRemove(T name) {
public List<V> getAllAndRemove(K name) {
return Collections.emptyList();
}
@Override
public Boolean getBoolean(T name) {
public Boolean getBoolean(K name) {
return null;
}
@Override
public boolean getBoolean(T name, boolean defaultValue) {
public boolean getBoolean(K name, boolean defaultValue) {
return defaultValue;
}
@Override
public Byte getByte(T name) {
public Byte getByte(K name) {
return null;
}
@Override
public byte getByte(T name, byte defaultValue) {
public byte getByte(K name, byte defaultValue) {
return defaultValue;
}
@Override
public Character getChar(T name) {
public Character getChar(K name) {
return null;
}
@Override
public char getChar(T name, char defaultValue) {
public char getChar(K name, char defaultValue) {
return defaultValue;
}
@Override
public Short getShort(T name) {
public Short getShort(K name) {
return null;
}
@Override
public short getShort(T name, short defaultValue) {
public short getShort(K name, short defaultValue) {
return defaultValue;
}
@Override
public Integer getInt(T name) {
public Integer getInt(K name) {
return null;
}
@Override
public int getInt(T name, int defaultValue) {
public int getInt(K name, int defaultValue) {
return defaultValue;
}
@Override
public Long getLong(T name) {
public Long getLong(K name) {
return null;
}
@Override
public long getLong(T name, long defaultValue) {
public long getLong(K name, long defaultValue) {
return defaultValue;
}
@Override
public Float getFloat(T name) {
public Float getFloat(K name) {
return null;
}
@Override
public float getFloat(T name, float defaultValue) {
public float getFloat(K name, float defaultValue) {
return defaultValue;
}
@Override
public Double getDouble(T name) {
public Double getDouble(K name) {
return null;
}
@Override
public double getDouble(T name, double defaultValue) {
public double getDouble(K name, double defaultValue) {
return defaultValue;
}
@Override
public Long getTimeMillis(T name) {
public Long getTimeMillis(K name) {
return null;
}
@Override
public long getTimeMillis(T name, long defaultValue) {
public long getTimeMillis(K name, long defaultValue) {
return defaultValue;
}
@Override
public Boolean getBooleanAndRemove(T name) {
public Boolean getBooleanAndRemove(K name) {
return null;
}
@Override
public boolean getBooleanAndRemove(T name, boolean defaultValue) {
public boolean getBooleanAndRemove(K name, boolean defaultValue) {
return defaultValue;
}
@Override
public Byte getByteAndRemove(T name) {
public Byte getByteAndRemove(K name) {
return null;
}
@Override
public byte getByteAndRemove(T name, byte defaultValue) {
public byte getByteAndRemove(K name, byte defaultValue) {
return defaultValue;
}
@Override
public Character getCharAndRemove(T name) {
public Character getCharAndRemove(K name) {
return null;
}
@Override
public char getCharAndRemove(T name, char defaultValue) {
public char getCharAndRemove(K name, char defaultValue) {
return defaultValue;
}
@Override
public Short getShortAndRemove(T name) {
public Short getShortAndRemove(K name) {
return null;
}
@Override
public short getShortAndRemove(T name, short defaultValue) {
public short getShortAndRemove(K name, short defaultValue) {
return defaultValue;
}
@Override
public Integer getIntAndRemove(T name) {
public Integer getIntAndRemove(K name) {
return null;
}
@Override
public int getIntAndRemove(T name, int defaultValue) {
public int getIntAndRemove(K name, int defaultValue) {
return defaultValue;
}
@Override
public Long getLongAndRemove(T name) {
public Long getLongAndRemove(K name) {
return null;
}
@Override
public long getLongAndRemove(T name, long defaultValue) {
public long getLongAndRemove(K name, long defaultValue) {
return defaultValue;
}
@Override
public Float getFloatAndRemove(T name) {
public Float getFloatAndRemove(K name) {
return null;
}
@Override
public float getFloatAndRemove(T name, float defaultValue) {
public float getFloatAndRemove(K name, float defaultValue) {
return defaultValue;
}
@Override
public Double getDoubleAndRemove(T name) {
public Double getDoubleAndRemove(K name) {
return null;
}
@Override
public double getDoubleAndRemove(T name, double defaultValue) {
public double getDoubleAndRemove(K name, double defaultValue) {
return defaultValue;
}
@Override
public Long getTimeMillisAndRemove(T name) {
public Long getTimeMillisAndRemove(K name) {
return null;
}
@Override
public long getTimeMillisAndRemove(T name, long defaultValue) {
public long getTimeMillisAndRemove(K name, long defaultValue) {
return defaultValue;
}
@Override
public boolean contains(T name) {
public boolean contains(K name) {
return false;
}
@Override
public boolean contains(T name, T value) {
public boolean contains(K name, V value) {
return false;
}
@Override
public boolean containsObject(T name, Object value) {
public boolean containsObject(K name, Object value) {
return false;
}
@Override
public boolean containsBoolean(T name, boolean value) {
public boolean containsBoolean(K name, boolean value) {
return false;
}
@Override
public boolean containsByte(T name, byte value) {
public boolean containsByte(K name, byte value) {
return false;
}
@Override
public boolean containsChar(T name, char value) {
public boolean containsChar(K name, char value) {
return false;
}
@Override
public boolean containsShort(T name, short value) {
public boolean containsShort(K name, short value) {
return false;
}
@Override
public boolean containsInt(T name, int value) {
public boolean containsInt(K name, int value) {
return false;
}
@Override
public boolean containsLong(T name, long value) {
public boolean containsLong(K name, long value) {
return false;
}
@Override
public boolean containsFloat(T name, float value) {
public boolean containsFloat(K name, float value) {
return false;
}
@Override
public boolean containsDouble(T name, double value) {
public boolean containsDouble(K name, double value) {
return false;
}
@Override
public boolean containsTimeMillis(T name, long value) {
public boolean containsTimeMillis(K name, long value) {
return false;
}
@ -302,188 +304,188 @@ public class EmptyHeaders<T> implements Headers<T> {
}
@Override
public Set<T> names() {
public Set<K> names() {
return Collections.emptySet();
}
@Override
public Headers<T> add(T name, T value) {
public T add(K name, V value) {
throw new UnsupportedOperationException("read only");
}
@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");
}
@Override
public Headers<T> add(T name, T... values) {
public T add(K name, V... values) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addObject(T name, Object value) {
public T addObject(K name, Object value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addObject(T name, Iterable<?> values) {
public T addObject(K name, Iterable<?> values) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addObject(T name, Object... values) {
public T addObject(K name, Object... values) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addBoolean(T name, boolean value) {
public T addBoolean(K name, boolean value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addByte(T name, byte value) {
public T addByte(K name, byte value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addChar(T name, char value) {
public T addChar(K name, char value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addShort(T name, short value) {
public T addShort(K name, short value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addInt(T name, int value) {
public T addInt(K name, int value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addLong(T name, long value) {
public T addLong(K name, long value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addFloat(T name, float value) {
public T addFloat(K name, float value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addDouble(T name, double value) {
public T addDouble(K name, double value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> addTimeMillis(T name, long value) {
public T addTimeMillis(K name, long value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> add(Headers<? extends T> headers) {
public T add(Headers<? extends K, ? extends V, ?> headers) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> set(T name, T value) {
public T set(K name, V value) {
throw new UnsupportedOperationException("read only");
}
@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");
}
@Override
public Headers<T> set(T name, T... values) {
public T set(K name, V... values) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setObject(T name, Object value) {
public T setObject(K name, Object value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setObject(T name, Iterable<?> values) {
public T setObject(K name, Iterable<?> values) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setObject(T name, Object... values) {
public T setObject(K name, Object... values) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setBoolean(T name, boolean value) {
public T setBoolean(K name, boolean value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setByte(T name, byte value) {
public T setByte(K name, byte value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setChar(T name, char value) {
public T setChar(K name, char value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setShort(T name, short value) {
public T setShort(K name, short value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setInt(T name, int value) {
public T setInt(K name, int value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setLong(T name, long value) {
public T setLong(K name, long value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setFloat(T name, float value) {
public T setFloat(K name, float value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setDouble(T name, double value) {
public T setDouble(K name, double value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setTimeMillis(T name, long value) {
public T setTimeMillis(K name, long value) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> set(Headers<? extends T> headers) {
public T set(Headers<? extends K, ? extends V, ?> headers) {
throw new UnsupportedOperationException("read only");
}
@Override
public Headers<T> setAll(Headers<? extends T> headers) {
public T setAll(Headers<? extends K, ? extends V, ?> headers) {
throw new UnsupportedOperationException("read only");
}
@Override
public boolean remove(T name) {
public boolean remove(K name) {
return false;
}
@Override
public Headers<T> clear() {
return this;
public T clear() {
return thisT();
}
@Override
public Iterator<Entry<T, T>> iterator() {
List<Entry<T, T>> empty = Collections.emptyList();
public Iterator<Entry<K, V>> iterator() {
List<Entry<K, V>> empty = Collections.emptyList();
return empty.iterator();
}
@ -493,17 +495,22 @@ public class EmptyHeaders<T> implements Headers<T> {
return false;
}
Headers<?> rhs = (Headers<?>) o;
Headers<?, ?, ?> rhs = (Headers<?, ?, ?>) o;
return isEmpty() && rhs.isEmpty();
}
@Override
public int hashCode() {
return 1;
return HASH_CODE_SEED;
}
@Override
public String 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.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,
* 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
* @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,
@ -37,7 +45,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param defaultValue the default value
* @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
@ -46,7 +54,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param name the name of the header to retrieve
* @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
@ -56,7 +64,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param defaultValue the default value
* @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.
@ -64,7 +72,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param name the name of the header to retrieve
* @return a {@link List} of header values or an empty {@link List} if no values are found.
*/
List<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.
@ -73,7 +81,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param name the name of the header to retrieve
* @return a {@link List} of header values or an empty {@link List} if no values are found.
*/
List<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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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
@ -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
* 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.
*
* @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.
@ -522,7 +530,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param name the header name
* @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.
@ -531,7 +539,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value
* @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.
@ -540,7 +548,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value
* @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.
@ -549,7 +557,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value
* @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.
@ -558,7 +566,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value
* @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.
@ -567,7 +575,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value
* @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.
@ -576,7 +584,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value
* @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.
@ -585,7 +593,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value
* @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.
@ -594,7 +602,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value
* @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.
@ -603,7 +611,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value
* @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.
@ -612,7 +620,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the header value
* @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.
@ -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.
*/
Set<T> names();
Set<K> names();
/**
* 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
* @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
@ -651,7 +659,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param values the values of the header
* @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
@ -666,7 +674,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param values the values of the header
* @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}.
@ -675,7 +683,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header
* @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
@ -690,7 +698,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param values the value of the header
* @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
@ -705,7 +713,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param values the value of the header
* @return {@code this}
*/
Headers<T> addObject(T name, Object... values);
T addObject(K name, Object... values);
/**
* Adds a new header.
@ -714,7 +722,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header
* @return {@code this}
*/
Headers<T> addBoolean(T name, boolean value);
T addBoolean(K name, boolean value);
/**
* Adds a new header.
@ -723,7 +731,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header
* @return {@code this}
*/
Headers<T> addByte(T name, byte value);
T addByte(K name, byte value);
/**
* Adds a new header.
@ -732,7 +740,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header
* @return {@code this}
*/
Headers<T> addChar(T name, char value);
T addChar(K name, char value);
/**
* Adds a new header.
@ -741,7 +749,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header
* @return {@code this}
*/
Headers<T> addShort(T name, short value);
T addShort(K name, short value);
/**
* Adds a new header.
@ -750,7 +758,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header
* @return {@code this}
*/
Headers<T> addInt(T name, int value);
T addInt(K name, int value);
/**
* Adds a new header.
@ -759,7 +767,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header
* @return {@code this}
*/
Headers<T> addLong(T name, long value);
T addLong(K name, long value);
/**
* Adds a new header.
@ -768,7 +776,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header
* @return {@code this}
*/
Headers<T> addFloat(T name, float value);
T addFloat(K name, float value);
/**
* Adds a new header.
@ -777,7 +785,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header
* @return {@code this}
*/
Headers<T> addDouble(T name, double value);
T addDouble(K name, double value);
/**
* Adds a new header.
@ -786,7 +794,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header
* @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.
@ -794,7 +802,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @throws IllegalArgumentException if {@code headers == 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.
@ -803,7 +811,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value the value of the header
* @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
@ -818,7 +826,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param values the value of the header
* @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
@ -835,7 +843,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param values the value of the header
* @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
@ -847,7 +855,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* {@code null}.
* @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
@ -864,7 +872,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param values the values of the header
* @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
@ -881,7 +889,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param values the values of the header
* @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}.
@ -889,7 +897,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value
* @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}.
@ -897,7 +905,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value
* @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}.
@ -905,7 +913,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value
* @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}.
@ -913,7 +921,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value
* @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}.
@ -921,7 +929,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value
* @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}.
@ -929,7 +937,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value
* @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}.
@ -937,7 +945,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value
* @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}.
@ -945,7 +953,7 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value
* @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}.
@ -953,14 +961,14 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param value The value
* @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}.
*
* @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}.
@ -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
* @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}.
@ -976,15 +984,15 @@ public interface Headers<T> extends Iterable<Entry<T, T>> {
* @param name the header name
* @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}.
*
* @return {@code this}
*/
Headers<T> clear();
T clear();
@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
* @return a {@link List} of header values or an empty {@link List} if no values are found.
*/
public static <T> List<String> getAllAsString(Headers<T> headers, T name) {
final List<T> allNames = headers.getAll(name);
public static <K, V> List<String> getAllAsString(Headers<K, V, ?> headers, K name) {
final List<V> allNames = headers.getAll(name);
return new AbstractList<String>() {
@Override
public String get(int index) {
T value = allNames.get(index);
V value = allNames.get(index);
return value != null ? value.toString() : null;
}
@ -59,8 +59,8 @@ public final class HeadersUtils {
* @param name the name of the header to retrieve
* @return the first header value if the header is found. {@code null} if there's no such entry.
*/
public static <T> String getAsString(Headers<T> headers, T name) {
T orig = headers.get(name);
public static <K, V> String getAsString(Headers<K, V, ?> headers, K name) {
V orig = headers.get(name);
return orig != null ? orig.toString() : null;
}
@ -77,7 +77,7 @@ public final class HeadersUtils {
* @param headers the headers to get the names from
* @return a {@link Set} of header values or an empty {@link Set} if no values are found.
*/
public static Set<String> namesAsString(Headers<CharSequence> headers) {
public static Set<String> namesAsString(Headers<CharSequence, CharSequence, ?> headers) {
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;
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 org.junit.Assert.assertEquals;
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.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}.
*/
public class DefaultHeadersTest {
private Headers<ByteString> newInstance() {
return new DefaultHeaders<ByteString>(ByteStringValueConverter.INSTANCE);
private static final class TestDefaultHeaders extends
DefaultHeaders<CharSequence, CharSequence, TestDefaultHeaders> {
public TestDefaultHeaders() {
super(CharSequenceValueConverter.INSTANCE);
}
}
private TestDefaultHeaders newInstance() {
return new TestDefaultHeaders();
}
@Test
public void addShouldIncreaseAndRemoveShouldDecreaseTheSize() {
Headers<ByteString> headers = newInstance();
TestDefaultHeaders headers = newInstance();
assertEquals(0, headers.size());
headers.add(bs("name1"), bs("value1"), bs("value2"));
headers.add(of("name1"), of("value1"), of("value2"));
assertEquals(2, headers.size());
headers.add(bs("name2"), bs("value3"), bs("value4"));
headers.add(of("name2"), of("value3"), of("value4"));
assertEquals(4, headers.size());
headers.add(bs("name3"), bs("value5"));
headers.add(of("name3"), of("value5"));
assertEquals(5, headers.size());
headers.remove(bs("name3"));
headers.remove(of("name3"));
assertEquals(4, headers.size());
headers.remove(bs("name1"));
headers.remove(of("name1"));
assertEquals(2, headers.size());
headers.remove(bs("name2"));
headers.remove(of("name2"));
assertEquals(0, headers.size());
assertTrue(headers.isEmpty());
}
@Test
public void afterClearHeadersShouldBeEmpty() {
Headers<ByteString> headers = newInstance();
headers.add(bs("name1"), bs("value1"));
headers.add(bs("name2"), bs("value2"));
TestDefaultHeaders headers = newInstance();
headers.add(of("name1"), of("value1"));
headers.add(of("name2"), of("value2"));
assertEquals(2, headers.size());
headers.clear();
assertEquals(0, headers.size());
assertTrue(headers.isEmpty());
assertFalse(headers.contains(bs("name1")));
assertFalse(headers.contains(bs("name2")));
assertFalse(headers.contains(of("name1")));
assertFalse(headers.contains(of("name2")));
}
@Test
public void removingANameForASecondTimeShouldReturnFalse() {
Headers<ByteString> headers = newInstance();
headers.add(bs("name1"), bs("value1"));
headers.add(bs("name2"), bs("value2"));
assertTrue(headers.remove(bs("name2")));
assertFalse(headers.remove(bs("name2")));
TestDefaultHeaders headers = newInstance();
headers.add(of("name1"), of("value1"));
headers.add(of("name2"), of("value2"));
assertTrue(headers.remove(of("name2")));
assertFalse(headers.remove(of("name2")));
}
@Test
public void multipleValuesPerNameShouldBeAllowed() {
Headers<ByteString> headers = newInstance();
headers.add(bs("name"), bs("value1"));
headers.add(bs("name"), bs("value2"));
headers.add(bs("name"), bs("value3"));
TestDefaultHeaders headers = newInstance();
headers.add(of("name"), of("value1"));
headers.add(of("name"), of("value2"));
headers.add(of("name"), of("value3"));
assertEquals(3, headers.size());
List<ByteString> values = headers.getAll(bs("name"));
List<CharSequence> values = headers.getAll(of("name"));
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
public void testContains() {
Headers<ByteString> headers = newInstance();
headers.addBoolean(bs("boolean"), true);
assertTrue(headers.containsBoolean(bs("boolean"), true));
assertFalse(headers.containsBoolean(bs("boolean"), false));
TestDefaultHeaders headers = newInstance();
headers.addBoolean(of("boolean"), true);
assertTrue(headers.containsBoolean(of("boolean"), true));
assertFalse(headers.containsBoolean(of("boolean"), false));
headers.addLong(bs("long"), Long.MAX_VALUE);
assertTrue(headers.containsLong(bs("long"), Long.MAX_VALUE));
assertFalse(headers.containsLong(bs("long"), Long.MIN_VALUE));
headers.addLong(of("long"), Long.MAX_VALUE);
assertTrue(headers.containsLong(of("long"), Long.MAX_VALUE));
assertFalse(headers.containsLong(of("long"), Long.MIN_VALUE));
headers.addInt(bs("int"), Integer.MIN_VALUE);
assertTrue(headers.containsInt(bs("int"), Integer.MIN_VALUE));
assertFalse(headers.containsInt(bs("int"), Integer.MAX_VALUE));
headers.addInt(of("int"), Integer.MIN_VALUE);
assertTrue(headers.containsInt(of("int"), Integer.MIN_VALUE));
assertFalse(headers.containsInt(of("int"), Integer.MAX_VALUE));
headers.addShort(bs("short"), Short.MAX_VALUE);
assertTrue(headers.containsShort(bs("short"), Short.MAX_VALUE));
assertFalse(headers.containsShort(bs("short"), Short.MIN_VALUE));
headers.addShort(of("short"), Short.MAX_VALUE);
assertTrue(headers.containsShort(of("short"), Short.MAX_VALUE));
assertFalse(headers.containsShort(of("short"), Short.MIN_VALUE));
headers.addChar(bs("char"), Character.MAX_VALUE);
assertTrue(headers.containsChar(bs("char"), Character.MAX_VALUE));
assertFalse(headers.containsChar(bs("char"), Character.MIN_VALUE));
headers.addChar(of("char"), Character.MAX_VALUE);
assertTrue(headers.containsChar(of("char"), Character.MAX_VALUE));
assertFalse(headers.containsChar(of("char"), Character.MIN_VALUE));
headers.addByte(bs("byte"), Byte.MAX_VALUE);
assertTrue(headers.containsByte(bs("byte"), Byte.MAX_VALUE));
assertFalse(headers.containsLong(bs("byte"), Byte.MIN_VALUE));
headers.addByte(of("byte"), Byte.MAX_VALUE);
assertTrue(headers.containsByte(of("byte"), Byte.MAX_VALUE));
assertFalse(headers.containsLong(of("byte"), Byte.MIN_VALUE));
headers.addDouble(bs("double"), Double.MAX_VALUE);
assertTrue(headers.containsDouble(bs("double"), Double.MAX_VALUE));
assertFalse(headers.containsDouble(bs("double"), Double.MIN_VALUE));
headers.addDouble(of("double"), Double.MAX_VALUE);
assertTrue(headers.containsDouble(of("double"), Double.MAX_VALUE));
assertFalse(headers.containsDouble(of("double"), Double.MIN_VALUE));
headers.addFloat(bs("float"), Float.MAX_VALUE);
assertTrue(headers.containsFloat(bs("float"), Float.MAX_VALUE));
assertFalse(headers.containsFloat(bs("float"), Float.MIN_VALUE));
headers.addFloat(of("float"), Float.MAX_VALUE);
assertTrue(headers.containsFloat(of("float"), Float.MAX_VALUE));
assertFalse(headers.containsFloat(of("float"), Float.MIN_VALUE));
long millis = System.currentTimeMillis();
headers.addTimeMillis(bs("millis"), millis);
assertTrue(headers.containsTimeMillis(bs("millis"), millis));
headers.addTimeMillis(of("millis"), millis);
assertTrue(headers.containsTimeMillis(of("millis"), millis));
// 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");
assertTrue(headers.containsObject(bs("object"), "Hello World"));
assertFalse(headers.containsObject(bs("object"), ""));
headers.addObject(of("object"), "Hello World");
assertTrue(headers.containsObject(of("object"), "Hello World"));
assertFalse(headers.containsObject(of("object"), ""));
headers.add(bs("name"), bs("value"));
assertTrue(headers.contains(bs("name"), bs("value")));
assertFalse(headers.contains(bs("name"), bs("value1")));
headers.add(of("name"), of("value"));
assertTrue(headers.contains(of("name"), of("value")));
assertFalse(headers.contains(of("name"), of("value1")));
}
@Test
public void canMixConvertedAndNormalValues() {
Headers<ByteString> headers = newInstance();
headers.add(bs("name"), bs("value"));
headers.addInt(bs("name"), 100);
headers.addBoolean(bs("name"), false);
TestDefaultHeaders headers = newInstance();
headers.add(of("name"), of("value"));
headers.addInt(of("name"), 100);
headers.addBoolean(of("name"), false);
assertEquals(3, headers.size());
assertTrue(headers.contains(bs("name")));
assertTrue(headers.contains(bs("name"), bs("value")));
assertTrue(headers.containsInt(bs("name"), 100));
assertTrue(headers.containsBoolean(bs("name"), false));
assertTrue(headers.contains(of("name")));
assertTrue(headers.contains(of("name"), of("value")));
assertTrue(headers.containsInt(of("name"), 100));
assertTrue(headers.containsBoolean(of("name"), false));
}
@Test
public void testGetAndRemove() {
Headers<ByteString> headers = newInstance();
headers.add(bs("name1"), bs("value1"));
headers.add(bs("name2"), bs("value2"), bs("value3"));
headers.add(bs("name3"), bs("value4"), bs("value5"), bs("value6"));
TestDefaultHeaders headers = newInstance();
headers.add(of("name1"), of("value1"));
headers.add(of("name2"), of("value2"), of("value3"));
headers.add(of("name3"), of("value4"), of("value5"), of("value6"));
assertEquals(bs("value1"), headers.getAndRemove(bs("name1"), bs("defaultvalue")));
assertEquals(bs("value2"), headers.getAndRemove(bs("name2")));
assertNull(headers.getAndRemove(bs("name2")));
assertEquals(asList(bs("value4"), bs("value5"), bs("value6")), headers.getAllAndRemove(bs("name3")));
assertEquals(of("value1"), headers.getAndRemove(of("name1"), of("defaultvalue")));
assertEquals(of("value2"), headers.getAndRemove(of("name2")));
assertNull(headers.getAndRemove(of("name2")));
assertEquals(asList(of("value4"), of("value5"), of("value6")), headers.getAllAndRemove(of("name3")));
assertEquals(0, headers.size());
assertNull(headers.getAndRemove(bs("noname")));
assertEquals(bs("defaultvalue"), headers.getAndRemove(bs("noname"), bs("defaultvalue")));
assertNull(headers.getAndRemove(of("noname")));
assertEquals(of("defaultvalue"), headers.getAndRemove(of("noname"), of("defaultvalue")));
}
@Test
public void whenNameContainsMultipleValuesGetShouldReturnTheFirst() {
Headers<ByteString> headers = newInstance();
headers.add(bs("name1"), bs("value1"), bs("value2"));
assertEquals(bs("value1"), headers.get(bs("name1")));
TestDefaultHeaders headers = newInstance();
headers.add(of("name1"), of("value1"), of("value2"));
assertEquals(of("value1"), headers.get(of("name1")));
}
@Test
public void getWithDefaultValueWorks() {
Headers<ByteString> headers = newInstance();
headers.add(bs("name1"), bs("value1"));
TestDefaultHeaders headers = newInstance();
headers.add(of("name1"), of("value1"));
assertEquals(bs("value1"), headers.get(bs("name1"), bs("defaultvalue")));
assertEquals(bs("defaultvalue"), headers.get(bs("noname"), bs("defaultvalue")));
assertEquals(of("value1"), headers.get(of("name1"), of("defaultvalue")));
assertEquals(of("defaultvalue"), headers.get(of("noname"), of("defaultvalue")));
}
@Test
public void setShouldOverWritePreviousValue() {
Headers<ByteString> headers = newInstance();
headers.set(bs("name"), bs("value1"));
headers.set(bs("name"), bs("value2"));
TestDefaultHeaders headers = newInstance();
headers.set(of("name"), of("value1"));
headers.set(of("name"), of("value2"));
assertEquals(1, headers.size());
assertEquals(1, headers.getAll(bs("name")).size());
assertEquals(bs("value2"), headers.getAll(bs("name")).get(0));
assertEquals(bs("value2"), headers.get(bs("name")));
assertEquals(1, headers.getAll(of("name")).size());
assertEquals(of("value2"), headers.getAll(of("name")).get(0));
assertEquals(of("value2"), headers.get(of("name")));
}
@Test
public void setAllShouldOverwriteSomeAndLeaveOthersUntouched() {
Headers<ByteString> h1 = newInstance();
TestDefaultHeaders h1 = newInstance();
h1.add(bs("name1"), bs("value1"));
h1.add(bs("name2"), bs("value2"));
h1.add(bs("name2"), bs("value3"));
h1.add(bs("name3"), bs("value4"));
h1.add(of("name1"), of("value1"));
h1.add(of("name2"), of("value2"));
h1.add(of("name2"), of("value3"));
h1.add(of("name3"), of("value4"));
Headers<ByteString> h2 = newInstance();
h2.add(bs("name1"), bs("value5"));
h2.add(bs("name2"), bs("value6"));
h2.add(bs("name1"), bs("value7"));
TestDefaultHeaders h2 = newInstance();
h2.add(of("name1"), of("value5"));
h2.add(of("name2"), of("value6"));
h2.add(of("name1"), of("value7"));
Headers<ByteString> expected = newInstance();
expected.add(bs("name1"), bs("value5"));
expected.add(bs("name2"), bs("value6"));
expected.add(bs("name1"), bs("value7"));
expected.add(bs("name3"), bs("value4"));
TestDefaultHeaders expected = newInstance();
expected.add(of("name1"), of("value5"));
expected.add(of("name2"), of("value6"));
expected.add(of("name1"), of("value7"));
expected.add(of("name3"), of("value4"));
h1.setAll(h2);
@ -231,15 +236,15 @@ public class DefaultHeadersTest {
@Test
public void headersWithSameNamesAndValuesShouldBeEquivalent() {
Headers<ByteString> headers1 = newInstance();
headers1.add(bs("name1"), bs("value1"));
headers1.add(bs("name2"), bs("value2"));
headers1.add(bs("name2"), bs("value3"));
TestDefaultHeaders headers1 = newInstance();
headers1.add(of("name1"), of("value1"));
headers1.add(of("name2"), of("value2"));
headers1.add(of("name2"), of("value3"));
Headers<ByteString> headers2 = newInstance();
headers2.add(bs("name1"), bs("value1"));
headers2.add(bs("name2"), bs("value2"));
headers2.add(bs("name2"), bs("value3"));
TestDefaultHeaders headers2 = newInstance();
headers2.add(of("name1"), of("value1"));
headers2.add(of("name2"), of("value2"));
headers2.add(of("name2"), of("value3"));
assertEquals(headers1, headers2);
assertEquals(headers2, headers1);
@ -252,8 +257,8 @@ public class DefaultHeadersTest {
@Test
public void emptyHeadersShouldBeEqual() {
Headers<ByteString> headers1 = newInstance();
Headers<ByteString> headers2 = newInstance();
TestDefaultHeaders headers1 = newInstance();
TestDefaultHeaders headers2 = newInstance();
assertNotSame(headers1, headers2);
assertEquals(headers1, headers2);
assertEquals(headers1.hashCode(), headers2.hashCode());
@ -261,29 +266,29 @@ public class DefaultHeadersTest {
@Test
public void headersWithSameNamesButDifferentValuesShouldNotBeEquivalent() {
Headers<ByteString> headers1 = newInstance();
headers1.add(bs("name1"), bs("value1"));
Headers<ByteString> headers2 = newInstance();
headers1.add(bs("name1"), bs("value2"));
TestDefaultHeaders headers1 = newInstance();
headers1.add(of("name1"), of("value1"));
TestDefaultHeaders headers2 = newInstance();
headers1.add(of("name1"), of("value2"));
assertNotEquals(headers1, headers2);
}
@Test
public void subsetOfHeadersShouldNotBeEquivalent() {
Headers<ByteString> headers1 = newInstance();
headers1.add(bs("name1"), bs("value1"));
headers1.add(bs("name2"), bs("value2"));
Headers<ByteString> headers2 = newInstance();
headers1.add(bs("name1"), bs("value1"));
TestDefaultHeaders headers1 = newInstance();
headers1.add(of("name1"), of("value1"));
headers1.add(of("name2"), of("value2"));
TestDefaultHeaders headers2 = newInstance();
headers1.add(of("name1"), of("value1"));
assertNotEquals(headers1, headers2);
}
@Test
public void headersWithDifferentNamesAndValuesShouldNotBeEquivalent() {
Headers<ByteString> h1 = newInstance();
h1.set(bs("name1"), bs("value1"));
Headers<ByteString> h2 = newInstance();
h2.set(bs("name2"), bs("value2"));
TestDefaultHeaders h1 = newInstance();
h1.set(of("name1"), of("value1"));
TestDefaultHeaders h2 = newInstance();
h2.set(of("name2"), of("value2"));
assertNotEquals(h1, h2);
assertNotEquals(h2, h1);
assertEquals(h1, h1);
@ -292,22 +297,22 @@ public class DefaultHeadersTest {
@Test(expected = NoSuchElementException.class)
public void iterateEmptyHeadersShouldThrow() {
Iterator<Map.Entry<ByteString, ByteString>> iterator = newInstance().iterator();
Iterator<Map.Entry<CharSequence, CharSequence>> iterator = newInstance().iterator();
assertFalse(iterator.hasNext());
iterator.next();
}
@Test
public void iteratorShouldReturnAllNameValuePairs() {
Headers<ByteString> headers1 = newInstance();
headers1.add(bs("name1"), bs("value1"), bs("value2"));
headers1.add(bs("name2"), bs("value3"));
headers1.add(bs("name3"), bs("value4"), bs("value5"), bs("value6"));
headers1.add(bs("name1"), bs("value7"), bs("value8"));
TestDefaultHeaders headers1 = newInstance();
headers1.add(of("name1"), of("value1"), of("value2"));
headers1.add(of("name2"), of("value3"));
headers1.add(of("name3"), of("value4"), of("value5"), of("value6"));
headers1.add(of("name1"), of("value7"), of("value8"));
assertEquals(8, headers1.size());
Headers<ByteString> headers2 = newInstance();
for (Entry<ByteString, ByteString> entry : headers1) {
TestDefaultHeaders headers2 = newInstance();
for (Entry<CharSequence, CharSequence> entry : headers1) {
headers2.add(entry.getKey(), entry.getValue());
}
@ -316,45 +321,45 @@ public class DefaultHeadersTest {
@Test
public void iteratorSetValueShouldChangeHeaderValue() {
Headers<ByteString> headers = newInstance();
headers.add(bs("name1"), bs("value1"), bs("value2"), bs("value3"));
headers.add(bs("name2"), bs("value4"));
TestDefaultHeaders headers = newInstance();
headers.add(of("name1"), of("value1"), of("value2"), of("value3"));
headers.add(of("name2"), of("value4"));
assertEquals(4, headers.size());
Iterator<Entry<ByteString, ByteString>> iter = headers.iterator();
Iterator<Entry<CharSequence, CharSequence>> iter = headers.iterator();
while (iter.hasNext()) {
Entry<ByteString, ByteString> header = iter.next();
if (bs("name1").equals(header.getKey()) && bs("value2").equals(header.getValue())) {
header.setValue(bs("updatedvalue2"));
assertEquals(bs("updatedvalue2"), header.getValue());
Entry<CharSequence, CharSequence> header = iter.next();
if (of("name1").equals(header.getKey()) && of("value2").equals(header.getValue())) {
header.setValue(of("updatedvalue2"));
assertEquals(of("updatedvalue2"), header.getValue());
}
if (bs("name1").equals(header.getKey()) && bs("value3").equals(header.getValue())) {
header.setValue(bs("updatedvalue3"));
assertEquals(bs("updatedvalue3"), header.getValue());
if (of("name1").equals(header.getKey()) && of("value3").equals(header.getValue())) {
header.setValue(of("updatedvalue3"));
assertEquals(of("updatedvalue3"), header.getValue());
}
}
assertEquals(4, headers.size());
assertTrue(headers.contains(bs("name1"), bs("updatedvalue2")));
assertFalse(headers.contains(bs("name1"), bs("value2")));
assertTrue(headers.contains(bs("name1"), bs("updatedvalue3")));
assertFalse(headers.contains(bs("name1"), bs("value3")));
assertTrue(headers.contains(of("name1"), of("updatedvalue2")));
assertFalse(headers.contains(of("name1"), of("value2")));
assertTrue(headers.contains(of("name1"), of("updatedvalue3")));
assertFalse(headers.contains(of("name1"), of("value3")));
}
@Test
public void getAllReturnsEmptyListForUnknownName() {
Headers<ByteString> headers = newInstance();
assertEquals(0, headers.getAll(bs("noname")).size());
TestDefaultHeaders headers = newInstance();
assertEquals(0, headers.getAll(of("noname")).size());
}
@Test
public void setHeadersShouldClearAndOverwrite() {
Headers<ByteString> headers1 = newInstance();
headers1.add(bs("name"), bs("value"));
TestDefaultHeaders headers1 = newInstance();
headers1.add(of("name"), of("value"));
Headers<ByteString> headers2 = newInstance();
headers2.add(bs("name"), bs("newvalue"));
headers2.add(bs("name1"), bs("value1"));
TestDefaultHeaders headers2 = newInstance();
headers2.add(of("name"), of("newvalue"));
headers2.add(of("name1"), of("value1"));
headers1.set(headers2);
assertEquals(headers1, headers2);
@ -362,18 +367,18 @@ public class DefaultHeadersTest {
@Test
public void setAllHeadersShouldOnlyOverwriteHeaders() {
Headers<ByteString> headers1 = newInstance();
headers1.add(bs("name"), bs("value"));
headers1.add(bs("name1"), bs("value1"));
TestDefaultHeaders headers1 = newInstance();
headers1.add(of("name"), of("value"));
headers1.add(of("name1"), of("value1"));
Headers<ByteString> headers2 = newInstance();
headers2.add(bs("name"), bs("newvalue"));
headers2.add(bs("name2"), bs("value2"));
TestDefaultHeaders headers2 = newInstance();
headers2.add(of("name"), of("newvalue"));
headers2.add(of("name2"), of("value2"));
Headers<ByteString> expected = newInstance();
expected.add(bs("name"), bs("newvalue"));
expected.add(bs("name1"), bs("value1"));
expected.add(bs("name2"), bs("value2"));
TestDefaultHeaders expected = newInstance();
expected.add(of("name"), of("newvalue"));
expected.add(of("name1"), of("value1"));
expected.add(of("name2"), of("value2"));
headers1.setAll(headers2);
assertEquals(headers1, expected);
@ -381,17 +386,13 @@ public class DefaultHeadersTest {
@Test(expected = IllegalArgumentException.class)
public void testAddSelf() {
Headers<ByteString> headers = newInstance();
TestDefaultHeaders headers = newInstance();
headers.add(headers);
}
@Test(expected = IllegalArgumentException.class)
public void testSetSelf() {
Headers<ByteString> headers = newInstance();
TestDefaultHeaders headers = newInstance();
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;
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 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 {
if (logger.isDebugEnabled()) {

View File

@ -22,17 +22,20 @@ import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.Random;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static io.netty.util.AsciiString.caseInsensitiveHashCode;
import static io.netty.util.AsciiString.contains;
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
public void testGetBytesStringBuilder() {
final StringBuilder b = new StringBuilder();
@ -44,7 +47,7 @@ public class AsciiStringTest {
for (int i = 0; i < charsets.length; ++i) {
final Charset charset = charsets[i];
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);
}
}
@ -60,7 +63,7 @@ public class AsciiStringTest {
for (int i = 0; i < charsets.length; ++i) {
final Charset charset = charsets[i];
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);
}
}
@ -156,18 +159,17 @@ public class AsciiStringTest {
@Test
public void testCaseSensitivity() {
Random r = new Random();
int i = 0;
for (; i < 32; i++) {
doCaseSensitivity(r, i);
doCaseSensitivity(i);
}
final int min = i;
final int max = 4000;
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
final int upperA = 'A';
final int upperZ = 'Z';
@ -183,22 +185,18 @@ public class AsciiStringTest {
String lowerCaseString = new String(lowerCaseBytes);
AsciiString lowerCaseAscii = new AsciiString(lowerCaseBytes, false);
AsciiString upperCaseAscii = new AsciiString(upperCaseString);
ByteString lowerCaseByteString = new ByteString(lowerCaseBytes);
ByteString upperCaseByteString = new ByteString(upperCaseString, CharsetUtil.US_ASCII);
final String errorString = "len: " + len;
// Test upper case hash codes are equal
final int upperCaseExpected = upperCaseAscii.hashCode();
assertEquals(errorString, upperCaseExpected, AsciiString.hashCode(upperCaseBuilder));
assertEquals(errorString, upperCaseExpected, AsciiString.hashCode(upperCaseString));
assertEquals(errorString, upperCaseExpected, upperCaseAscii.hashCode());
assertEquals(errorString, upperCaseExpected, upperCaseByteString.hashCode());
// Test lower case hash codes are equal
final int lowerCaseExpected = lowerCaseAscii.hashCode();
assertEquals(errorString, lowerCaseExpected, AsciiString.hashCode(lowerCaseAscii));
assertEquals(errorString, lowerCaseExpected, AsciiString.hashCode(lowerCaseString));
assertEquals(errorString, lowerCaseExpected, lowerCaseAscii.hashCode());
assertEquals(errorString, lowerCaseExpected, lowerCaseByteString.hashCode());
// Test case insensitive hash codes are equal
final int expectedCaseInsensative = lowerCaseAscii.hashCodeCaseInsensitive();
@ -210,11 +208,9 @@ public class AsciiStringTest {
assertEquals(errorString, expectedCaseInsensative, lowerCaseAscii.hashCodeCaseInsensitive());
assertEquals(errorString, expectedCaseInsensative, upperCaseAscii.hashCodeCaseInsensitive());
// Test that opposite cases are not equal
if (len != 0) {
assertNotEquals(errorString, lowerCaseAscii.hashCode(), AsciiString.hashCode(upperCaseString));
assertNotEquals(errorString, upperCaseAscii.hashCode(), AsciiString.hashCode(lowerCaseString));
}
// Test that opposite cases are equal
assertEquals(errorString, lowerCaseAscii.hashCode(), AsciiString.hashCode(upperCaseString));
assertEquals(errorString, upperCaseAscii.hashCode(), AsciiString.hashCode(lowerCaseString));
}
@Test
@ -228,4 +224,13 @@ public class AsciiStringTest {
CharBuffer buffer = CharBuffer.wrap(array, offset, s1.length());
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;
/**
* 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[] b;
private int aOffset = 22;
private int bOffset = 53;
private int length = 100;
private ByteString aByteString;
private ByteString bByteString;
private AsciiString aAsciiString;
private AsciiString bAsciiString;
private Random r = new Random();
@Before
@ -47,32 +47,32 @@ public class ByteStringTest {
bOffset = 53;
length = 100;
System.arraycopy(a, aOffset, b, bOffset, length);
aByteString = new ByteString(a, aOffset, length, false);
bByteString = new ByteString(b, bOffset, length, false);
aAsciiString = new AsciiString(a, aOffset, length, false);
bAsciiString = new AsciiString(b, bOffset, length, false);
}
@Test
public void testSharedMemory() {
++a[aOffset];
ByteString aByteString1 = new ByteString(a, aOffset, length, true);
ByteString aByteString2 = new ByteString(a, aOffset, length, false);
assertEquals(aByteString, aByteString1);
assertEquals(aByteString, aByteString2);
AsciiString aAsciiString1 = new AsciiString(a, aOffset, length, true);
AsciiString aAsciiString2 = new AsciiString(a, aOffset, length, false);
assertEquals(aAsciiString, aAsciiString1);
assertEquals(aAsciiString, aAsciiString2);
for (int i = aOffset; i < length; ++i) {
assertEquals(a[i], aByteString.byteAt(i - aOffset));
assertEquals(a[i], aAsciiString.byteAt(i - aOffset));
}
}
@Test
public void testNotSharedMemory() {
ByteString aByteString1 = new ByteString(a, aOffset, length, true);
AsciiString aAsciiString1 = new AsciiString(a, aOffset, length, true);
++a[aOffset];
assertNotEquals(aByteString, aByteString1);
assertNotEquals(aAsciiString, aAsciiString1);
int i = aOffset;
assertNotEquals(a[i], aByteString1.byteAt(i - aOffset));
assertNotEquals(a[i], aAsciiString1.byteAt(i - aOffset));
++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 {
final AtomicReference<Integer> aCount = new AtomicReference<Integer>(0);
final AtomicReference<Integer> bCount = new AtomicReference<Integer>(0);
aByteString.forEachByte(new ByteProcessor() {
aAsciiString.forEachByte(new ByteProcessor() {
int i;
@Override
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);
return true;
}
});
bByteString.forEachByte(new ByteProcessor() {
bAsciiString.forEachByte(new ByteProcessor() {
int i;
@Override
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);
return true;
}
});
assertEquals(aByteString.length(), aCount.get().intValue());
assertEquals(bByteString.length(), bCount.get().intValue());
assertEquals(aAsciiString.length(), aCount.get().intValue());
assertEquals(bAsciiString.length(), bCount.get().intValue());
}
@Test
public void forEachDescTest() throws Exception {
final AtomicReference<Integer> aCount = new AtomicReference<Integer>(0);
final AtomicReference<Integer> bCount = new AtomicReference<Integer>(0);
aByteString.forEachByteDesc(new ByteProcessor() {
aAsciiString.forEachByteDesc(new ByteProcessor() {
int i = 1;
@Override
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);
return true;
}
});
bByteString.forEachByteDesc(new ByteProcessor() {
bAsciiString.forEachByteDesc(new ByteProcessor() {
int i = 1;
@Override
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);
return true;
}
});
assertEquals(aByteString.length(), aCount.get().intValue());
assertEquals(bByteString.length(), bCount.get().intValue());
assertEquals(aAsciiString.length(), aCount.get().intValue());
assertEquals(bAsciiString.length(), bCount.get().intValue());
}
@Test
public void subSequenceTest() {
final int start = 12;
final int end = aByteString.length();
ByteString aSubSequence = aByteString.subSequence(start, end, false);
ByteString bSubSequence = bByteString.subSequence(start, end, true);
final int end = aAsciiString.length();
AsciiString aSubSequence = aAsciiString.subSequence(start, end, false);
AsciiString bSubSequence = bAsciiString.subSequence(start, end, true);
assertEquals(aSubSequence, bSubSequence);
assertEquals(aSubSequence.hashCode(), bSubSequence.hashCode());
}
@Test
public void copyTest() {
byte[] aCopy = new byte[aByteString.length()];
aByteString.copy(0, aCopy, 0, aCopy.length);
ByteString aByteStringCopy = new ByteString(aCopy, false);
assertEquals(aByteString, aByteStringCopy);
byte[] aCopy = new byte[aAsciiString.length()];
aAsciiString.copy(0, aCopy, 0, aCopy.length);
AsciiString aAsciiStringCopy = new AsciiString(aCopy, false);
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.microbench.util.AbstractMicrobenchmark;
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.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
@ -53,9 +51,6 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
AsciiString[] httpNames;
AsciiString[] httpValues;
ByteString[] http2Names;
ByteString[] http2Values;
DefaultHttpHeaders httpHeaders;
DefaultHttp2Headers http2Headers;
@ -64,8 +59,6 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
Map<String, String> headers = ExampleHeaders.EXAMPLES.get(exampleHeader);
httpNames = new AsciiString[headers.size()];
httpValues = new AsciiString[headers.size()];
http2Names = new ByteString[headers.size()];
http2Values = new ByteString[headers.size()];
httpHeaders = new DefaultHttpHeaders(false);
http2Headers = new DefaultHttp2Headers(false);
int idx = 0;
@ -74,11 +67,9 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
String value = header.getValue();
httpNames[idx] = new AsciiString(name);
httpValues[idx] = new AsciiString(value);
http2Names[idx] = new ByteString(name, CharsetUtil.US_ASCII);
http2Values[idx] = new ByteString(value, CharsetUtil.US_ASCII);
idx++;
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
@BenchmarkMode(Mode.AverageTime)
public void http2Remove(Blackhole bh) {
for (ByteString name : http2Names) {
for (AsciiString name : httpNames) {
bh.consume(http2Headers.remove(name));
}
}
@ -128,7 +119,7 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public void http2Get(Blackhole bh) {
for (ByteString name : http2Names) {
for (AsciiString name : httpNames) {
bh.consume(http2Headers.get(name));
}
}
@ -146,7 +137,7 @@ public class HeadersBenchmark extends AbstractMicrobenchmark {
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public void http2Iterate(Blackhole bh) {
for (Entry<ByteString, ByteString> entry : http2Headers) {
for (Entry<CharSequence, CharSequence> entry : http2Headers) {
bh.consume(entry);
}
}