From 19658e9cd80b57340df58ae573fa9dcbd141416a Mon Sep 17 00:00:00 2001 From: Scott Mitchell Date: Tue, 27 Oct 2015 13:47:47 -0700 Subject: [PATCH] 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 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 to Headers - Change Http2Headers to Http2Headers - 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. --- .../java/io/netty/buffer/AbstractByteBuf.java | 11 +- .../buffer/AbstractUnsafeSwappedByteBuf.java | 4 +- .../java/io/netty/buffer/ByteBufUtil.java | 28 +- .../io/netty/buffer/UnsafeByteBufUtil.java | 10 +- .../codec/http/CombinedHttpHeaders.java | 3 +- .../codec/http/DefaultHttpHeaders.java | 55 +- .../codec/http/HttpHeadersEncoder.java | 15 +- .../codec/http/HttpResponseStatus.java | 9 +- .../codec/spdy/DefaultSpdyHeaders.java | 227 +---- .../codec/spdy/SpdyHeaderBlockRawEncoder.java | 17 +- .../netty/handler/codec/spdy/SpdyHeaders.java | 104 +-- .../handler/codec/http2/CharSequenceMap.java | 37 + .../CompressorHttp2ConnectionEncoder.java | 30 +- .../DefaultHttp2HeaderTableListSize.java | 3 - .../codec/http2/DefaultHttp2Headers.java | 301 ++----- .../http2/DefaultHttp2HeadersDecoder.java | 20 +- .../http2/DefaultHttp2HeadersEncoder.java | 30 +- .../DelegatingDecompressorFrameListener.java | 30 +- .../codec/http2/EmptyHttp2Headers.java | 229 +---- .../handler/codec/http2/Http2Headers.java | 147 +-- .../codec/http2/Http2HeadersEncoder.java | 5 +- .../codec/http2/HttpConversionUtil.java | 99 +- .../codec/http2/StreamBufferingEncoder.java | 18 +- .../codec/http2/DefaultHttp2FrameIOTest.java | 3 +- .../codec/http2/DefaultHttp2HeadersTest.java | 50 +- .../handler/codec/http2/Http2TestUtil.java | 5 +- .../codec/stomp/DefaultStompHeaders.java | 208 +---- .../handler/codec/stomp/StompHeaders.java | 104 +-- .../codec/ByteStringValueConverter.java | 140 --- .../codec/CharSequenceValueConverter.java | 27 +- .../netty/handler/codec/DefaultHeaders.java | 402 +++++---- .../handler/codec/DefaultHeadersImpl.java | 29 + .../io/netty/handler/codec/EmptyHeaders.java | 199 +++-- .../java/io/netty/handler/codec/Headers.java | 192 ++-- .../io/netty/handler/codec/HeadersUtils.java | 12 +- .../codec/UnsupportedValueConverter.java | 125 +++ .../handler/codec/DefaultHeadersTest.java | 369 ++++---- .../main/java/io/netty/util/AsciiString.java | 844 ++++++++++++++---- .../main/java/io/netty/util/ByteString.java | 705 --------------- .../java/io/netty/util/internal/MathUtil.java | 12 + .../util/internal/PlatformDependent.java | 2 +- ...est.java => AsciiStringCharacterTest.java} | 45 +- ...ngTest.java => AsciiStringMemoryTest.java} | 68 +- .../microbench/headers/HeadersBenchmark.java | 17 +- 44 files changed, 1902 insertions(+), 3088 deletions(-) create mode 100644 codec-http2/src/main/java/io/netty/handler/codec/http2/CharSequenceMap.java delete mode 100644 codec/src/main/java/io/netty/handler/codec/ByteStringValueConverter.java create mode 100644 codec/src/main/java/io/netty/handler/codec/DefaultHeadersImpl.java create mode 100644 codec/src/main/java/io/netty/handler/codec/UnsupportedValueConverter.java delete mode 100644 common/src/main/java/io/netty/util/ByteString.java rename common/src/test/java/io/netty/util/{AsciiStringTest.java => AsciiStringCharacterTest.java} (87%) rename common/src/test/java/io/netty/util/{ByteStringTest.java => AsciiStringMemoryTest.java} (58%) diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java index 8ca303bfcd..6ede8208a3 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java @@ -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 diff --git a/buffer/src/main/java/io/netty/buffer/AbstractUnsafeSwappedByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractUnsafeSwappedByteBuf.java index 66ba5c3689..6d9667c7de 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractUnsafeSwappedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractUnsafeSwappedByteBuf.java @@ -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 diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java index 48aa9c75c8..10a4c1dd01 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java @@ -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() + ')'); diff --git a/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java index 9fa3fbf7bf..6186a610bf 100644 --- a/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java +++ b/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java @@ -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); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java index 1145fd6ea0..6b95464035 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/CombinedHttpHeaders.java @@ -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 { + private static final class CombinedHttpHeadersImpl + extends DefaultHeaders { /** * An estimate of the size of a header value. */ diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java index fdea966c45..e49790c3f2 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpHeaders.java @@ -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 headers; + private final DefaultHeaders headers; public DefaultHttpHeaders() { this(true); @@ -80,10 +79,12 @@ public class DefaultHttpHeaders extends HttpHeaders { } protected DefaultHttpHeaders(boolean validate, NameValidator nameValidator) { - this(new DefaultHeaders(CASE_INSENSITIVE_HASHER, valueConverter(validate), nameValidator)); + this(new DefaultHeadersImpl(CASE_INSENSITIVE_HASHER, + valueConverter(validate), + nameValidator)); } - protected DefaultHttpHeaders(DefaultHeaders headers) { + protected DefaultHttpHeaders(DefaultHeaders 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); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeadersEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeadersEncoder.java index 544a3c5302..888a829c4c 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeadersEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeadersEncoder.java @@ -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 : '?'; - } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java index 54b57c18a3..10c3eee0ea 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseStatus.java @@ -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 { 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 { 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 { } 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 { * * @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); diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeaders.java index 07efc34145..a7c1c8e5d3 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeaders.java @@ -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 implements SpdyHeaders { +public class DefaultSpdyHeaders extends DefaultHeaders implements SpdyHeaders { private static final NameValidator SpydNameValidator = new NameValidator() { @Override public void validateName(CharSequence name) { @@ -42,214 +41,10 @@ public class DefaultSpdyHeaders extends DefaultHeaders 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 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 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 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 headers) { - super.set(headers); - return this; - } - - @Override - public SpdyHeaders setAll(Headers 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 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 diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockRawEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockRawEncoder.java index 0b93fa28c4..afd547966a 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockRawEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockRawEncoder.java @@ -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; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaders.java index ebbcd52340..4078adb086 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaders.java @@ -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 { +public interface SpdyHeaders extends Headers { /** * SPDY HTTP header names @@ -60,108 +60,6 @@ public interface SpdyHeaders extends Headers { private HttpNames() { } } - @Override - SpdyHeaders add(CharSequence name, CharSequence value); - - @Override - SpdyHeaders add(CharSequence name, Iterable 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 headers); - - @Override - SpdyHeaders set(CharSequence name, CharSequence value); - - @Override - SpdyHeaders set(CharSequence name, Iterable 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 headers); - - @Override - SpdyHeaders setAll(Headers 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 diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/CharSequenceMap.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/CharSequenceMap.java new file mode 100644 index 0000000000..8a80e6caca --- /dev/null +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/CharSequenceMap.java @@ -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 extends DefaultHeaders> { + public CharSequenceMap() { + this(true); + } + + public CharSequenceMap(boolean caseSensative) { + this(caseSensative, UnsupportedValueConverter.instance()); + } + + public CharSequenceMap(boolean caseSensative, ValueConverter valueConverter) { + super(caseSensative ? CASE_SENSITIVE_HASHER : CASE_INSENSITIVE_HASHER, valueConverter); + } +} diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java index c907f672b7..6e68f6a841 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java @@ -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); diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeaderTableListSize.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeaderTableListSize.java index 7c043dfb0e..69d66aab23 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeaderTableListSize.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeaderTableListSize.java @@ -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} */ diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Headers.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Headers.java index 1c5ee94d0f..7a4bfe6043 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Headers.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Headers.java @@ -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 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 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 HTTP2_NAME_VALIDATOR = new NameValidator() { + private static final NameValidator HTTP2_NAME_VALIDATOR = new NameValidator() { @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 firstNonPseudo = head; + + private HeaderEntry firstNonPseudo = head; /** * Create a new instance. @@ -69,282 +83,83 @@ public class DefaultHttp2Headers extends DefaultHeaders 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 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 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 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 headers) { - super.set(headers); - return this; - } - - @Override - public Http2Headers setAll(Headers 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 newHeaderEntry(int h, ByteString name, ByteString value, - HeaderEntry next) { + protected final HeaderEntry newHeaderEntry(int h, CharSequence name, CharSequence value, + HeaderEntry next) { return new Http2HeaderEntry(h, name, value, next); } - private final class Http2HeaderEntry extends HeaderEntry { - protected Http2HeaderEntry(int hash, ByteString key, ByteString value, HeaderEntry next) { + private final class Http2HeaderEntry extends HeaderEntry { + protected Http2HeaderEntry(int hash, CharSequence key, CharSequence value, + HeaderEntry 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 { diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoder.java index b932b8277e..688498c9d0 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersDecoder.java @@ -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)); } }; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersEncoder.java index 69bc69f3df..33f948479b 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2HeadersEncoder.java @@ -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 header : headers) { + for (Entry 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)); } /** diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DelegatingDecompressorFrameListener.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DelegatingDecompressorFrameListener.java index 87ecc387ca..dca4209a9a 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DelegatingDecompressorFrameListener.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DelegatingDecompressorFrameListener.java @@ -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); diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/EmptyHttp2Headers.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/EmptyHttp2Headers.java index e55f7554f7..30da6648ad 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/EmptyHttp2Headers.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/EmptyHttp2Headers.java @@ -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 implements Http2Headers { +public final class EmptyHttp2Headers + extends EmptyHeaders 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 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 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 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 headers) { - super.set(headers); - return this; - } - - @Override - public Http2Headers setAll(Headers 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()); } } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Headers.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Headers.java index abaad7b75a..3c84716d15 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Headers.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Headers.java @@ -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 { +public interface Http2Headers extends Headers { /** * HTTP/2 pseudo-headers names. @@ -58,19 +55,19 @@ public interface Http2Headers extends Headers { */ STATUS(":status"); - private final ByteString value; - private static final Set PSEUDO_HEADERS = new HashSet(); + private final AsciiString value; + private static final CharSequenceMap PSEUDO_HEADERS = new CharSequenceMap(); 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 { /** * 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 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 headers); - - @Override - Http2Headers set(ByteString name, ByteString value); - - @Override - Http2Headers set(ByteString name, Iterable 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 headers); - - @Override - Http2Headers setAll(Headers 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> iterator(); + Iterator> 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(); } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersEncoder.java index 8f816aa679..9b5a38d5c9 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2HeadersEncoder.java @@ -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 { * sensitive. * {@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; } }; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpConversionUtil.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpConversionUtil.java index 2a8e41e1b2..9fea9736f0 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpConversionUtil.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpConversionUtil.java @@ -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 HTTP_TO_HTTP2_HEADER_BLACKLIST = new HashSet() { - 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 HTTP_TO_HTTP2_HEADER_BLACKLIST = + new CharSequenceMap(); + 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 entry : inputHeaders) { + for (Entry entry : inputHeaders) { translator.translate(entry); } } catch (Http2Exception ex) { @@ -326,12 +324,10 @@ public final class HttpConversionUtil { Entry 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 - REQUEST_HEADER_TRANSLATIONS = new HashMap(); - private static final Map - RESPONSE_HEADER_TRANSLATIONS = new HashMap(); + private static final CharSequenceMap + REQUEST_HEADER_TRANSLATIONS = new CharSequenceMap(); + private static final CharSequenceMap + RESPONSE_HEADER_TRANSLATIONS = new CharSequenceMap(); 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 translations; + private final CharSequenceMap 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 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 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)); } } } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/StreamBufferingEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/StreamBufferingEncoder.java index 7ed8e860c2..cb36801585 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/StreamBufferingEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/StreamBufferingEncoder.java @@ -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 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) { diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameIOTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameIOTest.java index 756501c880..18bd2e90a4 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameIOTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameIOTest.java @@ -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); diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersTest.java index 6a90ed0b9a..5885ea153c 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2HeadersTest.java @@ -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 nonPseudoHeaders = new HashSet(headers.size()); - for (Entry entry : headers) { - if (entry.getKey().isEmpty() || entry.getKey().byteAt(0) != ':') { - nonPseudoHeaders.add(entry.getKey()); + Http2Headers nonPseudoHeaders = new DefaultHttp2Headers(); + for (Entry 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 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 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 entry: headers) { - if (entry.getKey().isEmpty() || entry.getKey().byteAt(0) != ':') { + CharSequence lastNonPseudoName = null; + for (Entry 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; } } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java index b84aee0a14..934495e116 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java @@ -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) { diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompHeaders.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompHeaders.java index 119c3a0ac6..80fccf0f66 100644 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompHeaders.java +++ b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompHeaders.java @@ -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 implements StompHeaders { +public class DefaultStompHeaders + extends DefaultHeaders 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 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 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 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 headers) { - super.set(headers); - return this; - } - - @Override - public StompHeaders setAll(Headers 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); diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompHeaders.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompHeaders.java index 55cfa3d0ea..608475f14c 100644 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompHeaders.java +++ b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompHeaders.java @@ -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 { +public interface StompHeaders extends Headers { AsciiString ACCEPT_VERSION = new AsciiString("accept-version"); AsciiString HOST = new AsciiString("host"); @@ -48,108 +48,6 @@ public interface StompHeaders extends Headers { 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 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 headers); - - @Override - StompHeaders set(CharSequence name, CharSequence value); - - @Override - StompHeaders set(CharSequence name, Iterable 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 headers); - - @Override - StompHeaders setAll(Headers 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 diff --git a/codec/src/main/java/io/netty/handler/codec/ByteStringValueConverter.java b/codec/src/main/java/io/netty/handler/codec/ByteStringValueConverter.java deleted file mode 100644 index 76df1a4e1a..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/ByteStringValueConverter.java +++ /dev/null @@ -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 { - 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); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/CharSequenceValueConverter.java b/codec/src/main/java/io/netty/handler/codec/CharSequenceValueConverter.java index c59450ee45..0e243ce645 100644 --- a/codec/src/main/java/io/netty/handler/codec/CharSequenceValueConverter.java +++ b/codec/src/main/java/io/netty/handler/codec/CharSequenceValueConverter.java @@ -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 @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 @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 @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 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()); } } diff --git a/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java b/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java index 4f18c56146..89fe19561e 100644 --- a/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java @@ -40,9 +40,11 @@ import static io.netty.util.internal.ObjectUtil.checkNotNull; /** * Default implementation of {@link Headers}; * - * @param the type of the header name and value. + * @param the type of the header name. + * @param the type of the header value. + * @param the type to use for return values when the intention is to return {@code this} object. */ -public class DefaultHeaders implements Headers { +public class DefaultHeaders> implements Headers { /** * How big the underlying array is for the hash data structure. *

@@ -50,6 +52,7 @@ public class DefaultHeaders implements Headers { */ private static final int ARRAY_SIZE = 1 << 4; private static final int HASH_MASK = ARRAY_SIZE - 1; + static final int HASH_CODE_SEED = 0xc2b2ae35; // constant borrowed from murmur3 private static int index(int hash) { // Fold the upper 16 bits onto the 16 lower bits so more of the hash code is represented @@ -58,21 +61,21 @@ public class DefaultHeaders implements Headers { } @SuppressWarnings("unchecked") - private final HeaderEntry[] entries = new DefaultHeaders.HeaderEntry[ARRAY_SIZE]; - protected final HeaderEntry head = new HeaderEntry(); + private final HeaderEntry[] entries = new DefaultHeaders.HeaderEntry[ARRAY_SIZE]; + protected final HeaderEntry head = new HeaderEntry(); - private final ValueConverter valueConverter; - private final NameValidator nameValidator; - private final HashingStrategy hashingStrategy; + private final ValueConverter valueConverter; + private final NameValidator nameValidator; + private final HashingStrategy hashingStrategy; int size; - public interface NameValidator { + public interface NameValidator { /** * Verify that {@code name} is valid. * @param name The name to validate. * @throws RuntimeException if {@code name} is not valid. */ - void validateName(T name); + void validateName(K name); @SuppressWarnings("rawtypes") NameValidator NOT_NULL = new NameValidator() { @@ -84,22 +87,22 @@ public class DefaultHeaders implements Headers { } @SuppressWarnings("unchecked") - public DefaultHeaders(ValueConverter valueConverter) { + public DefaultHeaders(ValueConverter valueConverter) { this(JAVA_HASHER, valueConverter); } @SuppressWarnings("unchecked") - public DefaultHeaders(ValueConverter valueConverter, NameValidator nameValidator) { + public DefaultHeaders(ValueConverter valueConverter, NameValidator nameValidator) { this(JAVA_HASHER, valueConverter, nameValidator); } @SuppressWarnings("unchecked") - public DefaultHeaders(HashingStrategy nameHashingStrategy, ValueConverter valueConverter) { + public DefaultHeaders(HashingStrategy nameHashingStrategy, ValueConverter valueConverter) { this(nameHashingStrategy, valueConverter, NameValidator.NOT_NULL); } - public DefaultHeaders(HashingStrategy nameHashingStrategy, - ValueConverter valueConverter, NameValidator nameValidator) { + public DefaultHeaders(HashingStrategy nameHashingStrategy, + ValueConverter valueConverter, NameValidator nameValidator) { this.valueConverter = checkNotNull(valueConverter, "valueConverter"); this.nameValidator = checkNotNull(nameValidator, "nameValidator"); this.hashingStrategy = checkNotNull(nameHashingStrategy, "nameHashingStrategy"); @@ -107,13 +110,13 @@ public class DefaultHeaders implements Headers { } @Override - public T get(T name) { + public V get(K name) { checkNotNull(name, "name"); int h = hashingStrategy.hashCode(name); int i = index(h); - HeaderEntry e = entries[i]; - T value = null; + HeaderEntry e = entries[i]; + V value = null; // loop until the first header was found while (e != null) { if (e.hash == h && hashingStrategy.equals(name, e.key)) { @@ -126,8 +129,8 @@ public class DefaultHeaders implements Headers { } @Override - public T get(T name, T defaultValue) { - T value = get(name); + public V get(K name, V defaultValue) { + V value = get(name); if (value == null) { return defaultValue; } @@ -135,14 +138,14 @@ public class DefaultHeaders implements Headers { } @Override - public T getAndRemove(T name) { + public V getAndRemove(K name) { int h = hashingStrategy.hashCode(name); return remove0(h, index(h), checkNotNull(name, "name")); } @Override - public T getAndRemove(T name, T defaultValue) { - T value = getAndRemove(name); + public V getAndRemove(K name, V defaultValue) { + V value = getAndRemove(name); if (value == null) { return defaultValue; } @@ -150,14 +153,14 @@ public class DefaultHeaders implements Headers { } @Override - public List getAll(T name) { + public List getAll(K name) { checkNotNull(name, "name"); - LinkedList values = new LinkedList(); + LinkedList values = new LinkedList(); int h = hashingStrategy.hashCode(name); int i = index(h); - HeaderEntry e = entries[i]; + HeaderEntry e = entries[i]; while (e != null) { if (e.hash == h && hashingStrategy.equals(name, e.key)) { values.addFirst(e.getValue()); @@ -168,79 +171,79 @@ public class DefaultHeaders implements Headers { } @Override - public List getAllAndRemove(T name) { - List all = getAll(name); + public List getAllAndRemove(K name) { + List all = getAll(name); remove(name); return all; } @Override - public boolean contains(T name) { + public boolean contains(K name) { return get(name) != null; } @Override - public boolean containsObject(T name, Object value) { + public boolean containsObject(K name, Object value) { return contains(name, valueConverter.convertObject(checkNotNull(value, "value"))); } @Override - public boolean containsBoolean(T name, boolean value) { + public boolean containsBoolean(K name, boolean value) { return contains(name, valueConverter.convertBoolean(value)); } @Override - public boolean containsByte(T name, byte value) { + public boolean containsByte(K name, byte value) { return contains(name, valueConverter.convertByte(value)); } @Override - public boolean containsChar(T name, char value) { + public boolean containsChar(K name, char value) { return contains(name, valueConverter.convertChar(value)); } @Override - public boolean containsShort(T name, short value) { + public boolean containsShort(K name, short value) { return contains(name, valueConverter.convertShort(value)); } @Override - public boolean containsInt(T name, int value) { + public boolean containsInt(K name, int value) { return contains(name, valueConverter.convertInt(value)); } @Override - public boolean containsLong(T name, long value) { + public boolean containsLong(K name, long value) { return contains(name, valueConverter.convertLong(value)); } @Override - public boolean containsFloat(T name, float value) { + public boolean containsFloat(K name, float value) { return contains(name, valueConverter.convertFloat(value)); } @Override - public boolean containsDouble(T name, double value) { + public boolean containsDouble(K name, double value) { return contains(name, valueConverter.convertDouble(value)); } @Override - public boolean containsTimeMillis(T name, long value) { + public boolean containsTimeMillis(K name, long value) { return contains(name, valueConverter.convertTimeMillis(value)); } @SuppressWarnings("unchecked") @Override - public boolean contains(T name, T value) { + public boolean contains(K name, V value) { return contains(name, value, JAVA_HASHER); } - public final boolean contains(T name, T value, HashingStrategy valueHashingStrategy) { + public final boolean contains(K name, V value, HashingStrategy valueHashingStrategy) { checkNotNull(name, "name"); int h = hashingStrategy.hashCode(name); int i = index(h); - HeaderEntry e = entries[i]; + HeaderEntry e = entries[i]; while (e != null) { if (e.hash == h && hashingStrategy.equals(name, e.key) && valueHashingStrategy.equals(value, e.value)) { return true; @@ -261,12 +264,12 @@ public class DefaultHeaders implements Headers { } @Override - public Set names() { + public Set names() { if (isEmpty()) { return Collections.emptySet(); } - Set names = new LinkedHashSet(size()); - HeaderEntry e = head.after; + Set names = new LinkedHashSet(size()); + HeaderEntry e = head.after; while (e != head) { names.add(e.getKey()); e = e.after; @@ -275,141 +278,141 @@ public class DefaultHeaders implements Headers { } @Override - public Headers add(T name, T value) { + public T add(K name, V value) { nameValidator.validateName(name); checkNotNull(value, "value"); int h = hashingStrategy.hashCode(name); int i = index(h); add0(h, i, name, value); - return this; + return thisT(); } @Override - public Headers add(T name, Iterable values) { + public T add(K name, Iterable values) { nameValidator.validateName(name); int h = hashingStrategy.hashCode(name); int i = index(h); - for (T v: values) { + for (V v: values) { add0(h, i, name, v); } - return this; + return thisT(); } @Override - public Headers add(T name, T... values) { + public T add(K name, V... values) { nameValidator.validateName(name); int h = hashingStrategy.hashCode(name); int i = index(h); - for (T v: values) { + for (V v: values) { add0(h, i, name, v); } - return this; + return thisT(); } @Override - public Headers addObject(T name, Object value) { + public T addObject(K name, Object value) { return add(name, valueConverter.convertObject(checkNotNull(value, "value"))); } @Override - public Headers addObject(T name, Iterable values) { + public T addObject(K name, Iterable values) { checkNotNull(values, "values"); for (Object value : values) { addObject(name, value); } - return this; + return thisT(); } @Override - public Headers addObject(T name, Object... values) { + public T addObject(K name, Object... values) { checkNotNull(values, "values"); for (int i = 0; i < values.length; i++) { addObject(name, values[i]); } - return this; + return thisT(); } @Override - public Headers addInt(T name, int value) { + public T addInt(K name, int value) { return add(name, valueConverter.convertInt(value)); } @Override - public Headers addLong(T name, long value) { + public T addLong(K name, long value) { return add(name, valueConverter.convertLong(value)); } @Override - public Headers addDouble(T name, double value) { + public T addDouble(K name, double value) { return add(name, valueConverter.convertDouble(value)); } @Override - public Headers addTimeMillis(T name, long value) { + public T addTimeMillis(K name, long value) { return add(name, valueConverter.convertTimeMillis(value)); } @Override - public Headers addChar(T name, char value) { + public T addChar(K name, char value) { return add(name, valueConverter.convertChar(value)); } @Override - public Headers addBoolean(T name, boolean value) { + public T addBoolean(K name, boolean value) { return add(name, valueConverter.convertBoolean(value)); } @Override - public Headers addFloat(T name, float value) { + public T addFloat(K name, float value) { return add(name, valueConverter.convertFloat(value)); } @Override - public Headers addByte(T name, byte value) { + public T addByte(K name, byte value) { return add(name, valueConverter.convertByte(value)); } @Override - public Headers addShort(T name, short value) { + public T addShort(K name, short value) { return add(name, valueConverter.convertShort(value)); } @Override - public Headers add(Headers headers) { + public T add(Headers headers) { checkNotNull(headers, "headers"); if (headers == this) { throw new IllegalArgumentException("can't add to itself."); } if (headers instanceof DefaultHeaders) { @SuppressWarnings("unchecked") - DefaultHeaders defaultHeaders = (DefaultHeaders) headers; - HeaderEntry e = defaultHeaders.head.after; + DefaultHeaders defaultHeaders = (DefaultHeaders) headers; + HeaderEntry e = defaultHeaders.head.after; while (e != defaultHeaders.head) { add(e.key, e.value); e = e.after; } - return this; + return thisT(); } else { - for (Entry header : headers) { + for (Entry header : headers) { add(header.getKey(), header.getValue()); } } - return this; + return thisT(); } @Override - public Headers set(T name, T value) { + public T set(K name, V value) { nameValidator.validateName(name); checkNotNull(value, "value"); int h = hashingStrategy.hashCode(name); int i = index(h); remove0(h, i, name); add0(h, i, name, value); - return this; + return thisT(); } @Override - public Headers set(T name, Iterable values) { + public T set(K name, Iterable values) { nameValidator.validateName(name); checkNotNull(values, "values"); @@ -417,18 +420,18 @@ public class DefaultHeaders implements Headers { int i = index(h); remove0(h, i, name); - for (T v: values) { + for (V v: values) { if (v == null) { break; } add0(h, i, name, v); } - return this; + return thisT(); } @Override - public Headers set(T name, T... values) { + public T set(K name, V... values) { nameValidator.validateName(name); checkNotNull(values, "values"); @@ -436,25 +439,25 @@ public class DefaultHeaders implements Headers { int i = index(h); remove0(h, i, name); - for (T v: values) { + for (V v: values) { if (v == null) { break; } add0(h, i, name, v); } - return this; + return thisT(); } @Override - public Headers setObject(T name, Object value) { + public T setObject(K name, Object value) { checkNotNull(value, "value"); - T convertedValue = checkNotNull(valueConverter.convertObject(value), "convertedValue"); + V convertedValue = checkNotNull(valueConverter.convertObject(value), "convertedValue"); return set(name, convertedValue); } @Override - public Headers setObject(T name, Iterable values) { + public T setObject(K name, Iterable values) { nameValidator.validateName(name); checkNotNull(values, "values"); @@ -469,11 +472,11 @@ public class DefaultHeaders implements Headers { add0(h, i, name, valueConverter.convertObject(v)); } - return this; + return thisT(); } @Override - public Headers setObject(T name, Object... values) { + public T setObject(K name, Object... values) { nameValidator.validateName(name); checkNotNull(values, "values"); @@ -488,56 +491,56 @@ public class DefaultHeaders implements Headers { add0(h, i, name, valueConverter.convertObject(v)); } - return this; + return thisT(); } @Override - public Headers setInt(T name, int value) { + public T setInt(K name, int value) { return set(name, valueConverter.convertInt(value)); } @Override - public Headers setLong(T name, long value) { + public T setLong(K name, long value) { return set(name, valueConverter.convertLong(value)); } @Override - public Headers setDouble(T name, double value) { + public T setDouble(K name, double value) { return set(name, valueConverter.convertDouble(value)); } @Override - public Headers setTimeMillis(T name, long value) { + public T setTimeMillis(K name, long value) { return set(name, valueConverter.convertTimeMillis(value)); } @Override - public Headers setFloat(T name, float value) { + public T setFloat(K name, float value) { return set(name, valueConverter.convertFloat(value)); } @Override - public Headers setChar(T name, char value) { + public T setChar(K name, char value) { return set(name, valueConverter.convertChar(value)); } @Override - public Headers setBoolean(T name, boolean value) { + public T setBoolean(K name, boolean value) { return set(name, valueConverter.convertBoolean(value)); } @Override - public Headers setByte(T name, byte value) { + public T setByte(K name, byte value) { return set(name, valueConverter.convertByte(value)); } @Override - public Headers setShort(T name, short value) { + public T setShort(K name, short value) { return set(name, valueConverter.convertShort(value)); } @Override - public Headers set(Headers headers) { + public T set(Headers headers) { checkNotNull(headers, "headers"); if (headers == this) { throw new IllegalArgumentException("can't add to itself."); @@ -545,8 +548,8 @@ public class DefaultHeaders implements Headers { clear(); if (headers instanceof DefaultHeaders) { @SuppressWarnings("unchecked") - DefaultHeaders defaultHeaders = (DefaultHeaders) headers; - HeaderEntry e = defaultHeaders.head.after; + DefaultHeaders defaultHeaders = (DefaultHeaders) headers; + HeaderEntry e = defaultHeaders.head.after; while (e != defaultHeaders.head) { add(e.key, e.value); e = e.after; @@ -554,177 +557,177 @@ public class DefaultHeaders implements Headers { } else { add(headers); } - return this; + return thisT(); } @Override - public Headers setAll(Headers headers) { + public T setAll(Headers headers) { checkNotNull(headers, "headers"); if (headers == this) { - return this; + return thisT(); } - for (T key : headers.names()) { + for (K key : headers.names()) { remove(key); } - for (Entry entry : headers) { + for (Entry entry : headers) { add(entry.getKey(), entry.getValue()); } - return this; + return thisT(); } @Override - public boolean remove(T name) { + public boolean remove(K name) { return getAndRemove(name) != null; } @Override - public Headers clear() { + public T clear() { Arrays.fill(entries, null); head.before = head.after = head; size = 0; - return this; + return thisT(); } @Override - public Iterator> iterator() { + public Iterator> iterator() { return new HeaderIterator(); } @Override - public Boolean getBoolean(T name) { - T v = get(name); + public Boolean getBoolean(K name) { + V v = get(name); return v != null ? valueConverter.convertToBoolean(v) : null; } @Override - public boolean getBoolean(T name, boolean defaultValue) { + public boolean getBoolean(K name, boolean defaultValue) { Boolean v = getBoolean(name); return v != null ? v : defaultValue; } @Override - public Byte getByte(T name) { - T v = get(name); + public Byte getByte(K name) { + V v = get(name); return v != null ? valueConverter.convertToByte(v) : null; } @Override - public byte getByte(T name, byte defaultValue) { + public byte getByte(K name, byte defaultValue) { Byte v = getByte(name); return v != null ? v : defaultValue; } @Override - public Character getChar(T name) { - T v = get(name); + public Character getChar(K name) { + V v = get(name); return v != null ? valueConverter.convertToChar(v) : null; } @Override - public char getChar(T name, char defaultValue) { + public char getChar(K name, char defaultValue) { Character v = getChar(name); return v != null ? v : defaultValue; } @Override - public Short getShort(T name) { - T v = get(name); + public Short getShort(K name) { + V v = get(name); return v != null ? valueConverter.convertToShort(v) : null; } @Override - public short getShort(T name, short defaultValue) { + public short getShort(K name, short defaultValue) { Short v = getShort(name); return v != null ? v : defaultValue; } @Override - public Integer getInt(T name) { - T v = get(name); + public Integer getInt(K name) { + V v = get(name); return v != null ? valueConverter.convertToInt(v) : null; } @Override - public int getInt(T name, int defaultValue) { + public int getInt(K name, int defaultValue) { Integer v = getInt(name); return v != null ? v : defaultValue; } @Override - public Long getLong(T name) { - T v = get(name); + public Long getLong(K name) { + V v = get(name); return v != null ? valueConverter.convertToLong(v) : null; } @Override - public long getLong(T name, long defaultValue) { + public long getLong(K name, long defaultValue) { Long v = getLong(name); return v != null ? v : defaultValue; } @Override - public Float getFloat(T name) { - T v = get(name); + public Float getFloat(K name) { + V v = get(name); return v != null ? valueConverter.convertToFloat(v) : null; } @Override - public float getFloat(T name, float defaultValue) { + public float getFloat(K name, float defaultValue) { Float v = getFloat(name); return v != null ? v : defaultValue; } @Override - public Double getDouble(T name) { - T v = get(name); + public Double getDouble(K name) { + V v = get(name); return v != null ? valueConverter.convertToDouble(v) : null; } @Override - public double getDouble(T name, double defaultValue) { + public double getDouble(K name, double defaultValue) { Double v = getDouble(name); return v != null ? v : defaultValue; } @Override - public Long getTimeMillis(T name) { - T v = get(name); + public Long getTimeMillis(K name) { + V v = get(name); return v != null ? valueConverter.convertToTimeMillis(v) : null; } @Override - public long getTimeMillis(T name, long defaultValue) { + public long getTimeMillis(K name, long defaultValue) { Long v = getTimeMillis(name); return v != null ? v : defaultValue; } @Override - public Boolean getBooleanAndRemove(T name) { - T v = getAndRemove(name); + public Boolean getBooleanAndRemove(K name) { + V v = getAndRemove(name); return v != null ? valueConverter.convertToBoolean(v) : null; } @Override - public boolean getBooleanAndRemove(T name, boolean defaultValue) { + public boolean getBooleanAndRemove(K name, boolean defaultValue) { Boolean v = getBooleanAndRemove(name); return v != null ? v : defaultValue; } @Override - public Byte getByteAndRemove(T name) { - T v = getAndRemove(name); + public Byte getByteAndRemove(K name) { + V v = getAndRemove(name); return v != null ? valueConverter.convertToByte(v) : null; } @Override - public byte getByteAndRemove(T name, byte defaultValue) { + public byte getByteAndRemove(K name, byte defaultValue) { Byte v = getByteAndRemove(name); return v != null ? v : defaultValue; } @Override - public Character getCharAndRemove(T name) { - T v = getAndRemove(name); + public Character getCharAndRemove(K name) { + V v = getAndRemove(name); if (v == null) { return null; } @@ -736,79 +739,79 @@ public class DefaultHeaders implements Headers { } @Override - public char getCharAndRemove(T name, char defaultValue) { + public char getCharAndRemove(K name, char defaultValue) { Character v = getCharAndRemove(name); return v != null ? v : defaultValue; } @Override - public Short getShortAndRemove(T name) { - T v = getAndRemove(name); + public Short getShortAndRemove(K name) { + V v = getAndRemove(name); return v != null ? valueConverter.convertToShort(v) : null; } @Override - public short getShortAndRemove(T name, short defaultValue) { + public short getShortAndRemove(K name, short defaultValue) { Short v = getShortAndRemove(name); return v != null ? v : defaultValue; } @Override - public Integer getIntAndRemove(T name) { - T v = getAndRemove(name); + public Integer getIntAndRemove(K name) { + V v = getAndRemove(name); return v != null ? valueConverter.convertToInt(v) : null; } @Override - public int getIntAndRemove(T name, int defaultValue) { + public int getIntAndRemove(K name, int defaultValue) { Integer v = getIntAndRemove(name); return v != null ? v : defaultValue; } @Override - public Long getLongAndRemove(T name) { - T v = getAndRemove(name); + public Long getLongAndRemove(K name) { + V v = getAndRemove(name); return v != null ? valueConverter.convertToLong(v) : null; } @Override - public long getLongAndRemove(T name, long defaultValue) { + public long getLongAndRemove(K name, long defaultValue) { Long v = getLongAndRemove(name); return v != null ? v : defaultValue; } @Override - public Float getFloatAndRemove(T name) { - T v = getAndRemove(name); + public Float getFloatAndRemove(K name) { + V v = getAndRemove(name); return v != null ? valueConverter.convertToFloat(v) : null; } @Override - public float getFloatAndRemove(T name, float defaultValue) { + public float getFloatAndRemove(K name, float defaultValue) { Float v = getFloatAndRemove(name); return v != null ? v : defaultValue; } @Override - public Double getDoubleAndRemove(T name) { - T v = getAndRemove(name); + public Double getDoubleAndRemove(K name) { + V v = getAndRemove(name); return v != null ? valueConverter.convertToDouble(v) : null; } @Override - public double getDoubleAndRemove(T name, double defaultValue) { + public double getDoubleAndRemove(K name, double defaultValue) { Double v = getDoubleAndRemove(name); return v != null ? v : defaultValue; } @Override - public Long getTimeMillisAndRemove(T name) { - T v = getAndRemove(name); + public Long getTimeMillisAndRemove(K name) { + V v = getAndRemove(name); return v != null ? valueConverter.convertToTimeMillis(v) : null; } @Override - public long getTimeMillisAndRemove(T name, long defaultValue) { + public long getTimeMillisAndRemove(K name, long defaultValue) { Long v = getTimeMillisAndRemove(name); return v != null ? v : defaultValue; } @@ -820,7 +823,7 @@ public class DefaultHeaders implements Headers { return false; } - return equals((Headers) o, JAVA_HASHER); + return equals((Headers) o, JAVA_HASHER); } @SuppressWarnings("unchecked") @@ -836,7 +839,7 @@ public class DefaultHeaders implements Headers { * @return {@code true} if this object equals {@code h2} given {@code valueHashingStrategy}. * {@code false} otherwise. */ - public final boolean equals(Headers h2, HashingStrategy valueHashingStrategy) { + public final boolean equals(Headers h2, HashingStrategy valueHashingStrategy) { if (h2.size() != size()) { return false; } @@ -845,9 +848,9 @@ public class DefaultHeaders implements Headers { return true; } - for (T name : names()) { - List otherValues = h2.getAll(name); - List values = getAll(name); + for (K name : names()) { + List otherValues = h2.getAll(name); + List values = getAll(name); if (otherValues.size() != values.size()) { return false; } @@ -865,11 +868,11 @@ public class DefaultHeaders implements Headers { * individual values. * @param valueHashingStrategy Defines how values will be hashed. */ - public final int hashCode(HashingStrategy valueHashingStrategy) { - int result = 1; - for (T name : names()) { + public final int hashCode(HashingStrategy valueHashingStrategy) { + int result = HASH_CODE_SEED; + for (K name : names()) { result = 31 * result + hashingStrategy.hashCode(name); - List values = getAll(name); + List values = getAll(name); for (int i = 0; i < values.size(); ++i) { result = 31 * result + valueHashingStrategy.hashCode(values.get(i)); } @@ -881,8 +884,8 @@ public class DefaultHeaders implements Headers { public String toString() { StringBuilder builder = new StringBuilder(getClass().getSimpleName()).append('['); String separator = ""; - for (T name : names()) { - List values = getAll(name); + for (K name : names()) { + List values = getAll(name); for (int i = 0; i < values.size(); ++i) { builder.append(separator); builder.append(name).append(": ").append(values.get(i)); @@ -892,15 +895,15 @@ public class DefaultHeaders implements Headers { return builder.append(']').toString(); } - protected HeaderEntry newHeaderEntry(int h, T name, T value, HeaderEntry next) { - return new HeaderEntry(h, name, value, next, head); + protected HeaderEntry newHeaderEntry(int h, K name, V value, HeaderEntry next) { + return new HeaderEntry(h, name, value, next, head); } - protected ValueConverter valueConverter() { + protected ValueConverter valueConverter() { return valueConverter; } - private void add0(int h, int i, T name, T value) { + private void add0(int h, int i, K name, V value) { // Update the hash table. entries[i] = newHeaderEntry(h, name, value, entries[i]); ++size; @@ -909,14 +912,14 @@ public class DefaultHeaders implements Headers { /** * @return the first value inserted whose hash code equals {@code h} and whose name is equal to {@code name}. */ - private T remove0(int h, int i, T name) { - HeaderEntry e = entries[i]; + private V remove0(int h, int i, K name) { + HeaderEntry e = entries[i]; if (e == null) { return null; } - T value = null; - HeaderEntry next = e.next; + V value = null; + HeaderEntry next = e.next; while (next != null) { if (next.hash == h && hashingStrategy.equals(name, next.key)) { value = next.value; @@ -943,6 +946,11 @@ public class DefaultHeaders implements Headers { return value; } + @SuppressWarnings("unchecked") + private T thisT() { + return (T) this; + } + /** * This {@link DateFormat} decodes 3 formats of {@link Date}. * @@ -1014,8 +1022,8 @@ public class DefaultHeaders implements Headers { } } - private final class HeaderIterator implements Iterator> { - private HeaderEntry current = head; + private final class HeaderIterator implements Iterator> { + private HeaderEntry current = head; @Override public boolean hasNext() { @@ -1023,7 +1031,7 @@ public class DefaultHeaders implements Headers { } @Override - public Entry next() { + public Entry next() { current = current.after; if (current == head) { @@ -1039,25 +1047,25 @@ public class DefaultHeaders implements Headers { } } - protected static class HeaderEntry implements Map.Entry { + protected static class HeaderEntry implements Map.Entry { protected final int hash; - protected final T key; - protected T value; + protected final K key; + protected V value; /** * In bucket linked list */ - protected HeaderEntry next; + protected HeaderEntry next; /** * Overall insertion order linked list */ - protected HeaderEntry before, after; + protected HeaderEntry before, after; - protected HeaderEntry(int hash, T key) { + protected HeaderEntry(int hash, K key) { this.hash = hash; this.key = key; } - HeaderEntry(int hash, T key, T value, HeaderEntry next, HeaderEntry head) { + HeaderEntry(int hash, K key, V value, HeaderEntry next, HeaderEntry head) { this.hash = hash; this.key = key; this.value = value; @@ -1078,11 +1086,11 @@ public class DefaultHeaders implements Headers { after.before = this; } - public final HeaderEntry before() { + public final HeaderEntry before() { return before; } - public final HeaderEntry after() { + public final HeaderEntry after() { return after; } @@ -1092,19 +1100,19 @@ public class DefaultHeaders implements Headers { } @Override - public final T getKey() { + public final K getKey() { return key; } @Override - public final T getValue() { + public final V getValue() { return value; } @Override - public final T setValue(T value) { + public final V setValue(V value) { checkNotNull(value, "value"); - T oldValue = this.value; + V oldValue = this.value; this.value = value; return oldValue; } diff --git a/codec/src/main/java/io/netty/handler/codec/DefaultHeadersImpl.java b/codec/src/main/java/io/netty/handler/codec/DefaultHeadersImpl.java new file mode 100644 index 0000000000..4579c3b13a --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/DefaultHeadersImpl.java @@ -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 the type of the header name. + * @param the type of the header value. + */ +public final class DefaultHeadersImpl extends DefaultHeaders> { + public DefaultHeadersImpl(HashingStrategy nameHashingStrategy, + ValueConverter valueConverter, NameValidator nameValidator) { + super(nameHashingStrategy, valueConverter, nameValidator); + } +} diff --git a/codec/src/main/java/io/netty/handler/codec/EmptyHeaders.java b/codec/src/main/java/io/netty/handler/codec/EmptyHeaders.java index 299c4bc969..c14e19d696 100644 --- a/codec/src/main/java/io/netty/handler/codec/EmptyHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/EmptyHeaders.java @@ -20,274 +20,276 @@ import java.util.List; import java.util.Map.Entry; import java.util.Set; -public class EmptyHeaders implements Headers { +import static io.netty.handler.codec.DefaultHeaders.HASH_CODE_SEED; + +public class EmptyHeaders> implements Headers { @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 getAll(T name) { + public List getAll(K name) { return Collections.emptyList(); } @Override - public List getAllAndRemove(T name) { + public List 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 implements Headers { } @Override - public Set names() { + public Set names() { return Collections.emptySet(); } @Override - public Headers add(T name, T value) { + public T add(K name, V value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers add(T name, Iterable values) { + public T add(K name, Iterable values) { throw new UnsupportedOperationException("read only"); } @Override - public Headers add(T name, T... values) { + public T add(K name, V... values) { throw new UnsupportedOperationException("read only"); } @Override - public Headers addObject(T name, Object value) { + public T addObject(K name, Object value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers addObject(T name, Iterable values) { + public T addObject(K name, Iterable values) { throw new UnsupportedOperationException("read only"); } @Override - public Headers addObject(T name, Object... values) { + public T addObject(K name, Object... values) { throw new UnsupportedOperationException("read only"); } @Override - public Headers addBoolean(T name, boolean value) { + public T addBoolean(K name, boolean value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers addByte(T name, byte value) { + public T addByte(K name, byte value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers addChar(T name, char value) { + public T addChar(K name, char value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers addShort(T name, short value) { + public T addShort(K name, short value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers addInt(T name, int value) { + public T addInt(K name, int value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers addLong(T name, long value) { + public T addLong(K name, long value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers addFloat(T name, float value) { + public T addFloat(K name, float value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers addDouble(T name, double value) { + public T addDouble(K name, double value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers addTimeMillis(T name, long value) { + public T addTimeMillis(K name, long value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers add(Headers headers) { + public T add(Headers headers) { throw new UnsupportedOperationException("read only"); } @Override - public Headers set(T name, T value) { + public T set(K name, V value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers set(T name, Iterable values) { + public T set(K name, Iterable values) { throw new UnsupportedOperationException("read only"); } @Override - public Headers set(T name, T... values) { + public T set(K name, V... values) { throw new UnsupportedOperationException("read only"); } @Override - public Headers setObject(T name, Object value) { + public T setObject(K name, Object value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers setObject(T name, Iterable values) { + public T setObject(K name, Iterable values) { throw new UnsupportedOperationException("read only"); } @Override - public Headers setObject(T name, Object... values) { + public T setObject(K name, Object... values) { throw new UnsupportedOperationException("read only"); } @Override - public Headers setBoolean(T name, boolean value) { + public T setBoolean(K name, boolean value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers setByte(T name, byte value) { + public T setByte(K name, byte value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers setChar(T name, char value) { + public T setChar(K name, char value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers setShort(T name, short value) { + public T setShort(K name, short value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers setInt(T name, int value) { + public T setInt(K name, int value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers setLong(T name, long value) { + public T setLong(K name, long value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers setFloat(T name, float value) { + public T setFloat(K name, float value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers setDouble(T name, double value) { + public T setDouble(K name, double value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers setTimeMillis(T name, long value) { + public T setTimeMillis(K name, long value) { throw new UnsupportedOperationException("read only"); } @Override - public Headers set(Headers headers) { + public T set(Headers headers) { throw new UnsupportedOperationException("read only"); } @Override - public Headers setAll(Headers headers) { + public T setAll(Headers headers) { throw new UnsupportedOperationException("read only"); } @Override - public boolean remove(T name) { + public boolean remove(K name) { return false; } @Override - public Headers clear() { - return this; + public T clear() { + return thisT(); } @Override - public Iterator> iterator() { - List> empty = Collections.emptyList(); + public Iterator> iterator() { + List> empty = Collections.emptyList(); return empty.iterator(); } @@ -493,17 +495,22 @@ public class EmptyHeaders implements Headers { 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; + } } diff --git a/codec/src/main/java/io/netty/handler/codec/Headers.java b/codec/src/main/java/io/netty/handler/codec/Headers.java index ccce0091f3..8ba18ba8cd 100644 --- a/codec/src/main/java/io/netty/handler/codec/Headers.java +++ b/codec/src/main/java/io/netty/handler/codec/Headers.java @@ -19,7 +19,15 @@ import java.util.List; import java.util.Map.Entry; import java.util.Set; -public interface Headers extends Iterable> { +/** + * Common interface for {@link Headers} which represents a mapping of key to value. + * Duplicate keys may be allowed by implementations. + * + * @param the type of the header name. + * @param the type of the header value. + * @param the type to use for return values when the intention is to return {@code this} object. + */ +public interface Headers> extends Iterable> { /** * 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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 getAll(T name); + List 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 extends Iterable> { * @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 getAllAndRemove(T name); + List 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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { * @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 extends Iterable> { /** * Returns a {@link Set} of all header names in this object. The returned {@link Set} cannot be modified. */ - Set names(); + Set names(); /** * Adds a new header with the specified {@code name} and {@code value}. @@ -636,7 +644,7 @@ public interface Headers extends Iterable> { * @param value the value of the header * @return {@code this} */ - Headers 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 extends Iterable> { * @param values the values of the header * @return {@code this} */ - Headers add(T name, Iterable values); + T add(K name, Iterable 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 extends Iterable> { * @param values the values of the header * @return {@code this} */ - Headers 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 extends Iterable> { * @param value the value of the header * @return {@code this} */ - Headers 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 extends Iterable> { * @param values the value of the header * @return {@code this} */ - Headers 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 extends Iterable> { * @param values the value of the header * @return {@code this} */ - Headers addObject(T name, Object... values); + T addObject(K name, Object... values); /** * Adds a new header. @@ -714,7 +722,7 @@ public interface Headers extends Iterable> { * @param value the value of the header * @return {@code this} */ - Headers addBoolean(T name, boolean value); + T addBoolean(K name, boolean value); /** * Adds a new header. @@ -723,7 +731,7 @@ public interface Headers extends Iterable> { * @param value the value of the header * @return {@code this} */ - Headers addByte(T name, byte value); + T addByte(K name, byte value); /** * Adds a new header. @@ -732,7 +740,7 @@ public interface Headers extends Iterable> { * @param value the value of the header * @return {@code this} */ - Headers addChar(T name, char value); + T addChar(K name, char value); /** * Adds a new header. @@ -741,7 +749,7 @@ public interface Headers extends Iterable> { * @param value the value of the header * @return {@code this} */ - Headers addShort(T name, short value); + T addShort(K name, short value); /** * Adds a new header. @@ -750,7 +758,7 @@ public interface Headers extends Iterable> { * @param value the value of the header * @return {@code this} */ - Headers addInt(T name, int value); + T addInt(K name, int value); /** * Adds a new header. @@ -759,7 +767,7 @@ public interface Headers extends Iterable> { * @param value the value of the header * @return {@code this} */ - Headers addLong(T name, long value); + T addLong(K name, long value); /** * Adds a new header. @@ -768,7 +776,7 @@ public interface Headers extends Iterable> { * @param value the value of the header * @return {@code this} */ - Headers addFloat(T name, float value); + T addFloat(K name, float value); /** * Adds a new header. @@ -777,7 +785,7 @@ public interface Headers extends Iterable> { * @param value the value of the header * @return {@code this} */ - Headers addDouble(T name, double value); + T addDouble(K name, double value); /** * Adds a new header. @@ -786,7 +794,7 @@ public interface Headers extends Iterable> { * @param value the value of the header * @return {@code this} */ - Headers 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 extends Iterable> { * @throws IllegalArgumentException if {@code headers == this}. * @return {@code this} */ - Headers add(Headers headers); + T add(Headers 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 extends Iterable> { * @param value the value of the header * @return {@code this} */ - Headers 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 extends Iterable> { * @param values the value of the header * @return {@code this} */ - Headers set(T name, Iterable values); + T set(K name, Iterable 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 extends Iterable> { * @param values the value of the header * @return {@code this} */ - Headers 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 extends Iterable> { * {@code null}. * @return {@code this} */ - Headers 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 extends Iterable> { * @param values the values of the header * @return {@code this} */ - Headers 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 extends Iterable> { * @param values the values of the header * @return {@code this} */ - Headers 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 extends Iterable> { * @param value The value * @return {@code this} */ - Headers 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 extends Iterable> { * @param value The value * @return {@code this} */ - Headers 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 extends Iterable> { * @param value The value * @return {@code this} */ - Headers 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 extends Iterable> { * @param value The value * @return {@code this} */ - Headers 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 extends Iterable> { * @param value The value * @return {@code this} */ - Headers 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 extends Iterable> { * @param value The value * @return {@code this} */ - Headers 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 extends Iterable> { * @param value The value * @return {@code this} */ - Headers 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 extends Iterable> { * @param value The value * @return {@code this} */ - Headers 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 extends Iterable> { * @param value The value * @return {@code this} */ - Headers 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 set(Headers headers); + T set(Headers headers); /** * Retains all current headers but calls {@link #set(T, T)} for each entry in {@code headers}. @@ -968,7 +976,7 @@ public interface Headers extends Iterable> { * @param headers The headers used to {@link #set(T, T)} values in this instance * @return {@code this} */ - Headers setAll(Headers headers); + T setAll(Headers headers); /** * Removes all headers with the specified {@code name}. @@ -976,15 +984,15 @@ public interface Headers extends Iterable> { * @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 clear(); + T clear(); @Override - Iterator> iterator(); + Iterator> iterator(); } diff --git a/codec/src/main/java/io/netty/handler/codec/HeadersUtils.java b/codec/src/main/java/io/netty/handler/codec/HeadersUtils.java index ac7c5973b9..77cd5f7465 100644 --- a/codec/src/main/java/io/netty/handler/codec/HeadersUtils.java +++ b/codec/src/main/java/io/netty/handler/codec/HeadersUtils.java @@ -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 List getAllAsString(Headers headers, T name) { - final List allNames = headers.getAll(name); + public static List getAllAsString(Headers headers, K name) { + final List allNames = headers.getAll(name); return new AbstractList() { @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 String getAsString(Headers headers, T name) { - T orig = headers.get(name); + public static String getAsString(Headers 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 namesAsString(Headers headers) { + public static Set namesAsString(Headers headers) { return new CharSequenceDelegatingStringSet(headers.names()); } diff --git a/codec/src/main/java/io/netty/handler/codec/UnsupportedValueConverter.java b/codec/src/main/java/io/netty/handler/codec/UnsupportedValueConverter.java new file mode 100644 index 0000000000..36bd34fe2e --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/UnsupportedValueConverter.java @@ -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 implements ValueConverter { + @SuppressWarnings("rawtypes") + private static final UnsupportedValueConverter INSTANCE = new UnsupportedValueConverter(); + private UnsupportedValueConverter() { } + + @SuppressWarnings("unchecked") + public static UnsupportedValueConverter instance() { + return (UnsupportedValueConverter) 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(); + } +} diff --git a/codec/src/test/java/io/netty/handler/codec/DefaultHeadersTest.java b/codec/src/test/java/io/netty/handler/codec/DefaultHeadersTest.java index 472fa3c08e..5f8472c385 100644 --- a/codec/src/test/java/io/netty/handler/codec/DefaultHeadersTest.java +++ b/codec/src/test/java/io/netty/handler/codec/DefaultHeadersTest.java @@ -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 newInstance() { - return new DefaultHeaders(ByteStringValueConverter.INSTANCE); + private static final class TestDefaultHeaders extends + DefaultHeaders { + public TestDefaultHeaders() { + super(CharSequenceValueConverter.INSTANCE); + } + } + + private TestDefaultHeaders newInstance() { + return new TestDefaultHeaders(); } @Test public void addShouldIncreaseAndRemoveShouldDecreaseTheSize() { - Headers 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 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 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 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 values = headers.getAll(bs("name")); + List 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 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 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 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 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 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 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 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 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 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 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 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 headers1 = newInstance(); - Headers 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 headers1 = newInstance(); - headers1.add(bs("name1"), bs("value1")); - Headers 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 headers1 = newInstance(); - headers1.add(bs("name1"), bs("value1")); - headers1.add(bs("name2"), bs("value2")); - Headers 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 h1 = newInstance(); - h1.set(bs("name1"), bs("value1")); - Headers 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> iterator = newInstance().iterator(); + Iterator> iterator = newInstance().iterator(); assertFalse(iterator.hasNext()); iterator.next(); } @Test public void iteratorShouldReturnAllNameValuePairs() { - Headers 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 headers2 = newInstance(); - for (Entry entry : headers1) { + TestDefaultHeaders headers2 = newInstance(); + for (Entry entry : headers1) { headers2.add(entry.getKey(), entry.getValue()); } @@ -316,45 +321,45 @@ public class DefaultHeadersTest { @Test public void iteratorSetValueShouldChangeHeaderValue() { - Headers 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> iter = headers.iterator(); + Iterator> iter = headers.iterator(); while (iter.hasNext()) { - Entry header = iter.next(); - if (bs("name1").equals(header.getKey()) && bs("value2").equals(header.getValue())) { - header.setValue(bs("updatedvalue2")); - assertEquals(bs("updatedvalue2"), header.getValue()); + Entry 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 headers = newInstance(); - assertEquals(0, headers.getAll(bs("noname")).size()); + TestDefaultHeaders headers = newInstance(); + assertEquals(0, headers.getAll(of("noname")).size()); } @Test public void setHeadersShouldClearAndOverwrite() { - Headers headers1 = newInstance(); - headers1.add(bs("name"), bs("value")); + TestDefaultHeaders headers1 = newInstance(); + headers1.add(of("name"), of("value")); - Headers 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 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 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 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 headers = newInstance(); + TestDefaultHeaders headers = newInstance(); headers.add(headers); } @Test(expected = IllegalArgumentException.class) public void testSetSelf() { - Headers headers = newInstance(); + TestDefaultHeaders headers = newInstance(); headers.set(headers); } - - private ByteString bs(String value) { - return new ByteString(value, CharsetUtil.US_ASCII); - } } diff --git a/common/src/main/java/io/netty/util/AsciiString.java b/common/src/main/java/io/netty/util/AsciiString.java index ff8d477226..f69301045c 100644 --- a/common/src/main/java/io/netty/util/AsciiString.java +++ b/common/src/main/java/io/netty/util/AsciiString.java @@ -20,150 +20,415 @@ import io.netty.util.internal.EmptyArrays; import io.netty.util.internal.PlatformDependent; import java.nio.ByteBuffer; +import java.nio.CharBuffer; import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty.util.internal.MathUtil.isOutOfBounds; /** * A string which has been encoded into a character encoding whose character always takes a single byte, similarly to * ASCII. It internally keeps its content in a byte array unlike {@link String}, which uses a character array, for * reduced memory footprint and faster data transfer from/to byte-based data structures such as a byte array and * {@link ByteBuffer}. It is often used in conjunction with {@link TextHeaders}. + *

+ * This class was designed to provide an immutable array of bytes, and caches some internal state based upon the value + * of this array. However underlying access to this byte array is provided via not copying the array on construction or + * {@link #array()}. If any changes are made to the underlying byte array it is the user's responsibility to call + * {@link #arrayChanged()} so the state of this class can be reset. */ -public final class AsciiString extends ByteString implements CharSequence, Comparable { - - private static final char MAX_CHAR_VALUE = 255; +public final class AsciiString implements CharSequence, Comparable { public static final AsciiString EMPTY_STRING = new AsciiString(""); - - public static final HashingStrategy CASE_INSENSITIVE_HASHER = - new HashingStrategy() { - @Override - public int hashCode(CharSequence o) { - return AsciiString.caseInsensitiveHashCode(o); - } - - @Override - public boolean equals(CharSequence a, CharSequence b) { - return AsciiString.contentEqualsIgnoreCase(a, b); - } - }; - public static final HashingStrategy CASE_SENSITIVE_HASHER = - new HashingStrategy() { - @Override - public int hashCode(CharSequence o) { - return AsciiString.hashCode(o); - } - - @Override - public boolean equals(CharSequence a, CharSequence b) { - return AsciiString.contentEquals(a, b); - } - }; + private static final char MAX_CHAR_VALUE = 255; + private static final int HASH_CODE_PRIME = 31; /** - * Factory which uses the {@link #AsciiString(byte[], int, int, boolean)} constructor. + * If this value is modified outside the constructor then call {@link #arrayChanged()}. + */ + private 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 to cache the {@link #toString()} value. */ - private static final ByteStringFactory DEFAULT_FACTORY = new ByteStringFactory() { - @Override - public ByteString newInstance(byte[] value, int start, int length, boolean copy) { - return new AsciiString(value, start, length, copy); - } - }; - private String string; - private int caseInsensitiveHash; /** - * Returns an {@link AsciiString} containing the given character sequence. If the given string is already a - * {@link AsciiString}, just returns the same instance. + * Initialize this byte string based upon a byte array. A copy will be made. */ - public static AsciiString of(CharSequence string) { - return string instanceof AsciiString ? (AsciiString) string : new AsciiString(string); - } - public AsciiString(byte[] value) { - super(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 AsciiString(byte[] value, boolean copy) { - super(value, copy); + this(value, 0, value.length, copy); } + /** + * Construct a new instance 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 AsciiString(byte[] value, int start, int length, boolean copy) { - super(value, start, length, copy); - } - - public AsciiString(ByteString value, boolean copy) { - super(value, copy); + if (copy) { + this.value = Arrays.copyOfRange(value, start, start + length); + this.offset = 0; + } else { + if (isOutOfBounds(start, length, value.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 copy of the underlying storage from {@link value}. + * The copy will start at {@link ByteBuffer#position()} and copy {@link ByteBuffer#remaining()} bytes. + */ public AsciiString(ByteBuffer value) { - super(value); + this(value, true); } + /** + * Initialize a instance 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 AsciiString(ByteBuffer value, boolean copy) { + this(value, value.position(), 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 AsciiString(ByteBuffer value, int start, int length, boolean copy) { - super(value, start, length, copy); + if (isOutOfBounds(start, length, value.capacity())) { + 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; + } else { + this.value = value.array(); + this.offset = start; + } + } 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 {@code value} into this instance assuming ASCII encoding. + */ public AsciiString(char[] value) { - this(checkNotNull(value, "value"), 0, value.length); + this(value, 0, value.length); } + /** + * Create a copy of {@code value} into this instance assuming ASCII encoding. + * The copy will start at index {@code start} and copy {@code length} bytes. + */ public AsciiString(char[] value, int start, int length) { - super(length); - if (start < 0 || start > checkNotNull(value, "value").length - length) { + if (isOutOfBounds(start, length, value.length)) { throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + ") <= " + "value.length(" + value.length + ')'); } + this.value = new byte[length]; for (int i = 0, j = start; i < length; i++, j++) { this.value[i] = c2b(value[j]); } + this.offset = 0; + this.length = length; } + /** + * Create a copy of {@link value} into this instance using the encoding type of {@code charset}. + */ + public AsciiString(char[] value, Charset charset) { + this(value, charset, 0, value.length); + } + + /** + * Create a copy of {@link value} into a this instance using the encoding type of {@code charset}. + * The copy will start at index {@code start} and copy {@code length} bytes. + */ + public AsciiString(char[] value, Charset charset, int start, int length) { + CharBuffer cbuf = CharBuffer.wrap(value, 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 {@code value} into this instance assuming ASCII encoding. + */ public AsciiString(CharSequence value) { - this(checkNotNull(value, "value"), 0, value.length()); + this(value, 0, value.length()); } + /** + * Create a copy of {@code value} into this instance assuming ASCII encoding. + * The copy will start at index {@code start} and copy {@code length} bytes. + */ public AsciiString(CharSequence value, int start, int length) { - super(length); - if (start < 0 || length < 0 || length > checkNotNull(value, "value").length() - start) { + if (isOutOfBounds(start, length, value.length())) { throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + ") <= " + "value.length(" + value.length() + ')'); } + this.value = new byte[length]; for (int i = 0, j = start; i < length; i++, j++) { this.value[i] = c2b(value.charAt(j)); } + this.offset = 0; + this.length = length; + } + + /** + * Create a copy of {@link value} into this instance using the encoding type of {@code charset}. + */ + public AsciiString(CharSequence value, Charset charset) { + this(value, charset, 0, value.length()); + } + + /** + * Create a copy of {@link value} into this instance using the encoding type of {@code charset}. + * The copy will start at index {@code start} and copy {@code length} bytes. + */ + public AsciiString(CharSequence value, Charset charset, int start, int 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; + } + + /** + * 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 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 int forEachByte(int index, int length, ByteProcessor visitor) throws Exception { + if (isOutOfBounds(index, length, length())) { + 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 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 int forEachByteDesc(int index, int length, ByteProcessor visitor) throws Exception { + if (isOutOfBounds(index, length, length())) { + 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 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 + ")"); + } + // Try to use unsafe to avoid double checking the index bounds + if (PlatformDependent.hasUnsafe()) { + return PlatformDependent.getByte(value, index + offset); + } + return value[index + offset]; + } + + /** + * Determine if this instance has 0 length. + */ + public boolean isEmpty() { + return length == 0; + } + + /** + * The length in bytes of this instance. + */ + @Override + public 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() { + string = null; + 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 byte[] array() { + return value; + } + + /** + * The offset into {@link #array()} for which data for this ByteString begins. + * @see #array() + * @see #isEntireArrayUsed() + */ + public int arrayOffset() { + return offset; + } + + /** + * Determine if the storage represented by {@link #array()} is entirely used. + * @see #array() + */ + public boolean isEntireArrayUsed() { + return offset == 0 && length == value.length; + } + + /** + * Converts this string to a byte array. + */ + public 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 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 void copy(int srcIdx, byte[] dst, int dstIdx, int length) { + if (isOutOfBounds(srcIdx, length, length())) { + throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + + length + ") <= srcLen(" + length() + ')'); + } + + System.arraycopy(value, srcIdx + offset, checkNotNull(dst, "dst"), dstIdx, length); } @Override public char charAt(int index) { - return (char) (byteAt(index) & 0xFF); + return b2c(byteAt(index)); } - @Override - public void arrayChanged() { - string = null; - caseInsensitiveHash = 0; - super.arrayChanged(); - } - - @Override - public String toString(Charset charset, int start, int end) { - if (start == 0 && end == length()) { - if (string == null) { - string = super.toString(charset, start, end); - } - return string; - } - - return super.toString(charset, start, end); + /** + * Determines if this {@code String} contains the sequence of characters in the {@code CharSequence} passed. + * + * @param cs the character sequence to search for. + * @return {@code true} if the sequence of characters are contained in this string, otherwise {@code false}. + */ + public boolean contains(CharSequence cs) { + return indexOf(cs) >= 0; } /** @@ -190,7 +455,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa int length2 = string.length(); int minLength = Math.min(length1, length2); for (int i = 0, j = arrayOffset(); i < minLength; i++, j++) { - result = (char) (value[j] & 0xFF) - string.charAt(i); + result = b2c(value[j]) - string.charAt(i); if (result != 0) { return result; } @@ -212,7 +477,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa return this; } - if (string instanceof AsciiString) { + if (string.getClass() == AsciiString.class) { AsciiString that = (AsciiString) string; if (isEmpty()) { return that; @@ -272,7 +537,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa } for (int i = arrayOffset(), j = 0; i < length(); ++i, ++j) { - if (!equalsIgnoreCase((char) (value[i] & 0xFF), string.charAt(j))) { + if (!equalsIgnoreCase(b2c(value[i]), string.charAt(j))) { return false; } } @@ -299,14 +564,14 @@ public final class AsciiString extends ByteString implements CharSequence, Compa return EmptyArrays.EMPTY_CHARS; } - if (start < 0 || length > length() - start) { + if (isOutOfBounds(start, length, length())) { throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= srcIdx + length(" + length + ") <= srcLen(" + length() + ')'); } final char[] buffer = new char[length]; for (int i = 0, j = start + arrayOffset(); i < length; i++, j++) { - buffer[i] = (char) (value[j] & 0xFF); + buffer[i] = b2c(value[j]); } return buffer; } @@ -324,30 +589,63 @@ public final class AsciiString extends ByteString implements CharSequence, Compa throw new NullPointerException("dst"); } - if (srcIdx < 0 || length > length() - srcIdx) { + if (isOutOfBounds(srcIdx, length, length())) { throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + length + ") <= srcLen(" + length() + ')'); } final int dstEnd = dstIdx + length; for (int i = dstIdx, j = srcIdx + arrayOffset(); i < dstEnd; i++, j++) { - dst[i] = (char) (value[j] & 0xFF); + dst[i] = b2c(value[j]); } } - @Override + /** + * 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 AsciiString 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()}. + */ @Override public AsciiString subSequence(int start, int end) { return subSequence(start, end, true); } - @Override + /** + * 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 AsciiString subSequence(int start, int end, boolean copy) { - return (AsciiString) super.subSequence(start, end, copy, DEFAULT_FACTORY); + if (isOutOfBounds(start, end - start, 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 new AsciiString(value, start + offset, end - start, copy); } /** @@ -400,7 +698,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa return -1; // handles subCount > count || start >= count } int o1 = i, o2 = 0; - while (++o2 < subCount && (char) (value[++o1 + arrayOffset()] & 0xFF) == subString.charAt(o2)) { + while (++o2 < subCount && b2c(value[++o1 + arrayOffset()]) == subString.charAt(o2)) { // Intentionally empty } if (o2 == subCount) { @@ -465,7 +763,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa return -1; } int o1 = i, o2 = 0; - while (++o2 < subCount && (char) (value[++o1 + arrayOffset()] & 0xFF) == subString.charAt(o2)) { + while (++o2 < subCount && b2c(value[++o1 + arrayOffset()]) == subString.charAt(o2)) { // Intentionally empty } if (o2 == subCount) { @@ -510,7 +808,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa final int thatEnd = start + length; for (int i = start, j = thisStart + arrayOffset(); i < thatEnd; i++, j++) { - if ((char) (value[j] & 0xFF) != string.charAt(i)) { + if (b2c(value[j]) != string.charAt(i)) { return false; } } @@ -549,7 +847,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa thisStart += arrayOffset(); final int thisEnd = thisStart + length; while (thisStart < thisEnd) { - if (!equalsIgnoreCase((char) (value[thisStart++] & 0xFF), string.charAt(start++))) { + if (!equalsIgnoreCase(b2c(value[thisStart++]), string.charAt(start++))) { return false; } } @@ -705,18 +1003,15 @@ public final class AsciiString extends ByteString implements CharSequence, Compa * @return {@code true} if equal, otherwise {@code false} */ public boolean contentEquals(CharSequence a) { - if (this == a) { - return true; + if (a == null || a.length() != length()) { + return false; } - if (a instanceof AsciiString) { + if (a.getClass() == AsciiString.class) { return equals(a); } - if (a.length() != length()) { - return false; - } for (int i = arrayOffset(), j = 0; j < a.length(); ++i, ++j) { - if ((char) (value[i] & 0xFF) != a.charAt(j)) { + if (b2c(value[i]) != a.charAt(j)) { return false; } } @@ -790,22 +1085,284 @@ public final class AsciiString extends ByteString implements CharSequence, Compa return res.toArray(new AsciiString[res.size()]); } - /** - * Generate a hash code that will be consistent regardless of ASCII character casing. - *

- * NOTE: This must be compatible with {@link #caseInsensitiveHashCode(CharSequence)}. - */ - public int hashCodeCaseInsensitive() { - int h = caseInsensitiveHash; + @Override + public int hashCode() { + int h = hash; if (h == 0) { final int end = arrayOffset() + length(); for (int i = arrayOffset(); i < end; ++i) { - h = h * HASH_CODE_PRIME + toLowerCase((char) (value[i] & 0xFF)); + // masking with 0x1F reduces the number of overall bits that impact the hash code but makes the hash + // code the same regardless of character case (upper case or lower case hash is the same). + h = h * HASH_CODE_PRIME + (value[i] & 0x1F); } - caseInsensitiveHash = h; + hash = h; } - return caseInsensitiveHash; + return hash; + } + + /** + * Generate a hash code that will be consistent regardless of ASCII character casing. + */ + public int hashCodeCaseInsensitive() { + return hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != AsciiString.class) { + return false; + } + if (this == obj) { + return true; + } + + AsciiString other = (AsciiString) 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)} + */ + @Override + public String toString() { + if (string != null) { + return string; + } + string = toString(0); + return string; + } + + /** + * Translates the entire byte string to a {@link String} using the {@code charset} encoding. + * @see {@link #toString(int, int)} + */ + public String toString(int start) { + return toString(start, length()); + } + + /** + * Translates the [{@code start}, {@code end}) range of this byte string to a {@link String}. + * @see {@link #toString(int, int)} + */ + public String toString(int start, int end) { + int length = end - start; + if (length == 0) { + return ""; + } + + if (isOutOfBounds(start, length, length())) { + throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= srcIdx + length(" + + length + ") <= srcLen(" + length() + ')'); + } + + @SuppressWarnings("deprecation") + final String str = new String(value, 0, start + offset, length); + return str; + } + + public boolean parseBoolean() { + return length >= 1 && value[offset] != 0; + } + + public 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) ((b2c(value[startWithOffset]) << 8) | b2c(value[startWithOffset + 1])); + } + + public short parseShort() { + return parseShort(0, length(), 10); + } + + public short parseShort(int radix) { + return parseShort(0, length(), radix); + } + + public short parseShort(int start, int end) { + return parseShort(start, end, 10); + } + + public short parseShort(int start, int end, int radix) { + int intValue = parseInt(start, end, radix); + short result = (short) intValue; + if (result != intValue) { + throw new NumberFormatException(subSequence(start, end, false).toString()); + } + return result; + } + + public int parseInt() { + return parseInt(0, length(), 10); + } + + public int parseInt(int radix) { + return parseInt(0, length(), radix); + } + + public int parseInt(int start, int end) { + return parseInt(start, end, 10); + } + + public int parseInt(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 parseInt(i, end, radix, negative); + } + + private int parseInt(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 long parseLong() { + return parseLong(0, length(), 10); + } + + public long parseLong(int radix) { + return parseLong(0, length(), radix); + } + + public long parseLong(int start, int end) { + return parseLong(start, end, 10); + } + + public long parseLong(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 parseLong(i, end, radix, negative); + } + + private long parseLong(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 float parseFloat() { + return parseFloat(0, length()); + } + + public float parseFloat(int start, int end) { + return Float.parseFloat(toString(start, end)); + } + + public double parseDouble() { + return parseDouble(0, length()); + } + + public double parseDouble(int start, int end) { + return Double.parseDouble(toString(start, end)); + } + + public static final HashingStrategy CASE_INSENSITIVE_HASHER = + new HashingStrategy() { + @Override + public int hashCode(CharSequence o) { + return AsciiString.caseInsensitiveHashCode(o); + } + + @Override + public boolean equals(CharSequence a, CharSequence b) { + return AsciiString.contentEqualsIgnoreCase(a, b); + } + }; + public static final HashingStrategy CASE_SENSITIVE_HASHER = + new HashingStrategy() { + @Override + public int hashCode(CharSequence o) { + return AsciiString.hashCode(o); + } + + @Override + public boolean equals(CharSequence a, CharSequence b) { + return AsciiString.contentEquals(a, b); + } + }; + + /** + * Returns an {@link AsciiString} containing the given character sequence. If the given string is already a + * {@link AsciiString}, just returns the same instance. + */ + public static AsciiString of(CharSequence string) { + return string.getClass() == AsciiString.class ? (AsciiString) string : new AsciiString(string); } /** @@ -814,13 +1371,16 @@ public final class AsciiString extends ByteString implements CharSequence, Compa * {@link CharSequence}s into the same {@link TextHeaders}. */ public static int caseInsensitiveHashCode(CharSequence value) { + if (value == null) { + return 0; + } if (value.getClass() == AsciiString.class) { return ((AsciiString) value).hashCodeCaseInsensitive(); } int hash = 0; for (int i = 0; i < value.length(); ++i) { - hash = hash * HASH_CODE_PRIME + toLowerCase(value.charAt(i)); + hash = hash * HASH_CODE_PRIME + (value.charAt(i) & 0x1F); } return hash; } @@ -831,13 +1391,16 @@ public final class AsciiString extends ByteString implements CharSequence, Compa * @return */ public static int hashCode(CharSequence value) { + if (value == null) { + return 0; + } if (value.getClass() == AsciiString.class) { return ((AsciiString) value).hashCode(); } int hash = 0; for (int i = 0; i < value.length(); ++i) { - hash = hash * HASH_CODE_PRIME + value.charAt(i); + hash = hash * HASH_CODE_PRIME + (value.charAt(i) & 0x1F); } return hash; } @@ -861,12 +1424,8 @@ public final class AsciiString extends ByteString implements CharSequence, Compa * ASCII. */ public static boolean contentEqualsIgnoreCase(CharSequence a, CharSequence b) { - if (a == b) { - return true; - } - if (a == null || b == null) { - return false; + return a == b; } if (a.getClass() == AsciiString.class) { @@ -927,12 +1486,8 @@ public final class AsciiString extends ByteString implements CharSequence, Compa * Returns {@code true} if the content of both {@link CharSequence}'s are equals. This only supports 8-bit ASCII. */ public static boolean contentEquals(CharSequence a, CharSequence b) { - if (a == b) { - return true; - } - if (a == null || b == null) { - return false; + return a == b; } if (a.getClass() == AsciiString.class) { @@ -962,19 +1517,6 @@ public final class AsciiString extends ByteString implements CharSequence, Compa return res; } - /** - * Determines if this {@code String} contains the sequence of characters in the {@code CharSequence} passed. - * - * @param cs the character sequence to search for. - * @return {@code true} if the sequence of characters are contained in this string, otherwise {@code false}. - */ - public boolean contains(CharSequence cs) { - if (cs == null) { - throw new NullPointerException(); - } - return indexOf(cs) >= 0; - } - private interface CharEqualityComparator { boolean equals(char a, char b); } @@ -1032,30 +1574,34 @@ public final class AsciiString extends ByteString implements CharSequence, Compa } private static byte toLowerCase(byte b) { - if ('A' <= b && b <= 'Z') { - return (byte) (b + 32); - } - return b; + return isUpperCase(b) ? (byte) (b + 32) : b; } private static char toLowerCase(char c) { - if ('A' <= c && c <= 'Z') { - return (char) (c + 32); - } - return c; + return isUpperCase(c) ? (char) (c + 32) : c; } private static byte toUpperCase(byte b) { - if ('a' <= b && b <= 'z') { - return (byte) (b - 32); - } - return b; + return isLowerCase(b) ? (byte) (b - 32) : b; } - private static byte c2b(char c) { - if (c > MAX_CHAR_VALUE) { - return '?'; - } - return (byte) c; + private static boolean isLowerCase(byte value) { + return value >= 'a' && value <= 'z'; + } + + public static boolean isUpperCase(byte value) { + return value >= 'A' && value <= 'Z'; + } + + public static boolean isUpperCase(char value) { + return value >= 'A' && value <= 'Z'; + } + + public static byte c2b(char c) { + return (byte) ((c > MAX_CHAR_VALUE) ? '?' : c); + } + + public static char b2c(byte b) { + return (char) (b & 0xFF); } } diff --git a/common/src/main/java/io/netty/util/ByteString.java b/common/src/main/java/io/netty/util/ByteString.java deleted file mode 100644 index 394ec0246b..0000000000 --- a/common/src/main/java/io/netty/util/ByteString.java +++ /dev/null @@ -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); - } -} diff --git a/common/src/main/java/io/netty/util/internal/MathUtil.java b/common/src/main/java/io/netty/util/internal/MathUtil.java index 09a45b311d..27461831a3 100644 --- a/common/src/main/java/io/netty/util/internal/MathUtil.java +++ b/common/src/main/java/io/netty/util/internal/MathUtil.java @@ -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; + } } diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index 2289b699b8..751bf49454 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -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()) { diff --git a/common/src/test/java/io/netty/util/AsciiStringTest.java b/common/src/test/java/io/netty/util/AsciiStringCharacterTest.java similarity index 87% rename from common/src/test/java/io/netty/util/AsciiStringTest.java rename to common/src/test/java/io/netty/util/AsciiStringCharacterTest.java index ba7d2410b0..2d790d6b3f 100644 --- a/common/src/test/java/io/netty/util/AsciiStringTest.java +++ b/common/src/test/java/io/netty/util/AsciiStringCharacterTest.java @@ -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()); + } } diff --git a/common/src/test/java/io/netty/util/ByteStringTest.java b/common/src/test/java/io/netty/util/AsciiStringMemoryTest.java similarity index 58% rename from common/src/test/java/io/netty/util/ByteStringTest.java rename to common/src/test/java/io/netty/util/AsciiStringMemoryTest.java index 7e459e938d..808ace76a7 100644 --- a/common/src/test/java/io/netty/util/ByteStringTest.java +++ b/common/src/test/java/io/netty/util/AsciiStringMemoryTest.java @@ -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 aCount = new AtomicReference(0); final AtomicReference bCount = new AtomicReference(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 aCount = new AtomicReference(0); final AtomicReference bCount = new AtomicReference(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); } } diff --git a/microbench/src/main/java/io/netty/microbench/headers/HeadersBenchmark.java b/microbench/src/main/java/io/netty/microbench/headers/HeadersBenchmark.java index 66ae0e4330..7a0d0713e9 100644 --- a/microbench/src/main/java/io/netty/microbench/headers/HeadersBenchmark.java +++ b/microbench/src/main/java/io/netty/microbench/headers/HeadersBenchmark.java @@ -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 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 entry : http2Headers) { + for (Entry entry : http2Headers) { bh.consume(entry); } }