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); } }