From b823bfa9507f6c27337f66e89742f70474b31a55 Mon Sep 17 00:00:00 2001 From: Scott Mitchell Date: Tue, 31 Mar 2015 16:23:52 -0700 Subject: [PATCH] ByteString introduced as AsciiString super class Motivation: The usage and code within AsciiString has exceeded the original design scope for this class. Its usage as a binary string is confusing and on the verge of violating interface assumptions in some spots. Modifications: - ByteString will be created as a base class to AsciiString. All of the generic byte handling processing will live in ByteString and all the special character encoding will live in AsciiString. Results: The AsciiString interface will be clarified. Users of AsciiString can now be clear of the limitations the class imposes while users of the ByteString class don't have to live with those limitations. --- .../java/io/netty/buffer/AbstractByteBuf.java | 13 +- .../buffer/AdvancedLeakAwareByteBuf.java | 9 +- .../main/java/io/netty/buffer/ByteBuf.java | 19 +- .../io/netty/buffer/ByteBufProcessor.java | 33 +- .../java/io/netty/buffer/ByteBufUtil.java | 82 +++ .../io/netty/buffer/DuplicatedByteBuf.java | 6 +- .../java/io/netty/buffer/EmptyByteBuf.java | 9 +- .../java/io/netty/buffer/ReadOnlyByteBuf.java | 6 +- .../java/io/netty/buffer/SlicedByteBuf.java | 6 +- .../java/io/netty/buffer/SwappedByteBuf.java | 10 +- .../java/io/netty/buffer/WrappedByteBuf.java | 9 +- .../io/netty/buffer/AbstractByteBufTest.java | 18 +- .../io/netty/buffer/ByteBufProcessorTest.java | 63 -- .../io/netty/buffer/ByteProcessorTest.java | 64 ++ .../handler/codec/haproxy/HAProxyMessage.java | 6 +- .../codec/http/DefaultHttpHeaders.java | 103 ++- .../codec/http/HttpClientUpgradeHandler.java | 2 +- .../handler/codec/http/HttpHeaderNames.java | 2 +- .../handler/codec/http/HttpHeaderValues.java | 2 +- .../netty/handler/codec/http/HttpHeaders.java | 1 - .../codec/http/HttpHeadersEncoder.java | 5 +- .../netty/handler/codec/http/HttpMethod.java | 2 +- .../handler/codec/http/HttpObjectDecoder.java | 4 +- .../codec/http/HttpRequestEncoder.java | 11 +- .../codec/http/HttpResponseEncoder.java | 13 +- .../codec/http/HttpResponseStatus.java | 83 ++- .../codec/http/HttpServerUpgradeHandler.java | 2 +- .../handler/codec/http/HttpStatusClass.java | 2 +- .../netty/handler/codec/http/HttpVersion.java | 2 +- .../codec/http/websocketx/Utf8Validator.java | 4 +- .../WebSocketClientHandshaker00.java | 2 +- .../WebSocketServerHandshaker00.java | 5 +- .../handler/codec/rtsp/RtspHeaderNames.java | 2 +- .../handler/codec/rtsp/RtspHeaderValues.java | 2 +- .../codec/rtsp/RtspRequestEncoder.java | 11 +- .../codec/rtsp/RtspResponseEncoder.java | 13 +- .../codec/spdy/DefaultSpdyHeaders.java | 2 +- .../codec/spdy/SpdyHeaderBlockRawEncoder.java | 20 +- .../netty/handler/codec/spdy/SpdyHeaders.java | 2 +- .../handler/codec/spdy/SpdyHttpHeaders.java | 2 +- .../codec/http/HttpHeaderUtilTest.java | 3 +- .../CompressorHttp2ConnectionEncoder.java | 16 +- .../codec/http2/DefaultHttp2Headers.java | 136 ++-- .../http2/DefaultHttp2HeadersDecoder.java | 6 +- .../http2/DefaultHttp2HeadersEncoder.java | 14 +- .../DelegatingDecompressorFrameListener.java | 18 +- .../codec/http2/EmptyHttp2Headers.java | 82 +-- .../handler/codec/http2/Http2Headers.java | 93 +-- .../codec/http2/Http2HeadersEncoder.java | 8 +- .../codec/http2/Http2ServerUpgradeCodec.java | 9 +- .../netty/handler/codec/http2/HttpUtil.java | 34 +- .../InboundHttp2ToHttpPriorityAdapter.java | 3 +- .../codec/http2/DataCompressionHttp2Test.java | 2 +- .../codec/http2/DefaultHttp2HeadersTest.java | 33 +- .../handler/codec/http2/Http2TestUtil.java | 9 +- .../http2/InboundHttp2ToHttpAdapterTest.java | 2 +- .../handler/codec/stomp/StompHeaders.java | 2 +- .../handler/codec/AsciiHeadersEncoder.java | 4 +- .../io/netty/handler/codec/BinaryHeaders.java | 76 +- .../handler/codec/DefaultBinaryHeaders.java | 178 +++-- .../netty/handler/codec/DefaultHeaders.java | 10 + .../handler/codec/DefaultTextHeaders.java | 14 +- .../handler/codec/EmptyBinaryHeaders.java | 64 +- .../java/io/netty/handler/codec/Headers.java | 10 +- .../codec/ReplayingDecoderByteBuf.java | 10 +- .../codec/DefaultBinaryHeadersTest.java | 32 +- .../main/java/io/netty/util}/AsciiString.java | 658 ++++------------- .../java/io/netty/util/ByteProcessor.java | 128 ++++ .../main/java/io/netty/util/ByteString.java | 660 ++++++++++++++++++ .../io/netty/util/internal/StringUtil.java | 1 + .../java/io/netty/util}/AsciiStringTest.java | 27 +- .../HttpHelloWorldServerHandler.java | 10 +- .../http2/server/HelloWorldHttp2Handler.java | 2 +- .../netty/handler/proxy/HttpProxyHandler.java | 2 +- 74 files changed, 1829 insertions(+), 1179 deletions(-) delete mode 100644 buffer/src/test/java/io/netty/buffer/ByteBufProcessorTest.java create mode 100644 buffer/src/test/java/io/netty/buffer/ByteProcessorTest.java rename {codec/src/main/java/io/netty/handler/codec => common/src/main/java/io/netty/util}/AsciiString.java (64%) create mode 100644 common/src/main/java/io/netty/util/ByteProcessor.java create mode 100644 common/src/main/java/io/netty/util/ByteString.java rename {codec/src/test/java/io/netty/handler/codec => common/src/test/java/io/netty/util}/AsciiStringTest.java (69%) diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java index 3f97be30c8..7dbc536bb9 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java @@ -15,6 +15,7 @@ */ package io.netty.buffer; +import io.netty.util.ByteProcessor; import io.netty.util.IllegalReferenceCountException; import io.netty.util.ResourceLeakDetector; import io.netty.util.internal.PlatformDependent; @@ -965,7 +966,7 @@ public abstract class AbstractByteBuf extends ByteBuf { } @Override - public int forEachByte(ByteBufProcessor processor) { + public int forEachByte(ByteProcessor processor) { int index = readerIndex; int length = writerIndex - index; ensureAccessible(); @@ -973,12 +974,12 @@ public abstract class AbstractByteBuf extends ByteBuf { } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { checkIndex(index, length); return forEachByteAsc0(index, length, processor); } - private int forEachByteAsc0(int index, int length, ByteBufProcessor processor) { + private int forEachByteAsc0(int index, int length, ByteProcessor processor) { if (processor == null) { throw new NullPointerException("processor"); } @@ -1005,7 +1006,7 @@ public abstract class AbstractByteBuf extends ByteBuf { } @Override - public int forEachByteDesc(ByteBufProcessor processor) { + public int forEachByteDesc(ByteProcessor processor) { int index = readerIndex; int length = writerIndex - index; ensureAccessible(); @@ -1013,13 +1014,13 @@ public abstract class AbstractByteBuf extends ByteBuf { } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { checkIndex(index, length); return forEachByteDesc0(index, length, processor); } - private int forEachByteDesc0(int index, int length, ByteBufProcessor processor) { + private int forEachByteDesc0(int index, int length, ByteProcessor processor) { if (processor == null) { throw new NullPointerException("processor"); diff --git a/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java b/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java index 3866baf967..ef683596ca 100644 --- a/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java @@ -16,6 +16,7 @@ package io.netty.buffer; +import io.netty.util.ByteProcessor; import io.netty.util.ResourceLeak; import java.io.IOException; @@ -599,25 +600,25 @@ final class AdvancedLeakAwareByteBuf extends WrappedByteBuf { } @Override - public int forEachByte(ByteBufProcessor processor) { + public int forEachByte(ByteProcessor processor) { leak.record(); return super.forEachByte(processor); } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { leak.record(); return super.forEachByte(index, length, processor); } @Override - public int forEachByteDesc(ByteBufProcessor processor) { + public int forEachByteDesc(ByteProcessor processor) { leak.record(); return super.forEachByteDesc(processor); } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { leak.record(); return super.forEachByteDesc(index, length, processor); } diff --git a/buffer/src/main/java/io/netty/buffer/ByteBuf.java b/buffer/src/main/java/io/netty/buffer/ByteBuf.java index 4d399c683e..13d03ca262 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBuf.java @@ -15,6 +15,7 @@ */ package io.netty.buffer; +import io.netty.util.ByteProcessor; import io.netty.util.ReferenceCounted; import java.io.IOException; @@ -178,7 +179,7 @@ import java.nio.charset.UnsupportedCharsetException; * * For simple single-byte searches, use {@link #indexOf(int, int, byte)} and {@link #bytesBefore(int, int, byte)}. * {@link #bytesBefore(byte)} is especially useful when you deal with a {@code NUL}-terminated string. - * For complicated searches, use {@link #forEachByte(int, int, ByteBufProcessor)} with a {@link ByteBufProcessor} + * For complicated searches, use {@link #forEachByte(int, int, ByteProcessor)} with a {@link ByteProcessor} * implementation. * *

Mark and reset

@@ -1605,26 +1606,26 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable { * 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 ByteBufProcessor#process(byte)} returned {@code false}. + * The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}. */ - public abstract int forEachByte(ByteBufProcessor processor); + public abstract int forEachByte(ByteProcessor processor); /** * 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 ByteBufProcessor#process(byte)} returned {@code false}. + * The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}. */ - public abstract int forEachByte(int index, int length, ByteBufProcessor processor); + public abstract int forEachByte(int index, int length, ByteProcessor processor); /** * 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 ByteBufProcessor#process(byte)} returned {@code false}. + * The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}. */ - public abstract int forEachByteDesc(ByteBufProcessor processor); + public abstract int forEachByteDesc(ByteProcessor processor); /** * Iterates over the specified area of this buffer with the specified {@code processor} in descending order. @@ -1632,9 +1633,9 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable { * * * @return {@code -1} if the processor iterated to or beyond the beginning of the specified area. - * The last-visited index If the {@link ByteBufProcessor#process(byte)} returned {@code false}. + * The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}. */ - public abstract int forEachByteDesc(int index, int length, ByteBufProcessor processor); + public abstract int forEachByteDesc(int index, int length, ByteProcessor processor); /** * Returns a copy of this buffer's readable bytes. Modifying the content diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufProcessor.java b/buffer/src/main/java/io/netty/buffer/ByteBufProcessor.java index a1323a0ce6..13d063ff9e 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufProcessor.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBufProcessor.java @@ -16,10 +16,15 @@ package io.netty.buffer; -public interface ByteBufProcessor { +import io.netty.util.ByteProcessor; + +/** + * @deprecated Use {@link ByteProcessor}. + */ +public interface ByteBufProcessor extends ByteProcessor { /** - * Aborts on a {@code NUL (0x00)}. + * @deprecated Use {@link ByteProcessor#FIND_NULL}. */ ByteBufProcessor FIND_NUL = new ByteBufProcessor() { @Override @@ -29,7 +34,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a non-{@code NUL (0x00)}. + * @deprecated Use {@link ByteProcessor#FIND_NON_NUL}. */ ByteBufProcessor FIND_NON_NUL = new ByteBufProcessor() { @Override @@ -39,7 +44,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a {@code CR ('\r')}. + * @deprecated Use {@link ByteProcessor#FIND_CR}. */ ByteBufProcessor FIND_CR = new ByteBufProcessor() { @Override @@ -49,7 +54,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a non-{@code CR ('\r')}. + * @deprecated Use {@link ByteProcessor#FIND_NON_CR}. */ ByteBufProcessor FIND_NON_CR = new ByteBufProcessor() { @Override @@ -59,7 +64,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a {@code LF ('\n')}. + * @deprecated Use {@link ByteProcessor#FIND_LF}. */ ByteBufProcessor FIND_LF = new ByteBufProcessor() { @Override @@ -69,7 +74,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a non-{@code LF ('\n')}. + * @deprecated Use {@link ByteProcessor#FIND_NON_LF}. */ ByteBufProcessor FIND_NON_LF = new ByteBufProcessor() { @Override @@ -79,7 +84,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a {@code CR ('\r')} or a {@code LF ('\n')}. + * @deprecated Use {@link ByteProcessor#FIND_CRLF}. */ ByteBufProcessor FIND_CRLF = new ByteBufProcessor() { @Override @@ -89,7 +94,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a byte which is neither a {@code CR ('\r')} nor a {@code LF ('\n')}. + * @deprecated Use {@link ByteProcessor#FIND_NON_CRLF}. */ ByteBufProcessor FIND_NON_CRLF = new ByteBufProcessor() { @Override @@ -99,7 +104,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a linear whitespace (a ({@code ' '} or a {@code '\t'}). + * @deprecated Use {@link ByteProcessor#FIND_LINEAR_WHITESPACE}. */ ByteBufProcessor FIND_LINEAR_WHITESPACE = new ByteBufProcessor() { @Override @@ -109,7 +114,7 @@ public interface ByteBufProcessor { }; /** - * Aborts on a byte which is not a linear whitespace (neither {@code ' '} nor {@code '\t'}). + * @deprecated Use {@link ByteProcessor#FIND_NON_LINEAR_WHITESPACE}. */ ByteBufProcessor FIND_NON_LINEAR_WHITESPACE = new ByteBufProcessor() { @Override @@ -117,10 +122,4 @@ public interface ByteBufProcessor { return value == ' ' || value == '\t'; } }; - - /** - * @return {@code true} if the processor wants to continue the loop and handle the next byte in the buffer. - * {@code false} if the processor wants to stop handling bytes and abort the loop. - */ - boolean process(byte value) throws Exception; } diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java index 871db9bcdd..fbbe2b1c0f 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java @@ -15,6 +15,8 @@ */ package io.netty.buffer; +import static io.netty.util.internal.ObjectUtil.checkNotNull; +import io.netty.util.ByteString; import io.netty.util.CharsetUtil; import io.netty.util.Recycler; import io.netty.util.Recycler.Handle; @@ -31,6 +33,7 @@ import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; +import java.util.Arrays; import java.util.Locale; /** @@ -549,6 +552,85 @@ public final class ByteBufUtil { } } + /** + * Create a copy of the underlying storage from {@link value} into a byte array. + * 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()); + } + + /** + * Create a copy of the underlying storage from {@link buf} into a byte array. + * The copy will start at {@code start} and copy {@code length} bytes. + */ + public static byte[] getBytes(ByteBuf buf, int start, int length) { + return getBytes(buf, start, length, true); + } + + /** + * Return an array of the underlying storage from {@link buf} into a byte array. + * The copy will start at {@code start} and copy {@code length} bytes. + * If {@code copy} is true a copy will be made of the memory. + * 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) { + throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + + ") <= " + "buf.capacity(" + buf.capacity() + ')'); + } + + if (buf.hasArray()) { + if (copy || start != 0 || length != buf.capacity()) { + int baseOffset = buf.arrayOffset() + start; + return Arrays.copyOfRange(buf.array(), baseOffset, baseOffset + length); + } else { + return buf.array(); + } + } + + byte[] v = new byte[length]; + buf.getBytes(start, v); + return v; + } + + /** + * Copies the content of {@code src} to a {@link ByteBuf} using {@link ByteBuf#writeBytes(byte[], int, int)}. + * @param src The source of the data to copy. + * @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 static void copy(ByteString src, int srcIdx, ByteBuf dst, int dstIdx, int length) { + final int thisLen = src.length(); + + if (srcIdx < 0 || length > thisLen - srcIdx) { + throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + + length + ") <= srcLen(" + thisLen + ')'); + } + + checkNotNull(dst, "dst").setBytes(dstIdx, src.array(), srcIdx, length); + } + + /** + * Copies the content of {@code src} to a {@link ByteBuf} using {@link ByteBuf#writeBytes(byte[], int, int)}. + * @param src The source of the data to copy. + * @param srcIdx the starting offset of characters to copy. + * @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) { + throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + + length + ") <= srcLen(" + thisLen + ')'); + } + + checkNotNull(dst, "dst").writeBytes(src.array(), srcIdx, length); + } + static final class ThreadLocalUnsafeDirectByteBuf extends UnpooledUnsafeDirectByteBuf { private static final Recycler RECYCLER = diff --git a/buffer/src/main/java/io/netty/buffer/DuplicatedByteBuf.java b/buffer/src/main/java/io/netty/buffer/DuplicatedByteBuf.java index 43ff8b895c..d6f89644cd 100644 --- a/buffer/src/main/java/io/netty/buffer/DuplicatedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/DuplicatedByteBuf.java @@ -15,6 +15,8 @@ */ package io.netty.buffer; +import io.netty.util.ByteProcessor; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -293,12 +295,12 @@ public class DuplicatedByteBuf extends AbstractDerivedByteBuf { } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { return buffer.forEachByte(index, length, processor); } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { return buffer.forEachByteDesc(index, length, processor); } } diff --git a/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java b/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java index 9f8db89741..80facdb7fa 100644 --- a/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java @@ -16,6 +16,7 @@ package io.netty.buffer; +import io.netty.util.ByteProcessor; import io.netty.util.internal.EmptyArrays; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; @@ -668,23 +669,23 @@ public final class EmptyByteBuf extends ByteBuf { } @Override - public int forEachByte(ByteBufProcessor processor) { + public int forEachByte(ByteProcessor processor) { return -1; } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { checkIndex(index, length); return -1; } @Override - public int forEachByteDesc(ByteBufProcessor processor) { + public int forEachByteDesc(ByteProcessor processor) { return -1; } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { checkIndex(index, length); return -1; } diff --git a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBuf.java b/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBuf.java index a284c527d4..b6e46ab64c 100644 --- a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBuf.java @@ -15,6 +15,8 @@ */ package io.netty.buffer; +import io.netty.util.ByteProcessor; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -296,12 +298,12 @@ public class ReadOnlyByteBuf extends AbstractDerivedByteBuf { } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { return buffer.forEachByte(index, length, processor); } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { return buffer.forEachByteDesc(index, length, processor); } diff --git a/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java b/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java index 4c109fe62e..7683e13bc2 100644 --- a/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/SlicedByteBuf.java @@ -15,6 +15,8 @@ */ package io.netty.buffer; +import io.netty.util.ByteProcessor; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -275,7 +277,7 @@ public class SlicedByteBuf extends AbstractDerivedByteBuf { } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { int ret = buffer.forEachByte(index + adjustment, length, processor); if (ret >= adjustment) { return ret - adjustment; @@ -285,7 +287,7 @@ public class SlicedByteBuf extends AbstractDerivedByteBuf { } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { int ret = buffer.forEachByteDesc(index + adjustment, length, processor); if (ret >= adjustment) { return ret - adjustment; diff --git a/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java b/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java index 3c917c4f84..df257b45c9 100644 --- a/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java @@ -15,6 +15,8 @@ */ package io.netty.buffer; +import io.netty.util.ByteProcessor; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -680,22 +682,22 @@ public class SwappedByteBuf extends ByteBuf { } @Override - public int forEachByte(ByteBufProcessor processor) { + public int forEachByte(ByteProcessor processor) { return buf.forEachByte(processor); } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { return buf.forEachByte(index, length, processor); } @Override - public int forEachByteDesc(ByteBufProcessor processor) { + public int forEachByteDesc(ByteProcessor processor) { return buf.forEachByteDesc(processor); } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { return buf.forEachByteDesc(index, length, processor); } diff --git a/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java b/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java index eae17e0e74..d3050851cb 100644 --- a/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java @@ -16,6 +16,7 @@ package io.netty.buffer; +import io.netty.util.ByteProcessor; import io.netty.util.internal.StringUtil; import java.io.IOException; @@ -668,22 +669,22 @@ public class WrappedByteBuf extends ByteBuf { } @Override - public int forEachByte(ByteBufProcessor processor) { + public int forEachByte(ByteProcessor processor) { return buf.forEachByte(processor); } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { return buf.forEachByte(index, length, processor); } @Override - public int forEachByteDesc(ByteBufProcessor processor) { + public int forEachByteDesc(ByteProcessor processor) { return buf.forEachByteDesc(processor); } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { return buf.forEachByteDesc(index, length, processor); } diff --git a/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java b/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java index 684bbb2042..7bccf0b876 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java @@ -15,9 +15,11 @@ */ package io.netty.buffer; +import io.netty.util.ByteProcessor; import io.netty.util.CharsetUtil; import io.netty.util.IllegalReferenceCountException; import io.netty.util.internal.ThreadLocalRandom; + import org.junit.After; import org.junit.Assume; import org.junit.Before; @@ -1666,7 +1668,7 @@ public abstract class AbstractByteBufTest { final AtomicInteger lastIndex = new AtomicInteger(); buffer.setIndex(CAPACITY / 4, CAPACITY * 3 / 4); - assertThat(buffer.forEachByte(new ByteBufProcessor() { + assertThat(buffer.forEachByte(new ByteProcessor() { int i = CAPACITY / 4; @Override @@ -1689,7 +1691,7 @@ public abstract class AbstractByteBufTest { } final int stop = CAPACITY / 2; - assertThat(buffer.forEachByte(CAPACITY / 3, CAPACITY / 3, new ByteBufProcessor() { + assertThat(buffer.forEachByte(CAPACITY / 3, CAPACITY / 3, new ByteProcessor() { int i = CAPACITY / 3; @Override @@ -1713,7 +1715,7 @@ public abstract class AbstractByteBufTest { } final AtomicInteger lastIndex = new AtomicInteger(); - assertThat(buffer.forEachByteDesc(CAPACITY / 4, CAPACITY * 2 / 4, new ByteBufProcessor() { + assertThat(buffer.forEachByteDesc(CAPACITY / 4, CAPACITY * 2 / 4, new ByteProcessor() { int i = CAPACITY * 3 / 4 - 1; @Override @@ -2377,22 +2379,22 @@ public abstract class AbstractByteBufTest { @Test(expected = IllegalReferenceCountException.class) public void testForEachByteAfterRelease() { - releasedBuffer().forEachByte(new TestByteBufProcessor()); + releasedBuffer().forEachByte(new TestByteProcessor()); } @Test(expected = IllegalReferenceCountException.class) public void testForEachByteAfterRelease1() { - releasedBuffer().forEachByte(0, 1, new TestByteBufProcessor()); + releasedBuffer().forEachByte(0, 1, new TestByteProcessor()); } @Test(expected = IllegalReferenceCountException.class) public void testForEachByteDescAfterRelease() { - releasedBuffer().forEachByteDesc(new TestByteBufProcessor()); + releasedBuffer().forEachByteDesc(new TestByteProcessor()); } @Test(expected = IllegalReferenceCountException.class) public void testForEachByteDescAfterRelease1() { - releasedBuffer().forEachByteDesc(0, 1, new TestByteBufProcessor()); + releasedBuffer().forEachByteDesc(0, 1, new TestByteProcessor()); } @Test(expected = IllegalReferenceCountException.class) @@ -2647,7 +2649,7 @@ public abstract class AbstractByteBufTest { } } - private static final class TestByteBufProcessor implements ByteBufProcessor { + private static final class TestByteProcessor implements ByteProcessor { @Override public boolean process(byte value) throws Exception { return true; diff --git a/buffer/src/test/java/io/netty/buffer/ByteBufProcessorTest.java b/buffer/src/test/java/io/netty/buffer/ByteBufProcessorTest.java deleted file mode 100644 index 26e833489e..0000000000 --- a/buffer/src/test/java/io/netty/buffer/ByteBufProcessorTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.buffer; - -import io.netty.util.CharsetUtil; -import org.junit.Test; - -import static io.netty.util.ReferenceCountUtil.releaseLater; -import static org.junit.Assert.*; - -public class ByteBufProcessorTest { - @Test - public void testForward() { - final ByteBuf buf = releaseLater( - Unpooled.copiedBuffer("abc\r\n\ndef\r\rghi\n\njkl\0\0mno \t\tx", CharsetUtil.ISO_8859_1)); - final int length = buf.readableBytes(); - - assertEquals(3, buf.forEachByte(0, length, ByteBufProcessor.FIND_CRLF)); - assertEquals(6, buf.forEachByte(3, length - 3, ByteBufProcessor.FIND_NON_CRLF)); - assertEquals(9, buf.forEachByte(6, length - 6, ByteBufProcessor.FIND_CR)); - assertEquals(11, buf.forEachByte(9, length - 9, ByteBufProcessor.FIND_NON_CR)); - assertEquals(14, buf.forEachByte(11, length - 11, ByteBufProcessor.FIND_LF)); - assertEquals(16, buf.forEachByte(14, length - 14, ByteBufProcessor.FIND_NON_LF)); - assertEquals(19, buf.forEachByte(16, length - 16, ByteBufProcessor.FIND_NUL)); - assertEquals(21, buf.forEachByte(19, length - 19, ByteBufProcessor.FIND_NON_NUL)); - assertEquals(24, buf.forEachByte(21, length - 21, ByteBufProcessor.FIND_LINEAR_WHITESPACE)); - assertEquals(28, buf.forEachByte(24, length - 24, ByteBufProcessor.FIND_NON_LINEAR_WHITESPACE)); - assertEquals(-1, buf.forEachByte(28, length - 28, ByteBufProcessor.FIND_LINEAR_WHITESPACE)); - } - - @Test - public void testBackward() { - final ByteBuf buf = releaseLater( - Unpooled.copiedBuffer("abc\r\n\ndef\r\rghi\n\njkl\0\0mno \t\tx", CharsetUtil.ISO_8859_1)); - final int length = buf.readableBytes(); - - assertEquals(27, buf.forEachByteDesc(0, length, ByteBufProcessor.FIND_LINEAR_WHITESPACE)); - assertEquals(23, buf.forEachByteDesc(0, 28, ByteBufProcessor.FIND_NON_LINEAR_WHITESPACE)); - assertEquals(20, buf.forEachByteDesc(0, 24, ByteBufProcessor.FIND_NUL)); - assertEquals(18, buf.forEachByteDesc(0, 21, ByteBufProcessor.FIND_NON_NUL)); - assertEquals(15, buf.forEachByteDesc(0, 19, ByteBufProcessor.FIND_LF)); - assertEquals(13, buf.forEachByteDesc(0, 16, ByteBufProcessor.FIND_NON_LF)); - assertEquals(10, buf.forEachByteDesc(0, 14, ByteBufProcessor.FIND_CR)); - assertEquals(8, buf.forEachByteDesc(0, 11, ByteBufProcessor.FIND_NON_CR)); - assertEquals(5, buf.forEachByteDesc(0, 9, ByteBufProcessor.FIND_CRLF)); - assertEquals(2, buf.forEachByteDesc(0, 6, ByteBufProcessor.FIND_NON_CRLF)); - assertEquals(-1, buf.forEachByteDesc(0, 3, ByteBufProcessor.FIND_CRLF)); - } -} diff --git a/buffer/src/test/java/io/netty/buffer/ByteProcessorTest.java b/buffer/src/test/java/io/netty/buffer/ByteProcessorTest.java new file mode 100644 index 0000000000..6f2f3044ed --- /dev/null +++ b/buffer/src/test/java/io/netty/buffer/ByteProcessorTest.java @@ -0,0 +1,64 @@ +/* + * Copyright 2013 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.netty.buffer; + +import static io.netty.util.ReferenceCountUtil.releaseLater; +import static org.junit.Assert.assertEquals; +import io.netty.util.ByteProcessor; +import io.netty.util.CharsetUtil; + +import org.junit.Test; + +public class ByteProcessorTest { + @Test + public void testForward() { + final ByteBuf buf = releaseLater( + Unpooled.copiedBuffer("abc\r\n\ndef\r\rghi\n\njkl\0\0mno \t\tx", CharsetUtil.ISO_8859_1)); + final int length = buf.readableBytes(); + + assertEquals(3, buf.forEachByte(0, length, ByteProcessor.FIND_CRLF)); + assertEquals(6, buf.forEachByte(3, length - 3, ByteProcessor.FIND_NON_CRLF)); + assertEquals(9, buf.forEachByte(6, length - 6, ByteProcessor.FIND_CR)); + assertEquals(11, buf.forEachByte(9, length - 9, ByteProcessor.FIND_NON_CR)); + assertEquals(14, buf.forEachByte(11, length - 11, ByteProcessor.FIND_LF)); + assertEquals(16, buf.forEachByte(14, length - 14, ByteProcessor.FIND_NON_LF)); + assertEquals(19, buf.forEachByte(16, length - 16, ByteProcessor.FIND_NUL)); + assertEquals(21, buf.forEachByte(19, length - 19, ByteProcessor.FIND_NON_NUL)); + assertEquals(24, buf.forEachByte(21, length - 21, ByteProcessor.FIND_LINEAR_WHITESPACE)); + assertEquals(28, buf.forEachByte(24, length - 24, ByteProcessor.FIND_NON_LINEAR_WHITESPACE)); + assertEquals(-1, buf.forEachByte(28, length - 28, ByteProcessor.FIND_LINEAR_WHITESPACE)); + } + + @Test + public void testBackward() { + final ByteBuf buf = releaseLater( + Unpooled.copiedBuffer("abc\r\n\ndef\r\rghi\n\njkl\0\0mno \t\tx", CharsetUtil.ISO_8859_1)); + final int length = buf.readableBytes(); + + assertEquals(27, buf.forEachByteDesc(0, length, ByteProcessor.FIND_LINEAR_WHITESPACE)); + assertEquals(23, buf.forEachByteDesc(0, 28, ByteProcessor.FIND_NON_LINEAR_WHITESPACE)); + assertEquals(20, buf.forEachByteDesc(0, 24, ByteProcessor.FIND_NUL)); + assertEquals(18, buf.forEachByteDesc(0, 21, ByteProcessor.FIND_NON_NUL)); + assertEquals(15, buf.forEachByteDesc(0, 19, ByteProcessor.FIND_LF)); + assertEquals(13, buf.forEachByteDesc(0, 16, ByteProcessor.FIND_NON_LF)); + assertEquals(10, buf.forEachByteDesc(0, 14, ByteProcessor.FIND_CR)); + assertEquals(8, buf.forEachByteDesc(0, 11, ByteProcessor.FIND_NON_CR)); + assertEquals(5, buf.forEachByteDesc(0, 9, ByteProcessor.FIND_CRLF)); + assertEquals(2, buf.forEachByteDesc(0, 6, ByteProcessor.FIND_NON_CRLF)); + assertEquals(-1, buf.forEachByteDesc(0, 3, ByteProcessor.FIND_CRLF)); + } +} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java index f9e9d3370b..7172c00d03 100644 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java +++ b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java @@ -16,8 +16,8 @@ package io.netty.handler.codec.haproxy; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufProcessor; import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.AddressFamily; +import io.netty.util.ByteProcessor; import io.netty.util.CharsetUtil; import io.netty.util.NetUtil; import io.netty.util.internal.StringUtil; @@ -166,7 +166,7 @@ public final class HAProxyMessage { Math.min(addressInfoLen, header.readableBytes()) + " bytes (expected: 216+ bytes)"); } int startIdx = header.readerIndex(); - int addressEnd = header.forEachByte(startIdx, 108, ByteBufProcessor.FIND_NUL); + int addressEnd = header.forEachByte(startIdx, 108, ByteProcessor.FIND_NUL); if (addressEnd == -1) { addressLen = 108; } else { @@ -176,7 +176,7 @@ public final class HAProxyMessage { startIdx += 108; - addressEnd = header.forEachByte(startIdx, 108, ByteBufProcessor.FIND_NUL); + addressEnd = header.forEachByte(startIdx, 108, ByteProcessor.FIND_NUL); if (addressEnd == -1) { addressLen = 108; } else { 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 52e7dd204a..704c1426c3 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 @@ -15,9 +15,11 @@ */ package io.netty.handler.codec.http; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.DefaultTextHeaders; import io.netty.handler.codec.TextHeaders; +import io.netty.util.AsciiString; +import io.netty.util.ByteProcessor; +import io.netty.util.internal.PlatformDependent; import java.util.Calendar; import java.util.Date; @@ -81,17 +83,34 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader return seq; } - private static void validateValue(AsciiString seq) { - int state = 0; - // Start looping through each of the character - final int start = seq.arrayOffset(); - final int end = start + seq.length(); - final byte[] array = seq.array(); - for (int index = start; index < end; index++) { - state = validateValueChar(seq, state, (char) (array[index] & 0xFF)); + private static final class ValidateValueProcessor implements ByteProcessor { + private final CharSequence seq; + private int state; + + public ValidateValueProcessor(CharSequence seq) { + this.seq = seq; } - if (state != 0) { + @Override + public boolean process(byte value) throws Exception { + state = validateValueChar(state, (char) value, seq); + return true; + } + + public int state() { + return state; + } + } + + private static void validateValue(AsciiString seq) { + ValidateValueProcessor processor = new ValidateValueProcessor(seq); + try { + seq.forEachByte(processor); + } catch (Throwable t) { + PlatformDependent.throwException(t); + } + + if (processor.state() != 0) { throw new IllegalArgumentException("a header value must not end with '\\r' or '\\n':" + seq); } } @@ -100,7 +119,7 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader int state = 0; // Start looping through each of the character for (int index = 0; index < seq.length(); index++) { - state = validateValueChar(seq, state, seq.charAt(index)); + state = validateValueChar(state, seq.charAt(index), seq); } if (state != 0) { @@ -108,16 +127,16 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader } } - private static int validateValueChar(CharSequence seq, int state, char character) { + private static int validateValueChar(int state, char c, CharSequence seq) { /* * State: * 0: Previous character was neither CR nor LF * 1: The previous character was CR * 2: The previous character was LF */ - if ((character & HIGHEST_INVALID_VALUE_CHAR_MASK) == 0) { + if ((c & HIGHEST_INVALID_VALUE_CHAR_MASK) == 0) { // Check the absolutely prohibited characters. - switch (character) { + switch (c) { case 0x0: // NULL throw new IllegalArgumentException("a header value contains a prohibited character '\0': " + seq); case 0x0b: // Vertical tab @@ -130,7 +149,7 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader // Check the CRLF (HT | SP) pattern switch (state) { case 0: - switch (character) { + switch (c) { case '\r': state = 1; break; @@ -140,7 +159,7 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader } break; case 1: - switch (character) { + switch (c) { case '\n': state = 2; break; @@ -149,7 +168,7 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader } break; case 2: - switch (character) { + switch (c) { case '\t': case ' ': state = 0; @@ -165,6 +184,24 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader static class HttpHeadersNameConverter implements NameConverter { protected final boolean validate; + private static final class ValidateNameProcessor implements ByteProcessor { + private final CharSequence seq; + + public ValidateNameProcessor(CharSequence seq) { + this.seq = seq; + } + + @Override + public boolean process(byte value) throws Exception { + // Check to see if the character is not an ASCII character. + if (value < 0) { + throw new IllegalArgumentException("a header name cannot contain non-ASCII character: " + seq); + } + validateNameChar(value, seq); + return true; + } + } + HttpHeadersNameConverter(boolean validate) { this.validate = validate; } @@ -183,43 +220,33 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader } private static void validateName(AsciiString name) { - // Go through each characters in the name - final int start = name.arrayOffset(); - final int end = start + name.length(); - final byte[] array = name.array(); - for (int index = start; index < end; index ++) { - byte b = array[index]; - - // Check to see if the character is not an ASCII character - if (b < 0) { - throw new IllegalArgumentException("a header name cannot contain non-ASCII characters: " + name); - } - - // Check for prohibited characters. - validateNameChar(name, b); + try { + name.forEachByte(new ValidateNameProcessor(name)); + } catch (Throwable t) { + PlatformDependent.throwException(t); } } private static void validateName(CharSequence name) { - // Go through each characters in the name + // Go through each characters in the name. for (int index = 0; index < name.length(); index++) { - char character = name.charAt(index); + char c = name.charAt(index); - // Check to see if the character is not an ASCII character - if (character > 127) { + // Check to see if the character is not an ASCII character. + if (c > 127) { throw new IllegalArgumentException("a header name cannot contain non-ASCII characters: " + name); } // Check for prohibited characters. - validateNameChar(name, character); + validateNameChar(c, name); } } - private static void validateNameChar(CharSequence name, int character) { + private static void validateNameChar(int character, CharSequence seq) { if ((character & HIGHEST_INVALID_NAME_CHAR_MASK) == 0 && LOOKUP_TABLE[character] != 0) { throw new IllegalArgumentException( "a header name cannot contain the following prohibited characters: =,;: \\t\\r\\n\\v\\f: " + - name); + seq); } } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientUpgradeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientUpgradeHandler.java index fa7b35d21a..6ba33719b7 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientUpgradeHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientUpgradeHandler.java @@ -16,7 +16,7 @@ package io.netty.handler.codec.http; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; import java.util.Collection; import java.util.LinkedHashSet; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderNames.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderNames.java index a79fafebb6..5a6a024606 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderNames.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderNames.java @@ -16,7 +16,7 @@ package io.netty.handler.codec.http; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; /** * Standard HTTP header names. diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValues.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValues.java index ce4534337b..f2d1c91a9f 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValues.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderValues.java @@ -16,7 +16,7 @@ package io.netty.handler.codec.http; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; /** * Standard HTTP header values. diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java index b6343ebcd4..8be81f871a 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java @@ -17,7 +17,6 @@ package io.netty.handler.codec.http; import io.netty.handler.codec.TextHeaders; - /** * Provides the constants for the standard HTTP header names and values and * commonly used utility methods that accesses an {@link HttpMessage}. 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 6e2e344ec9..559c6f3ecb 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 @@ -17,8 +17,9 @@ package io.netty.handler.codec.http; import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.AsciiString; +import io.netty.buffer.ByteBufUtil; import io.netty.handler.codec.TextHeaders.EntryVisitor; +import io.netty.util.AsciiString; import java.util.Map.Entry; @@ -61,7 +62,7 @@ final class HttpHeadersEncoder implements EntryVisitor { } private static void writeAsciiString(ByteBuf buf, int offset, AsciiString value, int valueLen) { - value.copy(0, buf, offset, valueLen); + ByteBufUtil.copy(value, 0, buf, offset, valueLen); } private static void writeCharSequence(ByteBuf buf, int offset, CharSequence value, int valueLen) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMethod.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMethod.java index 35782663cd..f9c624e1e5 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMethod.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMethod.java @@ -15,7 +15,7 @@ */ package io.netty.handler.codec.http; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; import java.util.HashMap; import java.util.Map; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index 1ffc685727..4f59242566 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -16,13 +16,13 @@ package io.netty.handler.codec.http; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufProcessor; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.TooLongFrameException; +import io.netty.util.ByteProcessor; import io.netty.util.internal.AppendableCharSequence; import java.util.List; @@ -734,7 +734,7 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder { return result; } - private static class HeaderParser implements ByteBufProcessor { + private static class HeaderParser implements ByteProcessor { private final AppendableCharSequence seq; private final int maxLength; private int size; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestEncoder.java index 32ba70b631..14164fcc66 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestEncoder.java @@ -15,12 +15,13 @@ */ package io.netty.handler.codec.http; +import static io.netty.handler.codec.http.HttpConstants.CR; +import static io.netty.handler.codec.http.HttpConstants.LF; +import static io.netty.handler.codec.http.HttpConstants.SP; import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; import io.netty.util.CharsetUtil; -import static io.netty.handler.codec.http.HttpConstants.*; - /** * Encodes an {@link HttpRequest} or an {@link HttpContent} into * a {@link ByteBuf}. @@ -38,7 +39,7 @@ public class HttpRequestEncoder extends HttpObjectEncoder { @Override protected void encodeInitialLine(ByteBuf buf, HttpRequest request) throws Exception { AsciiString method = request.method().name(); - buf.writeBytes(method.array(), method.arrayOffset(), method.length()); + buf.writeBytes(method.array()); buf.writeByte(SP); // Add / as absolute path if no is present. @@ -75,7 +76,7 @@ public class HttpRequestEncoder extends HttpObjectEncoder { buf.writeByte(SP); AsciiString version = request.protocolVersion().text(); - buf.writeBytes(version.array(), version.arrayOffset(), version.length()); + buf.writeBytes(version.array()); buf.writeBytes(CRLF); } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseEncoder.java index 54ebfd41f0..8e6e513650 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseEncoder.java @@ -15,10 +15,11 @@ */ package io.netty.handler.codec.http; +import static io.netty.handler.codec.http.HttpConstants.CR; +import static io.netty.handler.codec.http.HttpConstants.LF; +import static io.netty.handler.codec.http.HttpConstants.SP; import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.AsciiString; - -import static io.netty.handler.codec.http.HttpConstants.*; +import io.netty.util.AsciiString; /** * Encodes an {@link HttpResponse} or an {@link HttpContent} into @@ -35,15 +36,15 @@ public class HttpResponseEncoder extends HttpObjectEncoder { @Override protected void encodeInitialLine(ByteBuf buf, HttpResponse response) throws Exception { AsciiString version = response.protocolVersion().text(); - buf.writeBytes(version.array(), version.arrayOffset(), version.length()); + buf.writeBytes(version.array()); buf.writeByte(SP); AsciiString code = response.status().codeAsText(); - buf.writeBytes(code.array(), code.arrayOffset(), code.length()); + buf.writeBytes(code.array()); buf.writeByte(SP); AsciiString reasonPhrase = response.status().reasonPhrase(); - buf.writeBytes(reasonPhrase.array(), reasonPhrase.arrayOffset(), reasonPhrase.length()); + buf.writeBytes(reasonPhrase.array()); buf.writeBytes(CRLF); } } 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 559c061067..bf4921d448 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 @@ -15,7 +15,9 @@ */ package io.netty.handler.codec.http; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; +import io.netty.util.ByteProcessor; +import io.netty.util.ByteString; /** * The response code and its description of HTTP or its derived protocols, such as @@ -464,6 +466,85 @@ public class HttpResponseStatus implements Comparable { } } + private static final class HttpStatusLineProcessor implements ByteProcessor { + private static final byte ASCII_SPACE = (byte) ' '; + private final ByteString string; + private int i; + /** + * 0 = New or havn't seen {@link ASCII_SPACE}. + * 1 = Last byte was {@link ASCII_SPACE}. + * 2 = Terminal State. Processed the byte after {@link ASCII_SPACE}, and parsed the status line. + * 3 = Terminal State. There was no byte after {@link ASCII_SPACE} but status has been parsed with what we saw. + */ + private int state; + private HttpResponseStatus status; + + public HttpStatusLineProcessor(ByteString string) { + this.string = string; + } + + @Override + public boolean process(byte value) { + switch (state) { + case 0: + if (value == ASCII_SPACE) { + state = 1; + } + break; + case 1: + parseStatus(i); + state = 2; + return false; + default: + break; + } + ++i; + return true; + } + + private void parseStatus(int codeEnd) { + int code = string.parseAsciiInt(0, codeEnd); + status = valueOf(code); + if (codeEnd < string.length()) { + String actualReason = string.toString(codeEnd + 1, string.length()); + if (!status.reasonPhrase().equals(actualReason)) { + status = new HttpResponseStatus(code, actualReason); + } + } + } + + public HttpResponseStatus status() { + if (state <= 1) { + parseStatus(string.length()); + state = 3; + } + return status; + } + } + + /** + * Parses the specified HTTP status line into a {@link HttpResponseStatus}. The expected formats of the line are: + *
    + *
  • {@code statusCode} (e.g. 200)
  • + *
  • {@code statusCode} {@code reasonPhrase} (e.g. 404 Not Found)
  • + *
+ * + * @throws IllegalArgumentException if the specified status line is malformed + */ + public static HttpResponseStatus parseLine(ByteString line) { + try { + HttpStatusLineProcessor processor = new HttpStatusLineProcessor(line); + line.forEachByte(processor); + HttpResponseStatus status = processor.status(); + if (status == null) { + throw new IllegalArgumentException("unable to get status after parsing input"); + } + return status; + } catch (Exception e) { + throw new IllegalArgumentException("malformed status line: " + line, e); + } + } + private final int code; private final AsciiString codeAsText; private HttpStatusClass codeClass; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java index 7ed7c7b3be..326932beee 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerUpgradeHandler.java @@ -17,7 +17,7 @@ package io.netty.handler.codec.http; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCounted; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpStatusClass.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpStatusClass.java index 62d9092fb4..9f57e18984 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpStatusClass.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpStatusClass.java @@ -16,7 +16,7 @@ package io.netty.handler.codec.http; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; /** * The class of HTTP status. diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpVersion.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpVersion.java index a18170ab7e..01403c4b2d 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpVersion.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpVersion.java @@ -15,7 +15,7 @@ */ package io.netty.handler.codec.http; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/Utf8Validator.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/Utf8Validator.java index d4a612fcbf..3a377e7c5b 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/Utf8Validator.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/Utf8Validator.java @@ -36,13 +36,13 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufProcessor; import io.netty.handler.codec.CorruptedFrameException; +import io.netty.util.ByteProcessor; /** * Checks UTF8 bytes for validity */ -final class Utf8Validator implements ByteBufProcessor { +final class Utf8Validator implements ByteProcessor { private static final int UTF8_ACCEPT = 0; private static final int UTF8_REJECT = 12; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java index 1bf1d1c369..51d69e02f4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java @@ -17,7 +17,6 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -27,6 +26,7 @@ import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; +import io.netty.util.AsciiString; import java.net.URI; import java.nio.ByteBuffer; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java index fefd529738..a3dc0671e4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java @@ -15,12 +15,12 @@ */ package io.netty.handler.codec.http.websocketx; +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelPromise; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -28,11 +28,10 @@ import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.util.AsciiString; import java.util.regex.Pattern; -import static io.netty.handler.codec.http.HttpVersion.*; - /** *

* Performs server side opening and closing handshakes for web socket specification version { @Override protected void encodeInitialLine(ByteBuf buf, HttpRequest request) throws Exception { AsciiString method = request.method().name(); - buf.writeBytes(method.array(), method.arrayOffset(), method.length()); + buf.writeBytes(method.array()); buf.writeByte(SP); buf.writeBytes(request.uri().getBytes(CharsetUtil.UTF_8)); buf.writeByte(SP); AsciiString version = request.protocolVersion().text(); - buf.writeBytes(version.array(), version.arrayOffset(), version.length()); + buf.writeBytes(version.array()); buf.writeBytes(CRLF); } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspResponseEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspResponseEncoder.java index 49534be4ca..b723e6ebcf 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspResponseEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspResponseEncoder.java @@ -15,12 +15,13 @@ */ package io.netty.handler.codec.rtsp; +import static io.netty.handler.codec.http.HttpConstants.CR; +import static io.netty.handler.codec.http.HttpConstants.LF; +import static io.netty.handler.codec.http.HttpConstants.SP; import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpResponse; - -import static io.netty.handler.codec.http.HttpConstants.*; +import io.netty.util.AsciiString; /** * Encodes an RTSP response represented in {@link FullHttpResponse} into @@ -37,15 +38,15 @@ public class RtspResponseEncoder extends RtspObjectEncoder { @Override protected void encodeInitialLine(ByteBuf buf, HttpResponse response) throws Exception { AsciiString version = response.protocolVersion().text(); - buf.writeBytes(version.array(), version.arrayOffset(), version.length()); + buf.writeBytes(version.array()); buf.writeByte(SP); AsciiString code = response.status().codeAsText(); - buf.writeBytes(code.array(), code.arrayOffset(), code.length()); + buf.writeBytes(code.array()); buf.writeByte(SP); AsciiString reasonPhrase = response.status().reasonPhrase(); - buf.writeBytes(reasonPhrase.array(), reasonPhrase.arrayOffset(), reasonPhrase.length()); + buf.writeBytes(reasonPhrase.array()); buf.writeBytes(CRLF); } } 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 978e1a5a3f..53c00b087a 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 @@ -15,10 +15,10 @@ */ package io.netty.handler.codec.spdy; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.DefaultTextHeaders; import io.netty.handler.codec.Headers; import io.netty.handler.codec.TextHeaders; +import io.netty.util.AsciiString; import java.util.Locale; 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 a5a5692541..0b93fa28c4 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 @@ -18,8 +18,10 @@ 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.handler.codec.AsciiString; +import io.netty.util.AsciiString; +import io.netty.util.ByteString; import io.netty.util.CharsetUtil; import java.util.Set; @@ -57,18 +59,20 @@ public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder { ByteBuf headerBlock = alloc.heapBuffer(); writeLengthField(headerBlock, numHeaders); for (CharSequence name: names) { - byte[] nameBytes = AsciiString.getBytes(name, CharsetUtil.UTF_8); - writeLengthField(headerBlock, nameBytes.length); - headerBlock.writeBytes(nameBytes); + final ByteString nameBytes = new ByteString(name, CharsetUtil.UTF_8); + int length = nameBytes.length(); + writeLengthField(headerBlock, length); + ByteBufUtil.copy(nameBytes, 0, headerBlock, length); int savedIndex = headerBlock.writerIndex(); int valueLength = 0; writeLengthField(headerBlock, valueLength); for (CharSequence value: frame.headers().getAll(name)) { - byte[] valueBytes = AsciiString.getBytes(value, CharsetUtil.UTF_8); - if (valueBytes.length > 0) { - headerBlock.writeBytes(valueBytes); + final ByteString valueBytes = new ByteString(value, CharsetUtil.UTF_8); + length = valueBytes.length(); + if (length > 0) { + ByteBufUtil.copy(valueBytes, 0, headerBlock, length); headerBlock.writeByte(0); - valueLength += valueBytes.length + 1; + valueLength += length + 1; } } if (valueLength != 0) { 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 44a4d7d94b..e82fccef2d 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 @@ -15,8 +15,8 @@ */ package io.netty.handler.codec.spdy; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.TextHeaders; +import io.netty.util.AsciiString; /** * Provides the constants for the standard SPDY HTTP header names and commonly diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java index 29cc13a81c..eb1113c08b 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java @@ -15,7 +15,7 @@ */ package io.netty.handler.codec.spdy; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; /** * Provides the constants for the header names used by the {@link SpdyHttpDecoder} and {@link SpdyHttpEncoder}. diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeaderUtilTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeaderUtilTest.java index 3c28d917aa..596cfb556d 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeaderUtilTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeaderUtilTest.java @@ -15,7 +15,8 @@ */ package io.netty.handler.codec.http; -import io.netty.handler.codec.AsciiString; +import io.netty.util.AsciiString; + import org.junit.Test; import java.util.List; 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 4d91377550..609a39474d 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 @@ -28,10 +28,10 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromiseAggregator; import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.AsciiString; 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 decorating HTTP2 encoder that will compress data frames according to the {@code content-encoding} header for each @@ -170,11 +170,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(AsciiString contentEncoding) throws Http2Exception { - if (GZIP.equalsIgnoreCase(contentEncoding) || X_GZIP.equalsIgnoreCase(contentEncoding)) { + protected EmbeddedChannel newContentCompressor(ByteString contentEncoding) throws Http2Exception { + if (GZIP.equals(contentEncoding) || X_GZIP.equals(contentEncoding)) { return newCompressionChannel(ZlibWrapper.GZIP); } - if (DEFLATE.equalsIgnoreCase(contentEncoding) || X_DEFLATE.equalsIgnoreCase(contentEncoding)) { + if (DEFLATE.equals(contentEncoding) || X_DEFLATE.equals(contentEncoding)) { return newCompressionChannel(ZlibWrapper.ZLIB); } // 'identity' or unsupported @@ -189,7 +189,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 AsciiString getTargetContentEncoding(AsciiString contentEncoding) throws Http2Exception { + protected ByteString getTargetContentEncoding(ByteString contentEncoding) throws Http2Exception { return contentEncoding; } @@ -219,7 +219,7 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE EmbeddedChannel compressor = stream.getProperty(CompressorHttp2ConnectionEncoder.class); if (compressor == null) { if (!endOfStream) { - AsciiString encoding = headers.get(CONTENT_ENCODING); + ByteString encoding = headers.get(CONTENT_ENCODING); if (encoding == null) { encoding = IDENTITY; } @@ -227,8 +227,8 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE compressor = newContentCompressor(encoding); if (compressor != null) { stream.setProperty(CompressorHttp2ConnectionEncoder.class, compressor); - AsciiString targetContentEncoding = getTargetContentEncoding(encoding); - if (IDENTITY.equalsIgnoreCase(targetContentEncoding)) { + ByteString targetContentEncoding = getTargetContentEncoding(encoding); + if (IDENTITY.equals(targetContentEncoding)) { headers.remove(CONTENT_ENCODING); } else { headers.set(CONTENT_ENCODING, targetContentEncoding); 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 71f802af37..1b515de968 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,11 +14,63 @@ */ package io.netty.handler.codec.http2; -import io.netty.handler.codec.AsciiString; +import static io.netty.util.internal.StringUtil.UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET; import io.netty.handler.codec.BinaryHeaders; import io.netty.handler.codec.DefaultBinaryHeaders; +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 DefaultBinaryHeaders implements Http2Headers { + private static final ByteProcessor HTTP2_ASCII_UPPERCASE_PROCESSOR = new ByteProcessor() { + @Override + public boolean process(byte value) throws Exception { + return value < 'A' || value > 'Z'; + } + }; + + private static final class Http2AsciiToLowerCaseConverter implements ByteProcessor { + private final byte[] result; + private int i; + + public Http2AsciiToLowerCaseConverter(int length) { + result = new byte[length]; + } + + @Override + public boolean process(byte value) throws Exception { + result[i++] = (value >= 'A' && value <= 'Z') + ? (byte) (value + UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET) : value; + return true; + } + + public byte[] result() { + return result; + } + }; + + private static final NameConverter HTTP2_ASCII_TO_LOWER_CONVERTER = new NameConverter() { + @Override + public ByteString convertName(ByteString name) { + if (name instanceof AsciiString) { + return ((AsciiString) name).toLowerCase(); + } + + try { + if (name.forEachByte(HTTP2_ASCII_UPPERCASE_PROCESSOR) == -1) { + return name; + } + + Http2AsciiToLowerCaseConverter converter = new Http2AsciiToLowerCaseConverter(name.length()); + name.forEachByte(converter); + return new ByteString(converter.result(), false); + } catch (Exception e) { + PlatformDependent.throwException(e); + return null; + } + } + }; /** * Creates an instance that will convert all header names to lowercase. @@ -41,95 +93,95 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He * @param forceKeyToLower if @{code false} no header name conversion will be performed */ public DefaultHttp2Headers(boolean forceKeyToLower) { - super(forceKeyToLower); + super(forceKeyToLower ? HTTP2_ASCII_TO_LOWER_CONVERTER : IDENTITY_NAME_CONVERTER); } @Override - public Http2Headers add(AsciiString name, AsciiString value) { + public Http2Headers add(ByteString name, ByteString value) { super.add(name, value); return this; } @Override - public Http2Headers add(AsciiString name, Iterable values) { + public Http2Headers add(ByteString name, Iterable values) { super.add(name, values); return this; } @Override - public Http2Headers add(AsciiString name, AsciiString... values) { + public Http2Headers add(ByteString name, ByteString... values) { super.add(name, values); return this; } @Override - public Http2Headers addObject(AsciiString name, Object value) { + public Http2Headers addObject(ByteString name, Object value) { super.addObject(name, value); return this; } @Override - public Http2Headers addObject(AsciiString name, Iterable values) { + public Http2Headers addObject(ByteString name, Iterable values) { super.addObject(name, values); return this; } @Override - public Http2Headers addObject(AsciiString name, Object... values) { + public Http2Headers addObject(ByteString name, Object... values) { super.addObject(name, values); return this; } @Override - public Http2Headers addBoolean(AsciiString name, boolean value) { + public Http2Headers addBoolean(ByteString name, boolean value) { super.addBoolean(name, value); return this; } @Override - public Http2Headers addChar(AsciiString name, char value) { + public Http2Headers addChar(ByteString name, char value) { super.addChar(name, value); return this; } @Override - public Http2Headers addByte(AsciiString name, byte value) { + public Http2Headers addByte(ByteString name, byte value) { super.addByte(name, value); return this; } @Override - public Http2Headers addShort(AsciiString name, short value) { + public Http2Headers addShort(ByteString name, short value) { super.addShort(name, value); return this; } @Override - public Http2Headers addInt(AsciiString name, int value) { + public Http2Headers addInt(ByteString name, int value) { super.addInt(name, value); return this; } @Override - public Http2Headers addLong(AsciiString name, long value) { + public Http2Headers addLong(ByteString name, long value) { super.addLong(name, value); return this; } @Override - public Http2Headers addFloat(AsciiString name, float value) { + public Http2Headers addFloat(ByteString name, float value) { super.addFloat(name, value); return this; } @Override - public Http2Headers addDouble(AsciiString name, double value) { + public Http2Headers addDouble(ByteString name, double value) { super.addDouble(name, value); return this; } @Override - public Http2Headers addTimeMillis(AsciiString name, long value) { + public Http2Headers addTimeMillis(ByteString name, long value) { super.addTimeMillis(name, value); return this; } @@ -141,91 +193,91 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He } @Override - public Http2Headers set(AsciiString name, AsciiString value) { + public Http2Headers set(ByteString name, ByteString value) { super.set(name, value); return this; } @Override - public Http2Headers set(AsciiString name, Iterable values) { + public Http2Headers set(ByteString name, Iterable values) { super.set(name, values); return this; } @Override - public Http2Headers set(AsciiString name, AsciiString... values) { + public Http2Headers set(ByteString name, ByteString... values) { super.set(name, values); return this; } @Override - public Http2Headers setObject(AsciiString name, Object value) { + public Http2Headers setObject(ByteString name, Object value) { super.setObject(name, value); return this; } @Override - public Http2Headers setObject(AsciiString name, Iterable values) { + public Http2Headers setObject(ByteString name, Iterable values) { super.setObject(name, values); return this; } @Override - public Http2Headers setObject(AsciiString name, Object... values) { + public Http2Headers setObject(ByteString name, Object... values) { super.setObject(name, values); return this; } @Override - public Http2Headers setBoolean(AsciiString name, boolean value) { + public Http2Headers setBoolean(ByteString name, boolean value) { super.setBoolean(name, value); return this; } @Override - public Http2Headers setChar(AsciiString name, char value) { + public Http2Headers setChar(ByteString name, char value) { super.setChar(name, value); return this; } @Override - public Http2Headers setByte(AsciiString name, byte value) { + public Http2Headers setByte(ByteString name, byte value) { super.setByte(name, value); return this; } @Override - public Http2Headers setShort(AsciiString name, short value) { + public Http2Headers setShort(ByteString name, short value) { super.setShort(name, value); return this; } @Override - public Http2Headers setInt(AsciiString name, int value) { + public Http2Headers setInt(ByteString name, int value) { super.setInt(name, value); return this; } @Override - public Http2Headers setLong(AsciiString name, long value) { + public Http2Headers setLong(ByteString name, long value) { super.setLong(name, value); return this; } @Override - public Http2Headers setFloat(AsciiString name, float value) { + public Http2Headers setFloat(ByteString name, float value) { super.setFloat(name, value); return this; } @Override - public Http2Headers setDouble(AsciiString name, double value) { + public Http2Headers setDouble(ByteString name, double value) { super.setDouble(name, value); return this; } @Override - public Http2Headers setTimeMillis(AsciiString name, long value) { + public Http2Headers setTimeMillis(ByteString name, long value) { super.setTimeMillis(name, value); return this; } @@ -249,57 +301,57 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He } @Override - public Http2Headers method(AsciiString value) { + public Http2Headers method(ByteString value) { set(PseudoHeaderName.METHOD.value(), value); return this; } @Override - public Http2Headers scheme(AsciiString value) { + public Http2Headers scheme(ByteString value) { set(PseudoHeaderName.SCHEME.value(), value); return this; } @Override - public Http2Headers authority(AsciiString value) { + public Http2Headers authority(ByteString value) { set(PseudoHeaderName.AUTHORITY.value(), value); return this; } @Override - public Http2Headers path(AsciiString value) { + public Http2Headers path(ByteString value) { set(PseudoHeaderName.PATH.value(), value); return this; } @Override - public Http2Headers status(AsciiString value) { + public Http2Headers status(ByteString value) { set(PseudoHeaderName.STATUS.value(), value); return this; } @Override - public AsciiString method() { + public ByteString method() { return get(PseudoHeaderName.METHOD.value()); } @Override - public AsciiString scheme() { + public ByteString scheme() { return get(PseudoHeaderName.SCHEME.value()); } @Override - public AsciiString authority() { + public ByteString authority() { return get(PseudoHeaderName.AUTHORITY.value()); } @Override - public AsciiString path() { + public ByteString path() { return get(PseudoHeaderName.PATH.value()); } @Override - public AsciiString status() { + public ByteString status() { return get(PseudoHeaderName.STATUS.value()); } } 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 4f4a3b31db..be207263c2 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 @@ -17,13 +17,13 @@ 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.Http2CodecUtil.DEFAULT_MAX_HEADER_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.Http2Error.COMPRESSION_ERROR; import static io.netty.handler.codec.http2.Http2Exception.connectionError; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; -import io.netty.handler.codec.AsciiString; +import io.netty.util.ByteString; import java.io.IOException; import java.io.InputStream; @@ -62,7 +62,7 @@ public class DefaultHttp2HeadersDecoder implements Http2HeadersDecoder, Http2Hea HeaderListener listener = new HeaderListener() { @Override public void addHeader(byte[] key, byte[] value, boolean sensitive) { - headers.add(new AsciiString(key, false), new AsciiString(value, false)); + headers.add(new ByteString(key, false), new ByteString(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 114d5e2ad2..b619606cc5 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 @@ -23,8 +23,8 @@ import static io.netty.handler.codec.http2.Http2Exception.connectionError; import static io.netty.util.internal.ObjectUtil.checkNotNull; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufOutputStream; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.BinaryHeaders.EntryVisitor; +import io.netty.util.ByteString; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -67,8 +67,8 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2Hea // Write pseudo headers first as required by the HTTP/2 spec. for (Http2Headers.PseudoHeaderName pseudoHeader : Http2Headers.PseudoHeaderName.values()) { - AsciiString name = pseudoHeader.value(); - AsciiString value = headers.get(name); + ByteString name = pseudoHeader.value(); + ByteString value = headers.get(name); if (value != null) { encodeHeader(name, value, stream); } @@ -76,9 +76,9 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2Hea headers.forEachEntry(new EntryVisitor() { @Override - public boolean visit(Entry entry) throws Exception { - final AsciiString name = entry.getKey(); - final AsciiString value = entry.getValue(); + public boolean visit(Entry entry) throws Exception { + final ByteString name = entry.getKey(); + final ByteString value = entry.getValue(); if (!Http2Headers.PseudoHeaderName.isPseudoHeader(name)) { encodeHeader(name, value, stream); } @@ -108,7 +108,7 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2Hea return this; } - private void encodeHeader(AsciiString key, AsciiString value, OutputStream stream) throws IOException { + private void encodeHeader(ByteString key, ByteString value, OutputStream stream) throws IOException { encoder.encodeHeader(stream, key.array(), value.array(), 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 9cff863d5a..eb8d15c120 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 @@ -28,10 +28,10 @@ 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.AsciiString; 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 @@ -159,13 +159,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(AsciiString contentEncoding) throws Http2Exception { - if (GZIP.equalsIgnoreCase(contentEncoding) || - X_GZIP.equalsIgnoreCase(contentEncoding)) { + protected EmbeddedChannel newContentDecompressor(ByteString contentEncoding) throws Http2Exception { + if (GZIP.equals(contentEncoding) || X_GZIP.equals(contentEncoding)) { return new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP)); } - if (DEFLATE.equalsIgnoreCase(contentEncoding) || - X_DEFLATE.equalsIgnoreCase(contentEncoding)) { + if (DEFLATE.equals(contentEncoding) || X_DEFLATE.equals(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)); @@ -182,7 +180,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 AsciiString getTargetContentEncoding(@SuppressWarnings("UnusedParameters") AsciiString contentEncoding) + protected ByteString getTargetContentEncoding(@SuppressWarnings("UnusedParameters") ByteString contentEncoding) throws Http2Exception { return IDENTITY; } @@ -205,7 +203,7 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor Http2Decompressor decompressor = decompressor(stream); if (decompressor == null && !endOfStream) { // Determine the content encoding. - AsciiString contentEncoding = headers.get(CONTENT_ENCODING); + ByteString contentEncoding = headers.get(CONTENT_ENCODING); if (contentEncoding == null) { contentEncoding = IDENTITY; } @@ -215,8 +213,8 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor stream.setProperty(Http2Decompressor.class, decompressor); // Decode the content and remove or replace the existing headers // so that the message looks like a decoded message. - AsciiString targetContentEncoding = getTargetContentEncoding(contentEncoding); - if (IDENTITY.equalsIgnoreCase(targetContentEncoding)) { + ByteString targetContentEncoding = getTargetContentEncoding(contentEncoding); + if (IDENTITY.equals(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 582c7e346a..e08941ba9f 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 @@ -15,9 +15,9 @@ package io.netty.handler.codec.http2; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.BinaryHeaders; import io.netty.handler.codec.EmptyBinaryHeaders; +import io.netty.util.ByteString; public final class EmptyHttp2Headers extends EmptyBinaryHeaders implements Http2Headers { public static final EmptyHttp2Headers INSTANCE = new EmptyHttp2Headers(); @@ -26,91 +26,91 @@ public final class EmptyHttp2Headers extends EmptyBinaryHeaders implements Http2 } @Override - public Http2Headers add(AsciiString name, AsciiString value) { + public Http2Headers add(ByteString name, ByteString value) { super.add(name, value); return this; } @Override - public Http2Headers add(AsciiString name, Iterable values) { + public Http2Headers add(ByteString name, Iterable values) { super.add(name, values); return this; } @Override - public Http2Headers add(AsciiString name, AsciiString... values) { + public Http2Headers add(ByteString name, ByteString... values) { super.add(name, values); return this; } @Override - public Http2Headers addObject(AsciiString name, Object value) { + public Http2Headers addObject(ByteString name, Object value) { super.addObject(name, value); return this; } @Override - public Http2Headers addObject(AsciiString name, Iterable values) { + public Http2Headers addObject(ByteString name, Iterable values) { super.addObject(name, values); return this; } @Override - public Http2Headers addObject(AsciiString name, Object... values) { + public Http2Headers addObject(ByteString name, Object... values) { super.addObject(name, values); return this; } @Override - public Http2Headers addBoolean(AsciiString name, boolean value) { + public Http2Headers addBoolean(ByteString name, boolean value) { super.addBoolean(name, value); return this; } @Override - public Http2Headers addChar(AsciiString name, char value) { + public Http2Headers addChar(ByteString name, char value) { super.addChar(name, value); return this; } @Override - public Http2Headers addByte(AsciiString name, byte value) { + public Http2Headers addByte(ByteString name, byte value) { super.addByte(name, value); return this; } @Override - public Http2Headers addShort(AsciiString name, short value) { + public Http2Headers addShort(ByteString name, short value) { super.addShort(name, value); return this; } @Override - public Http2Headers addInt(AsciiString name, int value) { + public Http2Headers addInt(ByteString name, int value) { super.addInt(name, value); return this; } @Override - public Http2Headers addLong(AsciiString name, long value) { + public Http2Headers addLong(ByteString name, long value) { super.addLong(name, value); return this; } @Override - public Http2Headers addFloat(AsciiString name, float value) { + public Http2Headers addFloat(ByteString name, float value) { super.addFloat(name, value); return this; } @Override - public Http2Headers addDouble(AsciiString name, double value) { + public Http2Headers addDouble(ByteString name, double value) { super.addDouble(name, value); return this; } @Override - public Http2Headers addTimeMillis(AsciiString name, long value) { + public Http2Headers addTimeMillis(ByteString name, long value) { super.addTimeMillis(name, value); return this; } @@ -122,91 +122,91 @@ public final class EmptyHttp2Headers extends EmptyBinaryHeaders implements Http2 } @Override - public Http2Headers set(AsciiString name, AsciiString value) { + public Http2Headers set(ByteString name, ByteString value) { super.set(name, value); return this; } @Override - public Http2Headers set(AsciiString name, Iterable values) { + public Http2Headers set(ByteString name, Iterable values) { super.set(name, values); return this; } @Override - public Http2Headers set(AsciiString name, AsciiString... values) { + public Http2Headers set(ByteString name, ByteString... values) { super.set(name, values); return this; } @Override - public Http2Headers setObject(AsciiString name, Object value) { + public Http2Headers setObject(ByteString name, Object value) { super.setObject(name, value); return this; } @Override - public Http2Headers setObject(AsciiString name, Iterable values) { + public Http2Headers setObject(ByteString name, Iterable values) { super.setObject(name, values); return this; } @Override - public Http2Headers setObject(AsciiString name, Object... values) { + public Http2Headers setObject(ByteString name, Object... values) { super.setObject(name, values); return this; } @Override - public Http2Headers setBoolean(AsciiString name, boolean value) { + public Http2Headers setBoolean(ByteString name, boolean value) { super.setBoolean(name, value); return this; } @Override - public Http2Headers setChar(AsciiString name, char value) { + public Http2Headers setChar(ByteString name, char value) { super.setChar(name, value); return this; } @Override - public Http2Headers setByte(AsciiString name, byte value) { + public Http2Headers setByte(ByteString name, byte value) { super.setByte(name, value); return this; } @Override - public Http2Headers setShort(AsciiString name, short value) { + public Http2Headers setShort(ByteString name, short value) { super.setShort(name, value); return this; } @Override - public Http2Headers setInt(AsciiString name, int value) { + public Http2Headers setInt(ByteString name, int value) { super.setInt(name, value); return this; } @Override - public Http2Headers setLong(AsciiString name, long value) { + public Http2Headers setLong(ByteString name, long value) { super.setLong(name, value); return this; } @Override - public Http2Headers setFloat(AsciiString name, float value) { + public Http2Headers setFloat(ByteString name, float value) { super.setFloat(name, value); return this; } @Override - public Http2Headers setDouble(AsciiString name, double value) { + public Http2Headers setDouble(ByteString name, double value) { super.setDouble(name, value); return this; } @Override - public Http2Headers setTimeMillis(AsciiString name, long value) { + public Http2Headers setTimeMillis(ByteString name, long value) { super.setTimeMillis(name, value); return this; } @@ -230,52 +230,52 @@ public final class EmptyHttp2Headers extends EmptyBinaryHeaders implements Http2 } @Override - public EmptyHttp2Headers method(AsciiString method) { + public EmptyHttp2Headers method(ByteString method) { throw new UnsupportedOperationException(); } @Override - public EmptyHttp2Headers scheme(AsciiString status) { + public EmptyHttp2Headers scheme(ByteString status) { throw new UnsupportedOperationException(); } @Override - public EmptyHttp2Headers authority(AsciiString authority) { + public EmptyHttp2Headers authority(ByteString authority) { throw new UnsupportedOperationException(); } @Override - public EmptyHttp2Headers path(AsciiString path) { + public EmptyHttp2Headers path(ByteString path) { throw new UnsupportedOperationException(); } @Override - public EmptyHttp2Headers status(AsciiString status) { + public EmptyHttp2Headers status(ByteString status) { throw new UnsupportedOperationException(); } @Override - public AsciiString method() { + public ByteString method() { return get(PseudoHeaderName.METHOD.value()); } @Override - public AsciiString scheme() { + public ByteString scheme() { return get(PseudoHeaderName.SCHEME.value()); } @Override - public AsciiString authority() { + public ByteString authority() { return get(PseudoHeaderName.AUTHORITY.value()); } @Override - public AsciiString path() { + public ByteString path() { return get(PseudoHeaderName.PATH.value()); } @Override - public AsciiString status() { + public ByteString 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 7adbfc7891..5b30fc713a 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,8 +15,9 @@ package io.netty.handler.codec.http2; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.BinaryHeaders; +import io.netty.util.ByteString; +import io.netty.util.CharsetUtil; import java.util.HashSet; import java.util.Set; @@ -55,8 +56,8 @@ public interface Http2Headers extends BinaryHeaders { */ STATUS(":status"); - private final AsciiString value; - private static final Set PSEUDO_HEADERS = new HashSet(); + private final ByteString value; + private static final Set PSEUDO_HEADERS = new HashSet(); static { for (PseudoHeaderName pseudoHeader : PseudoHeaderName.values()) { PSEUDO_HEADERS.add(pseudoHeader.value()); @@ -64,10 +65,10 @@ public interface Http2Headers extends BinaryHeaders { } PseudoHeaderName(String value) { - this.value = new AsciiString(value); + this.value = new ByteString(value, CharsetUtil.UTF_8); } - public AsciiString value() { + public ByteString value() { // Return a slice so that the buffer gets its own reader index. return value; } @@ -75,103 +76,103 @@ public interface Http2Headers extends BinaryHeaders { /** * Indicates whether the given header name is a valid HTTP/2 pseudo header. */ - public static boolean isPseudoHeader(AsciiString header) { + public static boolean isPseudoHeader(ByteString header) { return PSEUDO_HEADERS.contains(header); } } @Override - Http2Headers add(AsciiString name, AsciiString value); + Http2Headers add(ByteString name, ByteString value); @Override - Http2Headers add(AsciiString name, Iterable values); + Http2Headers add(ByteString name, Iterable values); @Override - Http2Headers add(AsciiString name, AsciiString... values); + Http2Headers add(ByteString name, ByteString... values); @Override - Http2Headers addObject(AsciiString name, Object value); + Http2Headers addObject(ByteString name, Object value); @Override - Http2Headers addObject(AsciiString name, Iterable values); + Http2Headers addObject(ByteString name, Iterable values); @Override - Http2Headers addObject(AsciiString name, Object... values); + Http2Headers addObject(ByteString name, Object... values); @Override - Http2Headers addBoolean(AsciiString name, boolean value); + Http2Headers addBoolean(ByteString name, boolean value); @Override - Http2Headers addByte(AsciiString name, byte value); + Http2Headers addByte(ByteString name, byte value); @Override - Http2Headers addChar(AsciiString name, char value); + Http2Headers addChar(ByteString name, char value); @Override - Http2Headers addShort(AsciiString name, short value); + Http2Headers addShort(ByteString name, short value); @Override - Http2Headers addInt(AsciiString name, int value); + Http2Headers addInt(ByteString name, int value); @Override - Http2Headers addLong(AsciiString name, long value); + Http2Headers addLong(ByteString name, long value); @Override - Http2Headers addFloat(AsciiString name, float value); + Http2Headers addFloat(ByteString name, float value); @Override - Http2Headers addDouble(AsciiString name, double value); + Http2Headers addDouble(ByteString name, double value); @Override - Http2Headers addTimeMillis(AsciiString name, long value); + Http2Headers addTimeMillis(ByteString name, long value); @Override Http2Headers add(BinaryHeaders headers); @Override - Http2Headers set(AsciiString name, AsciiString value); + Http2Headers set(ByteString name, ByteString value); @Override - Http2Headers set(AsciiString name, Iterable values); + Http2Headers set(ByteString name, Iterable values); @Override - Http2Headers set(AsciiString name, AsciiString... values); + Http2Headers set(ByteString name, ByteString... values); @Override - Http2Headers setObject(AsciiString name, Object value); + Http2Headers setObject(ByteString name, Object value); @Override - Http2Headers setObject(AsciiString name, Iterable values); + Http2Headers setObject(ByteString name, Iterable values); @Override - Http2Headers setObject(AsciiString name, Object... values); + Http2Headers setObject(ByteString name, Object... values); @Override - Http2Headers setBoolean(AsciiString name, boolean value); + Http2Headers setBoolean(ByteString name, boolean value); @Override - Http2Headers setByte(AsciiString name, byte value); + Http2Headers setByte(ByteString name, byte value); @Override - Http2Headers setChar(AsciiString name, char value); + Http2Headers setChar(ByteString name, char value); @Override - Http2Headers setShort(AsciiString name, short value); + Http2Headers setShort(ByteString name, short value); @Override - Http2Headers setInt(AsciiString name, int value); + Http2Headers setInt(ByteString name, int value); @Override - Http2Headers setLong(AsciiString name, long value); + Http2Headers setLong(ByteString name, long value); @Override - Http2Headers setFloat(AsciiString name, float value); + Http2Headers setFloat(ByteString name, float value); @Override - Http2Headers setDouble(AsciiString name, double value); + Http2Headers setDouble(ByteString name, double value); @Override - Http2Headers setTimeMillis(AsciiString name, long value); + Http2Headers setTimeMillis(ByteString name, long value); @Override Http2Headers set(BinaryHeaders headers); @@ -185,50 +186,50 @@ public interface Http2Headers extends BinaryHeaders { /** * Sets the {@link PseudoHeaderName#METHOD} header or {@code null} if there is no such header */ - Http2Headers method(AsciiString value); + Http2Headers method(ByteString value); /** * Sets the {@link PseudoHeaderName#SCHEME} header if there is no such header */ - Http2Headers scheme(AsciiString value); + Http2Headers scheme(ByteString value); /** * Sets the {@link PseudoHeaderName#AUTHORITY} header or {@code null} if there is no such header */ - Http2Headers authority(AsciiString value); + Http2Headers authority(ByteString value); /** * Sets the {@link PseudoHeaderName#PATH} header or {@code null} if there is no such header */ - Http2Headers path(AsciiString value); + Http2Headers path(ByteString value); /** * Sets the {@link PseudoHeaderName#STATUS} header or {@code null} if there is no such header */ - Http2Headers status(AsciiString value); + Http2Headers status(ByteString value); /** * Gets the {@link PseudoHeaderName#METHOD} header or {@code null} if there is no such header */ - AsciiString method(); + ByteString method(); /** * Gets the {@link PseudoHeaderName#SCHEME} header or {@code null} if there is no such header */ - AsciiString scheme(); + ByteString scheme(); /** * Gets the {@link PseudoHeaderName#AUTHORITY} header or {@code null} if there is no such header */ - AsciiString authority(); + ByteString authority(); /** * Gets the {@link PseudoHeaderName#PATH} header or {@code null} if there is no such header */ - AsciiString path(); + ByteString path(); /** * Gets the {@link PseudoHeaderName#STATUS} header or {@code null} if there is no such header */ - AsciiString status(); + ByteString 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 ee8a2904ad..8f816aa679 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,7 @@ package io.netty.handler.codec.http2; import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.AsciiString; +import io.netty.util.ByteString; /** * Encodes {@link Http2Headers} into HPACK-encoded headers blocks. @@ -47,7 +47,7 @@ public interface Http2HeadersEncoder { * sensitive. * {@code false} otherwise. */ - boolean isSensitive(AsciiString name, AsciiString value); + boolean isSensitive(ByteString name, ByteString value); } /** @@ -64,11 +64,11 @@ public interface Http2HeadersEncoder { Configuration configuration(); /** - * Always return {@code false} for {@link SensitivityDetector#isSensitive(AsciiString, AsciiString)}. + * Always return {@code false} for {@link SensitivityDetector#isSensitive(ByteString, ByteString)}. */ SensitivityDetector NEVER_SENSITIVE = new SensitivityDetector() { @Override - public boolean isSensitive(AsciiString name, AsciiString value) { + public boolean isSensitive(ByteString name, ByteString value) { return false; } }; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.java index 822dce42a5..2ace1cfa25 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerUpgradeCodec.java @@ -23,15 +23,17 @@ import static io.netty.handler.codec.http2.Http2CodecUtil.writeFrameHeader; import static io.netty.handler.codec.http2.Http2FrameTypes.SETTINGS; import static io.netty.util.internal.ObjectUtil.checkNotNull; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.base64.Base64; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpServerUpgradeHandler; +import io.netty.util.ByteString; import io.netty.util.CharsetUtil; +import java.nio.CharBuffer; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -104,8 +106,7 @@ public class Http2ServerUpgradeCodec implements HttpServerUpgradeHandler.Upgrade @Override public void upgradeTo(final ChannelHandlerContext ctx, FullHttpRequest upgradeRequest, FullHttpResponse upgradeResponse) { - // Add the HTTP/2 connection handler to the pipeline immediately following the current - // handler. + // Add the HTTP/2 connection handler to the pipeline immediately following the current handler. ctx.pipeline().addAfter(ctx.name(), handlerName, connectionHandler); } @@ -114,7 +115,7 @@ public class Http2ServerUpgradeCodec implements HttpServerUpgradeHandler.Upgrade */ private Http2Settings decodeSettingsHeader(ChannelHandlerContext ctx, CharSequence settingsHeader) throws Http2Exception { - ByteBuf header = Unpooled.wrappedBuffer(AsciiString.getBytes(settingsHeader, CharsetUtil.UTF_8)); + ByteBuf header = ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(settingsHeader), CharsetUtil.UTF_8); try { // Decode the SETTINGS payload. ByteBuf payload = Base64.decode(header, URL_SAFE); diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpUtil.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpUtil.java index 18f5098db2..f1c3bff65c 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpUtil.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpUtil.java @@ -18,7 +18,6 @@ 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.internal.ObjectUtil.checkNotNull; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.BinaryHeaders; import io.netty.handler.codec.TextHeaders.EntryVisitor; import io.netty.handler.codec.http.DefaultFullHttpRequest; @@ -35,6 +34,8 @@ 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.HttpVersion; +import io.netty.util.AsciiString; +import io.netty.util.ByteString; import java.net.URI; import java.util.HashMap; @@ -168,7 +169,7 @@ public final class HttpUtil { * @return The HTTP/1.x status * @throws Http2Exception If there is a problem translating from HTTP/2 to HTTP/1.x */ - public static HttpResponseStatus parseStatus(AsciiString status) throws Http2Exception { + public static HttpResponseStatus parseStatus(ByteString status) throws Http2Exception { HttpResponseStatus result; try { result = HttpResponseStatus.parseLine(status); @@ -220,11 +221,10 @@ public final class HttpUtil { */ public static FullHttpRequest toHttpRequest(int streamId, Http2Headers http2Headers, boolean validateHttpHeaders) throws Http2Exception { - // HTTP/2 does not define a way to carry the version identifier that is - // included in the HTTP/1.1 request line. - final AsciiString method = checkNotNull(http2Headers.method(), + // HTTP/2 does not define a way to carry the version identifier that is included in the HTTP/1.1 request line. + final ByteString method = checkNotNull(http2Headers.method(), "method header cannot be null in conversion to HTTP/1.x"); - final AsciiString path = checkNotNull(http2Headers.path(), + final ByteString path = checkNotNull(http2Headers.path(), "path header cannot be null in conversion to HTTP/1.x"); FullHttpRequest msg = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(method .toString()), path.toString(), validateHttpHeaders); @@ -330,10 +330,10 @@ public final class HttpUtil { /** * 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 Map + REQUEST_HEADER_TRANSLATIONS = new HashMap(); + private static final Map + RESPONSE_HEADER_TRANSLATIONS = new HashMap(); static { RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.AUTHORITY.value(), ExtensionHeaderNames.AUTHORITY.text()); @@ -346,7 +346,7 @@ public final class HttpUtil { private final int streamId; private final HttpHeaders output; - private final Map translations; + private final Map translations; /** * Create a new instance @@ -362,10 +362,10 @@ public final class HttpUtil { } @Override - public boolean visit(Entry entry) throws Http2Exception { - final AsciiString name = entry.getKey(); - final AsciiString value = entry.getValue(); - AsciiString translatedName = translations.get(name); + public boolean visit(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; @@ -373,11 +373,11 @@ public final class HttpUtil { // http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-8.1.2.3 // All headers that start with ':' are only valid in HTTP/2 context - if (translatedName.isEmpty() || translatedName.charAt(0) == ':') { + if (translatedName.isEmpty() || translatedName.byteAt(0) == ':') { throw streamError(streamId, PROTOCOL_ERROR, "Invalid HTTP/2 header '%s' encountered in translation to HTTP/1.x", translatedName); } else { - output.add(translatedName, value); + output.add(new AsciiString(translatedName.array(), false), new AsciiString(value.array(), false)); } } return true; diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpPriorityAdapter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpPriorityAdapter.java index 6b6ed9570a..d4e4a0a1d3 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpPriorityAdapter.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpPriorityAdapter.java @@ -16,14 +16,15 @@ package io.netty.handler.codec.http2; import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR; import static io.netty.handler.codec.http2.Http2Exception.connectionError; + import java.util.Map.Entry; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.TextHeaders.EntryVisitor; import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.FullHttpMessage; import io.netty.handler.codec.http.HttpHeaders; +import io.netty.util.AsciiString; import io.netty.util.collection.IntObjectHashMap; import io.netty.util.collection.IntObjectMap; import io.netty.util.internal.PlatformDependent; diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java index 8e28fc2e58..43b917e8c3 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java @@ -40,12 +40,12 @@ import io.netty.channel.ChannelPromise; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http2.Http2TestUtil.FrameAdapter; import io.netty.handler.codec.http2.Http2TestUtil.FrameCountDown; import io.netty.handler.codec.http2.Http2TestUtil.Http2Runnable; +import io.netty.util.AsciiString; import io.netty.util.CharsetUtil; import io.netty.util.NetUtil; import io.netty.util.concurrent.Future; 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 6e00b84297..52dd37bb8f 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 @@ -15,30 +15,41 @@ */ package io.netty.handler.codec.http2; -import io.netty.handler.codec.AsciiString; -import org.junit.Test; - +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import io.netty.util.AsciiString; +import io.netty.util.ByteString; +import io.netty.util.CharsetUtil; + +import org.junit.Test; public class DefaultHttp2HeadersTest { - private static final AsciiString NAME = new AsciiString("Test"); - private static final AsciiString VALUE = new AsciiString("some value"); + private static final byte[] NAME_BYTES = { 'T', 'E', 's', 'T' }; + private static final byte[] NAME_BYTES_LOWERCASE = { 't', 'e', 's', 't' }; + private static final ByteString NAME_BYTESTRING = new ByteString(NAME_BYTES); + private static final AsciiString NAME_ASCIISTRING = new AsciiString("Test"); + private static final ByteString VALUE = new ByteString("some value", CharsetUtil.UTF_8); @Test public void defaultLowercase() { - Http2Headers headers = new DefaultHttp2Headers().set(NAME, VALUE); - assertEquals(first(headers), NAME.toLowerCase()); + Http2Headers headers = new DefaultHttp2Headers().set(NAME_BYTESTRING, VALUE); + assertArrayEquals(NAME_BYTES_LOWERCASE, first(headers).toByteArray()); + } + + @Test + public void defaultLowercaseAsciiString() { + Http2Headers headers = new DefaultHttp2Headers().set(NAME_ASCIISTRING, VALUE); + assertEquals(NAME_ASCIISTRING.toLowerCase(), first(headers)); } @Test public void caseInsensitive() { - Http2Headers headers = new DefaultHttp2Headers(false).set(NAME, VALUE); - assertEquals(first(headers), NAME); + Http2Headers headers = new DefaultHttp2Headers(false).set(NAME_BYTESTRING, VALUE); + assertArrayEquals(NAME_BYTES, first(headers).toByteArray()); } - private static AsciiString first(Http2Headers headers) { + private static ByteString first(Http2Headers headers) { return headers.names().iterator().next(); } - } 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 5555cabf4a..7957643f77 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 @@ -17,8 +17,9 @@ package io.netty.handler.codec.http2; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.util.AsciiString; +import io.netty.util.ByteString; import java.util.List; import java.util.Random; @@ -61,8 +62,8 @@ final class Http2TestUtil { /** * Converts a byte array into an {@link AsciiString}. */ - public static AsciiString as(byte[] value) { - return new AsciiString(value); + public static ByteString as(byte[] value) { + return new ByteString(value); } /** @@ -77,7 +78,7 @@ final class Http2TestUtil { /** * Returns an {@link AsciiString} that wraps a randomly-filled byte array. */ - public static AsciiString randomString() { + public static ByteString randomString() { return as(randomBytes()); } diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java index 0da28419aa..f2a19a4f71 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java @@ -40,7 +40,6 @@ import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpMessage; @@ -54,6 +53,7 @@ import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http2.Http2TestUtil.FrameAdapter; import io.netty.handler.codec.http2.Http2TestUtil.Http2Runnable; +import io.netty.util.AsciiString; import io.netty.util.CharsetUtil; import io.netty.util.NetUtil; import io.netty.util.concurrent.Future; 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 5bc5aeccdc..89c93db414 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 @@ -15,8 +15,8 @@ */ package io.netty.handler.codec.stomp; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.TextHeaders; +import io.netty.util.AsciiString; /** * The multimap data structure for the STOMP header names and values. It also provides the constants for the standard diff --git a/codec/src/main/java/io/netty/handler/codec/AsciiHeadersEncoder.java b/codec/src/main/java/io/netty/handler/codec/AsciiHeadersEncoder.java index 805603ffb3..0531809e81 100644 --- a/codec/src/main/java/io/netty/handler/codec/AsciiHeadersEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/AsciiHeadersEncoder.java @@ -20,7 +20,9 @@ package io.netty.handler.codec; import java.util.Map.Entry; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.handler.codec.TextHeaders.EntryVisitor; +import io.netty.util.AsciiString; public final class AsciiHeadersEncoder implements EntryVisitor { @@ -129,7 +131,7 @@ public final class AsciiHeadersEncoder implements EntryVisitor { } private static void writeAsciiString(ByteBuf buf, int offset, AsciiString value, int valueLen) { - value.copy(0, buf, offset, valueLen); + ByteBufUtil.copy(value, 0, buf, offset, valueLen); } private static void writeCharSequence(ByteBuf buf, int offset, CharSequence value, int valueLen) { diff --git a/codec/src/main/java/io/netty/handler/codec/BinaryHeaders.java b/codec/src/main/java/io/netty/handler/codec/BinaryHeaders.java index a998decc0a..b220320da3 100644 --- a/codec/src/main/java/io/netty/handler/codec/BinaryHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/BinaryHeaders.java @@ -16,68 +16,70 @@ package io.netty.handler.codec; +import io.netty.util.ByteString; + /** - * A typical {@code AsciiString} multimap used by protocols that use binary headers (such as HTTP/2) for the - * representation of arbitrary key-value data. {@link AsciiString} is just a wrapper around a byte array but provides + * A typical {@code ByteString} multimap used by protocols that use binary headers (such as HTTP/2) for the + * representation of arbitrary key-value data. {@link ByteString} is just a wrapper around a byte array but provides * some additional utility when handling text data. */ -public interface BinaryHeaders extends Headers { +public interface BinaryHeaders extends Headers { /** - * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}. + * Provides an abstraction to iterate over elements maintained in the {@link Headers} collection. */ - interface EntryVisitor extends Headers.EntryVisitor { + interface EntryVisitor extends Headers.EntryVisitor { } /** - * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}. + * Provides an abstraction to iterate over elements maintained in the {@link Headers} collection. */ - interface NameVisitor extends Headers.NameVisitor { + interface NameVisitor extends Headers.NameVisitor { } @Override - BinaryHeaders add(AsciiString name, AsciiString value); + BinaryHeaders add(ByteString name, ByteString value); @Override - BinaryHeaders add(AsciiString name, Iterable values); + BinaryHeaders add(ByteString name, Iterable values); @Override - BinaryHeaders add(AsciiString name, AsciiString... values); + BinaryHeaders add(ByteString name, ByteString... values); @Override - BinaryHeaders addObject(AsciiString name, Object value); + BinaryHeaders addObject(ByteString name, Object value); @Override - BinaryHeaders addObject(AsciiString name, Iterable values); + BinaryHeaders addObject(ByteString name, Iterable values); @Override - BinaryHeaders addObject(AsciiString name, Object... values); + BinaryHeaders addObject(ByteString name, Object... values); @Override - BinaryHeaders addBoolean(AsciiString name, boolean value); + BinaryHeaders addBoolean(ByteString name, boolean value); @Override - BinaryHeaders addByte(AsciiString name, byte value); + BinaryHeaders addByte(ByteString name, byte value); @Override - BinaryHeaders addChar(AsciiString name, char value); + BinaryHeaders addChar(ByteString name, char value); @Override - BinaryHeaders addShort(AsciiString name, short value); + BinaryHeaders addShort(ByteString name, short value); @Override - BinaryHeaders addInt(AsciiString name, int value); + BinaryHeaders addInt(ByteString name, int value); @Override - BinaryHeaders addLong(AsciiString name, long value); + BinaryHeaders addLong(ByteString name, long value); @Override - BinaryHeaders addFloat(AsciiString name, float value); + BinaryHeaders addFloat(ByteString name, float value); @Override - BinaryHeaders addDouble(AsciiString name, double value); + BinaryHeaders addDouble(ByteString name, double value); @Override - BinaryHeaders addTimeMillis(AsciiString name, long value); + BinaryHeaders addTimeMillis(ByteString name, long value); /** * See {@link Headers#add(Headers)} @@ -85,49 +87,49 @@ public interface BinaryHeaders extends Headers { BinaryHeaders add(BinaryHeaders headers); @Override - BinaryHeaders set(AsciiString name, AsciiString value); + BinaryHeaders set(ByteString name, ByteString value); @Override - BinaryHeaders set(AsciiString name, Iterable values); + BinaryHeaders set(ByteString name, Iterable values); @Override - BinaryHeaders set(AsciiString name, AsciiString... values); + BinaryHeaders set(ByteString name, ByteString... values); @Override - BinaryHeaders setObject(AsciiString name, Object value); + BinaryHeaders setObject(ByteString name, Object value); @Override - BinaryHeaders setObject(AsciiString name, Iterable values); + BinaryHeaders setObject(ByteString name, Iterable values); @Override - BinaryHeaders setObject(AsciiString name, Object... values); + BinaryHeaders setObject(ByteString name, Object... values); @Override - BinaryHeaders setBoolean(AsciiString name, boolean value); + BinaryHeaders setBoolean(ByteString name, boolean value); @Override - BinaryHeaders setByte(AsciiString name, byte value); + BinaryHeaders setByte(ByteString name, byte value); @Override - BinaryHeaders setChar(AsciiString name, char value); + BinaryHeaders setChar(ByteString name, char value); @Override - BinaryHeaders setShort(AsciiString name, short value); + BinaryHeaders setShort(ByteString name, short value); @Override - BinaryHeaders setInt(AsciiString name, int value); + BinaryHeaders setInt(ByteString name, int value); @Override - BinaryHeaders setLong(AsciiString name, long value); + BinaryHeaders setLong(ByteString name, long value); @Override - BinaryHeaders setFloat(AsciiString name, float value); + BinaryHeaders setFloat(ByteString name, float value); @Override - BinaryHeaders setDouble(AsciiString name, double value); + BinaryHeaders setDouble(ByteString name, double value); @Override - BinaryHeaders setTimeMillis(AsciiString name, long value); + BinaryHeaders setTimeMillis(ByteString name, long value); /** * See {@link Headers#set(Headers)} diff --git a/codec/src/main/java/io/netty/handler/codec/DefaultBinaryHeaders.java b/codec/src/main/java/io/netty/handler/codec/DefaultBinaryHeaders.java index 3c484603ed..ae1b32b507 100644 --- a/codec/src/main/java/io/netty/handler/codec/DefaultBinaryHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/DefaultBinaryHeaders.java @@ -14,80 +14,74 @@ */ package io.netty.handler.codec; +import io.netty.util.ByteString; +import io.netty.util.CharsetUtil; import io.netty.util.internal.PlatformDependent; +import java.nio.charset.Charset; import java.text.ParseException; -import static io.netty.handler.codec.AsciiString.*; - -public class DefaultBinaryHeaders extends DefaultHeaders implements BinaryHeaders { - private static final HashCodeGenerator ASCII_HASH_CODE_GENERATOR = - new HashCodeGenerator() { +public class DefaultBinaryHeaders extends DefaultHeaders implements BinaryHeaders { + private static final ValueConverter OBJECT_TO_BYTE = new ValueConverter() { + private final Charset DEFAULT_CHARSET = CharsetUtil.UTF_8; @Override - public int generateHashCode(AsciiString name) { - return AsciiString.caseInsensitiveHashCode(name); - } - }; - - private static final ValueConverter OBJECT_TO_ASCII = new ValueConverter() { - @Override - public AsciiString convertObject(Object value) { - if (value instanceof AsciiString) { - return (AsciiString) value; + public ByteString convertObject(Object value) { + if (value instanceof ByteString) { + return (ByteString) value; } if (value instanceof CharSequence) { - return new AsciiString((CharSequence) value); + return new ByteString((CharSequence) value, DEFAULT_CHARSET); } - return new AsciiString(value.toString()); + return new ByteString(value.toString(), DEFAULT_CHARSET); } @Override - public AsciiString convertInt(int value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertInt(int value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public AsciiString convertLong(long value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertLong(long value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public AsciiString convertDouble(double value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertDouble(double value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public AsciiString convertChar(char value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertChar(char value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public AsciiString convertBoolean(boolean value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertBoolean(boolean value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public AsciiString convertFloat(float value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertFloat(float value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public int convertToInt(AsciiString value) { - return value.parseInt(); + public int convertToInt(ByteString value) { + return value.parseAsciiInt(); } @Override - public long convertToLong(AsciiString value) { - return value.parseLong(); + public long convertToLong(ByteString value) { + return value.parseAsciiLong(); } @Override - public AsciiString convertTimeMillis(long value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertTimeMillis(long value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public long convertToTimeMillis(AsciiString value) { + public long convertToTimeMillis(ByteString value) { try { return HeaderDateFormat.get().parse(value.toString()); } catch (ParseException e) { @@ -97,155 +91,145 @@ public class DefaultBinaryHeaders extends DefaultHeaders implements } @Override - public double convertToDouble(AsciiString value) { - return value.parseDouble(); + public double convertToDouble(ByteString value) { + return value.parseAsciiDouble(); } @Override - public char convertToChar(AsciiString value) { - return value.charAt(0); + public char convertToChar(ByteString value) { + return value.parseChar(); } @Override - public boolean convertToBoolean(AsciiString value) { + public boolean convertToBoolean(ByteString value) { return value.byteAt(0) != 0; } @Override - public float convertToFloat(AsciiString value) { - return value.parseFloat(); + public float convertToFloat(ByteString value) { + return value.parseAsciiFloat(); } @Override - public AsciiString convertShort(short value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertShort(short value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public short convertToShort(AsciiString value) { - return value.parseShort(); + public short convertToShort(ByteString value) { + return value.parseAsciiShort(); } @Override - public AsciiString convertByte(byte value) { - return new AsciiString(String.valueOf(value)); + public ByteString convertByte(byte value) { + return new ByteString(String.valueOf(value), DEFAULT_CHARSET); } @Override - public byte convertToByte(AsciiString value) { + public byte convertToByte(ByteString value) { return value.byteAt(0); } }; - private static final NameConverter ASCII_TO_LOWER_CONVERTER = new NameConverter() { - @Override - public AsciiString convertName(AsciiString name) { - return name.toLowerCase(); - } - }; - - private static final NameConverter ASCII_IDENTITY_CONVERTER = new NameConverter() { - @Override - public AsciiString convertName(AsciiString name) { - return name; - } - }; + private static final HashCodeGenerator JAVA_HASH_CODE_GENERATOR = + new JavaHashCodeGenerator(); + protected static final NameConverter IDENTITY_NAME_CONVERTER = new IdentityNameConverter(); public DefaultBinaryHeaders() { - this(false); + this(IDENTITY_NAME_CONVERTER); } - public DefaultBinaryHeaders(boolean forceKeyToLower) { - super(CASE_INSENSITIVE_ORDER, CASE_INSENSITIVE_ORDER, ASCII_HASH_CODE_GENERATOR, OBJECT_TO_ASCII, - forceKeyToLower ? ASCII_TO_LOWER_CONVERTER : ASCII_IDENTITY_CONVERTER); + public DefaultBinaryHeaders(NameConverter nameConverter) { + super(ByteString.DEFAULT_COMPARATOR, ByteString.DEFAULT_COMPARATOR, + JAVA_HASH_CODE_GENERATOR, OBJECT_TO_BYTE, nameConverter); } @Override - public BinaryHeaders add(AsciiString name, AsciiString value) { + public BinaryHeaders add(ByteString name, ByteString value) { super.add(name, value); return this; } @Override - public BinaryHeaders add(AsciiString name, Iterable values) { + public BinaryHeaders add(ByteString name, Iterable values) { super.add(name, values); return this; } @Override - public BinaryHeaders add(AsciiString name, AsciiString... values) { + public BinaryHeaders add(ByteString name, ByteString... values) { super.add(name, values); return this; } @Override - public BinaryHeaders addObject(AsciiString name, Object value) { + public BinaryHeaders addObject(ByteString name, Object value) { super.addObject(name, value); return this; } @Override - public BinaryHeaders addObject(AsciiString name, Iterable values) { + public BinaryHeaders addObject(ByteString name, Iterable values) { super.addObject(name, values); return this; } @Override - public BinaryHeaders addObject(AsciiString name, Object... values) { + public BinaryHeaders addObject(ByteString name, Object... values) { super.addObject(name, values); return this; } @Override - public BinaryHeaders addBoolean(AsciiString name, boolean value) { + public BinaryHeaders addBoolean(ByteString name, boolean value) { super.addBoolean(name, value); return this; } @Override - public BinaryHeaders addChar(AsciiString name, char value) { + public BinaryHeaders addChar(ByteString name, char value) { super.addChar(name, value); return this; } @Override - public BinaryHeaders addByte(AsciiString name, byte value) { + public BinaryHeaders addByte(ByteString name, byte value) { super.addByte(name, value); return this; } @Override - public BinaryHeaders addShort(AsciiString name, short value) { + public BinaryHeaders addShort(ByteString name, short value) { super.addShort(name, value); return this; } @Override - public BinaryHeaders addInt(AsciiString name, int value) { + public BinaryHeaders addInt(ByteString name, int value) { super.addInt(name, value); return this; } @Override - public BinaryHeaders addLong(AsciiString name, long value) { + public BinaryHeaders addLong(ByteString name, long value) { super.addLong(name, value); return this; } @Override - public BinaryHeaders addFloat(AsciiString name, float value) { + public BinaryHeaders addFloat(ByteString name, float value) { super.addFloat(name, value); return this; } @Override - public BinaryHeaders addDouble(AsciiString name, double value) { + public BinaryHeaders addDouble(ByteString name, double value) { super.addDouble(name, value); return this; } @Override - public BinaryHeaders addTimeMillis(AsciiString name, long value) { + public BinaryHeaders addTimeMillis(ByteString name, long value) { super.addTimeMillis(name, value); return this; } @@ -257,91 +241,91 @@ public class DefaultBinaryHeaders extends DefaultHeaders implements } @Override - public BinaryHeaders set(AsciiString name, AsciiString value) { + public BinaryHeaders set(ByteString name, ByteString value) { super.set(name, value); return this; } @Override - public BinaryHeaders set(AsciiString name, Iterable values) { + public BinaryHeaders set(ByteString name, Iterable values) { super.set(name, values); return this; } @Override - public BinaryHeaders set(AsciiString name, AsciiString... values) { + public BinaryHeaders set(ByteString name, ByteString... values) { super.set(name, values); return this; } @Override - public BinaryHeaders setObject(AsciiString name, Object value) { + public BinaryHeaders setObject(ByteString name, Object value) { super.setObject(name, value); return this; } @Override - public BinaryHeaders setObject(AsciiString name, Iterable values) { + public BinaryHeaders setObject(ByteString name, Iterable values) { super.setObject(name, values); return this; } @Override - public BinaryHeaders setObject(AsciiString name, Object... values) { + public BinaryHeaders setObject(ByteString name, Object... values) { super.setObject(name, values); return this; } @Override - public BinaryHeaders setBoolean(AsciiString name, boolean value) { + public BinaryHeaders setBoolean(ByteString name, boolean value) { super.setBoolean(name, value); return this; } @Override - public BinaryHeaders setChar(AsciiString name, char value) { + public BinaryHeaders setChar(ByteString name, char value) { super.setChar(name, value); return this; } @Override - public BinaryHeaders setByte(AsciiString name, byte value) { + public BinaryHeaders setByte(ByteString name, byte value) { super.setByte(name, value); return this; } @Override - public BinaryHeaders setShort(AsciiString name, short value) { + public BinaryHeaders setShort(ByteString name, short value) { super.setShort(name, value); return this; } @Override - public BinaryHeaders setInt(AsciiString name, int value) { + public BinaryHeaders setInt(ByteString name, int value) { super.setInt(name, value); return this; } @Override - public BinaryHeaders setLong(AsciiString name, long value) { + public BinaryHeaders setLong(ByteString name, long value) { super.setLong(name, value); return this; } @Override - public BinaryHeaders setFloat(AsciiString name, float value) { + public BinaryHeaders setFloat(ByteString name, float value) { super.setFloat(name, value); return this; } @Override - public BinaryHeaders setDouble(AsciiString name, double value) { + public BinaryHeaders setDouble(ByteString name, double value) { super.setDouble(name, value); return this; } @Override - public BinaryHeaders setTimeMillis(AsciiString name, long value) { + public BinaryHeaders setTimeMillis(ByteString name, long value) { super.setTimeMillis(name, value); return this; } 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 60616415a8..37b7ba45a8 100644 --- a/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java @@ -66,6 +66,16 @@ public class DefaultHeaders implements Headers { T convertName(T name); } + /** + * Uses the {@link #hashCode()} method to generate the hash code. + */ + public static final class JavaHashCodeGenerator implements HashCodeGenerator { + @Override + public int generateHashCode(T name) { + return name.hashCode(); + } + } + /** * A name converted which does not covert but instead just returns this {@code name} unchanged */ diff --git a/codec/src/main/java/io/netty/handler/codec/DefaultTextHeaders.java b/codec/src/main/java/io/netty/handler/codec/DefaultTextHeaders.java index 28badbc647..2f1d75ed39 100644 --- a/codec/src/main/java/io/netty/handler/codec/DefaultTextHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/DefaultTextHeaders.java @@ -16,6 +16,10 @@ package io.netty.handler.codec; +import static io.netty.util.AsciiString.CHARSEQUENCE_CASE_INSENSITIVE_ORDER; +import static io.netty.util.AsciiString.CHARSEQUENCE_CASE_SENSITIVE_ORDER; +import static io.netty.util.internal.StringUtil.COMMA; +import io.netty.util.AsciiString; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; @@ -23,9 +27,6 @@ import java.text.ParseException; import java.util.Comparator; import java.util.Iterator; -import static io.netty.handler.codec.AsciiString.*; -import static io.netty.util.internal.StringUtil.COMMA; - public class DefaultTextHeaders extends DefaultConvertibleHeaders implements TextHeaders { private static final HashCodeGenerator CHARSEQUECE_CASE_INSENSITIVE_HASH_CODE_GENERATOR = new HashCodeGenerator() { @@ -36,12 +37,7 @@ public class DefaultTextHeaders extends DefaultConvertibleHeaders CHARSEQUECE_CASE_SENSITIVE_HASH_CODE_GENERATOR = - new HashCodeGenerator() { - @Override - public int generateHashCode(CharSequence name) { - return name.hashCode(); - } - }; + new JavaHashCodeGenerator(); public static class DefaultTextValueTypeConverter implements ValueConverter { @Override diff --git a/codec/src/main/java/io/netty/handler/codec/EmptyBinaryHeaders.java b/codec/src/main/java/io/netty/handler/codec/EmptyBinaryHeaders.java index b3e048e58f..98f248b2ac 100644 --- a/codec/src/main/java/io/netty/handler/codec/EmptyBinaryHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/EmptyBinaryHeaders.java @@ -16,96 +16,98 @@ package io.netty.handler.codec; -public class EmptyBinaryHeaders extends EmptyHeaders implements BinaryHeaders { +import io.netty.util.ByteString; + +public class EmptyBinaryHeaders extends EmptyHeaders implements BinaryHeaders { protected EmptyBinaryHeaders() { } @Override - public BinaryHeaders add(AsciiString name, AsciiString value) { + public BinaryHeaders add(ByteString name, ByteString value) { super.add(name, value); return this; } @Override - public BinaryHeaders add(AsciiString name, Iterable values) { + public BinaryHeaders add(ByteString name, Iterable values) { super.add(name, values); return this; } @Override - public BinaryHeaders add(AsciiString name, AsciiString... values) { + public BinaryHeaders add(ByteString name, ByteString... values) { super.add(name, values); return this; } @Override - public BinaryHeaders addObject(AsciiString name, Object value) { + public BinaryHeaders addObject(ByteString name, Object value) { super.addObject(name, value); return this; } @Override - public BinaryHeaders addObject(AsciiString name, Iterable values) { + public BinaryHeaders addObject(ByteString name, Iterable values) { super.addObject(name, values); return this; } @Override - public BinaryHeaders addObject(AsciiString name, Object... values) { + public BinaryHeaders addObject(ByteString name, Object... values) { super.addObject(name, values); return this; } @Override - public BinaryHeaders addBoolean(AsciiString name, boolean value) { + public BinaryHeaders addBoolean(ByteString name, boolean value) { super.addBoolean(name, value); return this; } @Override - public BinaryHeaders addChar(AsciiString name, char value) { + public BinaryHeaders addChar(ByteString name, char value) { super.addChar(name, value); return this; } @Override - public BinaryHeaders addByte(AsciiString name, byte value) { + public BinaryHeaders addByte(ByteString name, byte value) { super.addByte(name, value); return this; } @Override - public BinaryHeaders addShort(AsciiString name, short value) { + public BinaryHeaders addShort(ByteString name, short value) { super.addShort(name, value); return this; } @Override - public BinaryHeaders addInt(AsciiString name, int value) { + public BinaryHeaders addInt(ByteString name, int value) { super.addInt(name, value); return this; } @Override - public BinaryHeaders addLong(AsciiString name, long value) { + public BinaryHeaders addLong(ByteString name, long value) { super.addLong(name, value); return this; } @Override - public BinaryHeaders addFloat(AsciiString name, float value) { + public BinaryHeaders addFloat(ByteString name, float value) { super.addFloat(name, value); return this; } @Override - public BinaryHeaders addDouble(AsciiString name, double value) { + public BinaryHeaders addDouble(ByteString name, double value) { super.addDouble(name, value); return this; } @Override - public BinaryHeaders addTimeMillis(AsciiString name, long value) { + public BinaryHeaders addTimeMillis(ByteString name, long value) { super.addTimeMillis(name, value); return this; } @@ -117,91 +119,91 @@ public class EmptyBinaryHeaders extends EmptyHeaders implements Bin } @Override - public BinaryHeaders set(AsciiString name, AsciiString value) { + public BinaryHeaders set(ByteString name, ByteString value) { super.set(name, value); return this; } @Override - public BinaryHeaders set(AsciiString name, Iterable values) { + public BinaryHeaders set(ByteString name, Iterable values) { super.set(name, values); return this; } @Override - public BinaryHeaders set(AsciiString name, AsciiString... values) { + public BinaryHeaders set(ByteString name, ByteString... values) { super.set(name, values); return this; } @Override - public BinaryHeaders setObject(AsciiString name, Object value) { + public BinaryHeaders setObject(ByteString name, Object value) { super.setObject(name, value); return this; } @Override - public BinaryHeaders setObject(AsciiString name, Iterable values) { + public BinaryHeaders setObject(ByteString name, Iterable values) { super.setObject(name, values); return this; } @Override - public BinaryHeaders setObject(AsciiString name, Object... values) { + public BinaryHeaders setObject(ByteString name, Object... values) { super.setObject(name, values); return this; } @Override - public BinaryHeaders setBoolean(AsciiString name, boolean value) { + public BinaryHeaders setBoolean(ByteString name, boolean value) { super.setBoolean(name, value); return this; } @Override - public BinaryHeaders setChar(AsciiString name, char value) { + public BinaryHeaders setChar(ByteString name, char value) { super.setChar(name, value); return this; } @Override - public BinaryHeaders setByte(AsciiString name, byte value) { + public BinaryHeaders setByte(ByteString name, byte value) { super.setByte(name, value); return this; } @Override - public BinaryHeaders setShort(AsciiString name, short value) { + public BinaryHeaders setShort(ByteString name, short value) { super.setShort(name, value); return this; } @Override - public BinaryHeaders setInt(AsciiString name, int value) { + public BinaryHeaders setInt(ByteString name, int value) { super.setInt(name, value); return this; } @Override - public BinaryHeaders setLong(AsciiString name, long value) { + public BinaryHeaders setLong(ByteString name, long value) { super.setLong(name, value); return this; } @Override - public BinaryHeaders setFloat(AsciiString name, float value) { + public BinaryHeaders setFloat(ByteString name, float value) { super.setFloat(name, value); return this; } @Override - public BinaryHeaders setDouble(AsciiString name, double value) { + public BinaryHeaders setDouble(ByteString name, double value) { super.setDouble(name, value); return this; } @Override - public BinaryHeaders setTimeMillis(AsciiString name, long value) { + public BinaryHeaders setTimeMillis(ByteString name, long value) { super.setTimeMillis(name, value); return 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 643d1b13e8..9718c0c0e2 100644 --- a/codec/src/main/java/io/netty/handler/codec/Headers.java +++ b/codec/src/main/java/io/netty/handler/codec/Headers.java @@ -23,7 +23,7 @@ import java.util.Set; public interface Headers extends Iterable> { /** - * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}. + * Provides an abstraction to iterate over elements maintained in the {@link Headers} collection. */ interface EntryVisitor { /** @@ -36,7 +36,7 @@ public interface Headers extends Iterable> { } /** - * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}. + * Provides an abstraction to iterate over elements maintained in the {@link Headers} collection. */ interface NameVisitor { /** @@ -1095,16 +1095,14 @@ public interface Headers extends Iterable> { Iterator> iterator(); /** - * Provide a means of iterating over elements in this map with low GC - * + * Provides an abstraction to iterate over elements maintained in the {@link Headers} collection. * @param visitor The visitor which will visit each element in this map * @return The last entry before iteration stopped or {@code null} if iteration went past the end */ Map.Entry forEachEntry(EntryVisitor visitor) throws Exception; /** - * Provide a means of iterating over elements in this map with low GC - * + * Provides an abstraction to iterate over elements maintained in the {@link Headers} collection. * @param visitor The visitor which will visit each element in this map * @return The last key before iteration stopped or {@code null} if iteration went past the end */ diff --git a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderByteBuf.java b/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderByteBuf.java index 6d660dba03..24d06dc1ef 100644 --- a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderByteBuf.java +++ b/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderByteBuf.java @@ -17,9 +17,9 @@ package io.netty.handler.codec; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.ByteBufProcessor; import io.netty.buffer.SwappedByteBuf; import io.netty.buffer.Unpooled; +import io.netty.util.ByteProcessor; import io.netty.util.Signal; import io.netty.util.internal.StringUtil; @@ -352,7 +352,7 @@ final class ReplayingDecoderByteBuf extends ByteBuf { } @Override - public int forEachByte(ByteBufProcessor processor) { + public int forEachByte(ByteProcessor processor) { int ret = buffer.forEachByte(processor); if (ret < 0) { throw REPLAY; @@ -362,7 +362,7 @@ final class ReplayingDecoderByteBuf extends ByteBuf { } @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { + public int forEachByte(int index, int length, ByteProcessor processor) { final int writerIndex = buffer.writerIndex(); if (index >= writerIndex) { throw REPLAY; @@ -381,7 +381,7 @@ final class ReplayingDecoderByteBuf extends ByteBuf { } @Override - public int forEachByteDesc(ByteBufProcessor processor) { + public int forEachByteDesc(ByteProcessor processor) { if (terminated) { return buffer.forEachByteDesc(processor); } else { @@ -391,7 +391,7 @@ final class ReplayingDecoderByteBuf extends ByteBuf { } @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + public int forEachByteDesc(int index, int length, ByteProcessor processor) { if (index + length > buffer.writerIndex()) { throw REPLAY; } diff --git a/codec/src/test/java/io/netty/handler/codec/DefaultBinaryHeadersTest.java b/codec/src/test/java/io/netty/handler/codec/DefaultBinaryHeadersTest.java index 6d477e07bc..c4bfb43b95 100644 --- a/codec/src/test/java/io/netty/handler/codec/DefaultBinaryHeadersTest.java +++ b/codec/src/test/java/io/netty/handler/codec/DefaultBinaryHeadersTest.java @@ -17,6 +17,8 @@ package io.netty.handler.codec; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import io.netty.util.AsciiString; +import io.netty.util.ByteString; import java.util.HashSet; import java.util.Iterator; @@ -40,11 +42,11 @@ public class DefaultBinaryHeadersTest { byte[] key2 = randomBytes(); byte[] value2 = randomBytes(); - DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); h1.set(as(key1), as(value1)); h1.set(as(key2), as(value2)); - DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(); h2.set(as(key1), as(value1)); h2.set(as(key2), as(value2)); @@ -63,13 +65,13 @@ public class DefaultBinaryHeadersTest { byte[] v3 = randomBytes(); byte[] v4 = randomBytes(); - DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); h1.set(as(k1), as(v1)); h1.set(as(k2), as(v2)); h1.add(as(k2), as(v3)); h1.add(as(k1), as(v4)); - DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(); h2.set(as(k1), as(v1)); h2.set(as(k2), as(v2)); h2.add(as(k1), as(v4)); @@ -90,13 +92,13 @@ public class DefaultBinaryHeadersTest { byte[] v3 = randomBytes(); byte[] v4 = randomBytes(); - DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); h1.set(as(k1), as(v1)); h1.set(as(k2), as(v2)); h1.add(as(k2), as(v3)); h1.add(as(k1), as(v4)); - DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(); h2.set(as(k1), as(v1)); h2.set(as(k2), as(v2)); h2.add(as(k1), as(v4)); @@ -116,18 +118,18 @@ public class DefaultBinaryHeadersTest { byte[] v3 = randomBytes(); byte[] v4 = randomBytes(); - DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); h1.set(as(k1), as(v1)); h1.set(as(k2), as(v2)); h1.add(as(k2), as(v3)); h1.add(as(k1), as(v4)); - DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(); h2.set(as(k1), as(v1)); h2.set(as(k2), as(v2)); h2.add(as(k1), as(v4)); - DefaultBinaryHeaders expected = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders expected = new DefaultBinaryHeaders(); expected.set(as(k1), as(v1)); expected.set(as(k2), as(v2)); expected.add(as(k2), as(v3)); @@ -148,14 +150,14 @@ public class DefaultBinaryHeadersTest { byte[] v2 = randomBytes(); byte[] v3 = randomBytes(); - DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); h1.add(as(k1), as(v1)); h1.add(as(k1), as(v2)); assertEquals(2, h1.size()); h1.set(as(k1), as(v3)); assertEquals(1, h1.size()); - List list = h1.getAll(as(k1)); + List list = h1.getAll(as(k1)); assertEquals(1, list.size()); assertEquals(as(v3), list.get(0)); } @@ -251,14 +253,14 @@ public class DefaultBinaryHeadersTest { h1.set(as("foo"), as("goo3")); assertEquals(1, h1.size()); - List list = h1.getAll(as("foo")); + List list = h1.getAll(as("foo")); assertEquals(1, list.size()); assertEquals(as("goo3"), list.get(0)); } @Test(expected = NoSuchElementException.class) public void iterateEmptyHeadersShouldThrow() { - Iterator> iterator = new DefaultBinaryHeaders().iterator(); + Iterator> iterator = new DefaultBinaryHeaders().iterator(); assertFalse(iterator.hasNext()); iterator.next(); } @@ -281,7 +283,7 @@ public class DefaultBinaryHeadersTest { } // Now iterate through the headers, removing them from the original set. - for (Map.Entry entry : h1) { + for (Map.Entry entry : h1) { assertTrue(headers.remove(entry.getKey().toString() + ':' + entry.getValue().toString())); } @@ -296,7 +298,7 @@ public class DefaultBinaryHeadersTest { h1.add(as("foo"), as("goo2")); assertEquals(as("goo"), h1.getAndRemove(as("foo"))); assertEquals(0, h1.size()); - List values = h1.getAll(as("foo")); + List values = h1.getAll(as("foo")); assertEquals(0, values.size()); } diff --git a/codec/src/main/java/io/netty/handler/codec/AsciiString.java b/common/src/main/java/io/netty/util/AsciiString.java similarity index 64% rename from codec/src/main/java/io/netty/handler/codec/AsciiString.java rename to common/src/main/java/io/netty/util/AsciiString.java index 7568c5e8c8..2e3d3d6918 100644 --- a/codec/src/main/java/io/netty/handler/codec/AsciiString.java +++ b/common/src/main/java/io/netty/util/AsciiString.java @@ -13,11 +13,13 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.handler.codec; +package io.netty.util; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; +import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty.util.internal.StringUtil.UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET; +import io.netty.util.ByteProcessor.IndexOfProcessor; import io.netty.util.internal.EmptyArrays; +import io.netty.util.internal.PlatformDependent; import java.nio.ByteBuffer; import java.nio.charset.Charset; @@ -32,11 +34,13 @@ import java.util.regex.PatternSyntaxException; * 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 ByteBuf}. It is often used in conjunction with {@link TextHeaders}. + * {@link ByteBuffer}. It is often used in conjunction with {@link TextHeaders}. */ -public final class AsciiString implements CharSequence, Comparable { +public final class AsciiString extends ByteString implements CharSequence, Comparable { + private static final char MAX_CHAR_VALUE = 255; public static final AsciiString EMPTY_STRING = new AsciiString(""); + public static final Comparator CASE_INSENSITIVE_ORDER = new Comparator() { @Override public int compare(AsciiString o1, AsciiString o2) { @@ -73,8 +77,8 @@ public final class AsciiString implements CharSequence, Comparable if (v1 == v2) { continue; } - int c1 = toLowerCase(v1) & 0xFF; - int c2 = toLowerCase(v2) & 0xFF; + int c1 = toLowerCase(v1); + int c2 = toLowerCase(v2); result = c1 - c2; if (result != 0) { return result; @@ -83,7 +87,7 @@ public final class AsciiString implements CharSequence, Comparable } else if (a1 != null) { byte[] thisValue = a1.value; for (int i = 0; i < minLength; i++) { - int c1 = toLowerCase(thisValue[i]) & 0xFF; + int c1 = toLowerCase(thisValue[i]); int c2 = toLowerCase(o2.charAt(i)); result = c1 - c2; if (result != 0) { @@ -94,7 +98,7 @@ public final class AsciiString implements CharSequence, Comparable byte[] thatValue = a2.value; for (int i = 0; i < minLength; i++) { int c1 = toLowerCase(o1.charAt(i)); - int c2 = toLowerCase(thatValue[i]) & 0xFF; + int c2 = toLowerCase(thatValue[i]); result = c1 - c2; if (result != 0) { return result; @@ -188,7 +192,7 @@ public final class AsciiString implements CharSequence, Comparable int hash = 0; final int end = value.length(); for (int i = 0; i < end; i++) { - hash = hash * 31 ^ value.charAt(i) & 31; + hash = hash * HASH_CODE_PRIME ^ value.charAt(i) & HASH_CODE_PRIME; } return hash; @@ -245,27 +249,7 @@ public final class AsciiString implements CharSequence, Comparable return a.equals(b); } - public static byte[] getBytes(CharSequence v, Charset charset) { - if (v instanceof AsciiString) { - return ((AsciiString) v).array(); - } else if (v instanceof String) { - return ((String) v).getBytes(charset); - } else if (v != null) { - final ByteBuf buf = Unpooled.copiedBuffer(v, charset); - try { - if (buf.hasArray()) { - return buf.array(); - } else { - byte[] result = new byte[buf.readableBytes()]; - buf.readBytes(result); - return result; - } - } finally { - buf.release(); - } - } - return null; - } + private String string; /** * Returns an {@link AsciiString} containing the given character sequence. If the given string is already a @@ -275,259 +259,113 @@ public final class AsciiString implements CharSequence, Comparable return string instanceof AsciiString ? (AsciiString) string : new AsciiString(string); } - private final byte[] value; - private String string; - private int hash; - public AsciiString(byte[] value) { - this(value, true); + super(value); } public AsciiString(byte[] value, boolean copy) { - checkNull(value); - if (copy) { - this.value = value.clone(); - } else { - this.value = value; - } + super(value, copy); } public AsciiString(byte[] value, int start, int length) { - this(value, start, length, true); + super(value, start, length); } public AsciiString(byte[] value, int start, int length, boolean copy) { - checkNull(value); - if (start < 0 || start > value.length - length) { - throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length - + ") <= " + "value.length(" + value.length + ')'); - } + super(value, start, length, copy); + } - if (copy || start != 0 || length != value.length) { - this.value = Arrays.copyOfRange(value, start, start + length); - } else { - this.value = value; - } + public AsciiString(ByteBuffer value) { + super(value); + } + + public AsciiString(ByteBuffer value, int start, int length) { + super(value, start, length); + } + + public AsciiString(ByteBuffer value, int start, int length, boolean copy) { + super(value, start, length, copy); } public AsciiString(char[] value) { - this(checkNull(value), 0, value.length); + this(checkNotNull(value, "value"), 0, value.length); } public AsciiString(char[] value, int start, int length) { - checkNull(value); - if (start < 0 || start > value.length - length) { + super(length); + if (start < 0 || start > checkNotNull(value, "value").length - 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]); } } public AsciiString(CharSequence value) { - this(checkNull(value), 0, value.length()); + this(checkNotNull(value, "value"), 0, value.length()); } public AsciiString(CharSequence value, int start, int length) { - if (value == null) { - throw new NullPointerException("value"); - } - - if (start < 0 || length < 0 || length > value.length() - start) { + super(length); + if (start < 0 || length < 0 || length > checkNotNull(value, "value").length() - start) { throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + ") <= " + "value.length(" + value.length() + ')'); } - this.value = new byte[length]; for (int i = 0; i < length; i++) { this.value[i] = c2b(value.charAt(start + i)); } } - public AsciiString(ByteBuffer value) { - this(checkNull(value), value.position(), value.remaining()); - } - - public AsciiString(ByteBuffer value, int start, int length) { - if (value == null) { - throw new NullPointerException("value"); - } - - if (start < 0 || length > value.capacity() - start) { - throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length - + ") <= " + "value.capacity(" + value.capacity() + ')'); - } - - if (value.hasArray()) { - int baseOffset = value.arrayOffset() + start; - this.value = Arrays.copyOfRange(value.array(), baseOffset, baseOffset + length); - } else { - this.value = new byte[length]; - int oldPos = value.position(); - value.get(this.value, 0, this.value.length); - value.position(oldPos); - } - } - - private static T checkNull(T value) { - if (value == null) { - throw new NullPointerException("value"); - } - return value; - } - - @Override - public int length() { - return value.length; - } - @Override public char charAt(int index) { - return (char) (byteAt(index) & 0xFF); - } - - public byte byteAt(int index) { - return value[index]; - } - - public byte[] array() { - return value; - } - - public int arrayOffset() { - return 0; + return b2c(byteAt(index)); } private static byte c2b(char c) { - if (c > 255) { + if (c > MAX_CHAR_VALUE) { return '?'; } return (byte) c; } + private static char b2c(byte b) { + return (char) (b & 0xFF); + } + private static byte toLowerCase(byte b) { if ('A' <= b && b <= 'Z') { - return (byte) (b + 32); + return (byte) (b + UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET); } return b; } private static char toLowerCase(char c) { if ('A' <= c && c <= 'Z') { - return (char) (c + 32); + return (char) (c + UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET); } return c; } private static byte toUpperCase(byte b) { if ('a' <= b && b <= 'z') { - return (byte) (b - 32); + return (byte) (b - UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET); } return b; } - /** - * Copies a range of characters into a new string. - * - * @param start the offset of the first character. - * @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()); - } - @Override - public AsciiString subSequence(int start, int end) { - if (start < 0 || start > end || end > length()) { - throw new IndexOutOfBoundsException("expected: 0 <= start(" + start + ") <= end (" + end + ") <= length(" - + length() + ')'); - } - - final byte[] value = this.value; - if (start == 0 && end == value.length) { - return this; - } - - if (end == start) { - return EMPTY_STRING; - } - - return new AsciiString(value, start, end - start, false); - } - - @Override - public int hashCode() { - int hash = this.hash; - final byte[] value = this.value; - if (hash != 0 || value.length == 0) { - return hash; - } - - for (int i = 0; i < value.length; ++i) { - hash = hash * 31 ^ value[i] & 31; - } - - return this.hash = hash; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof AsciiString)) { - return false; - } - - if (this == obj) { - return true; - } - - AsciiString that = (AsciiString) obj; - int thisHash = hashCode(); - int thatHash = that.hashCode(); - if (thisHash != thatHash || length() != that.length()) { - return false; - } - - byte[] thisValue = value; - byte[] thatValue = that.value; - int end = thisValue.length; - for (int i = 0, j = 0; i < end; i++, j++) { - if (thisValue[i] != thatValue[j]) { - return false; + public String toString(Charset charset, int start, int end) { + if (start == 0 && end == length()) { + if (string == null) { + string = super.toString(charset, start, end); } - } - - return true; - } - - @Override - @SuppressWarnings("deprecation") - public String toString() { - String string = this.string; - if (string != null) { return string; } - final byte[] value = this.value; - return this.string = new String(value, 0, 0, value.length); - } - - @SuppressWarnings("deprecation") - public String toString(int start, int end) { - final byte[] value = this.value; - if (start == 0 && end == value.length) { - return toString(); - } - - int length = end - start; - if (length == 0) { - return ""; - } - - return new String(value, 0, start, length); + return super.toString(charset, start, end); } /** @@ -543,7 +381,6 @@ public final class AsciiString implements CharSequence, Comparable * positive integer if this string is after the specified string. * @throws NullPointerException if {@code string} is {@code null}. */ - @Override public int compareTo(CharSequence string) { if (this == string) { return 0; @@ -555,7 +392,7 @@ public final class AsciiString implements CharSequence, Comparable int minLength = Math.min(length1, length2); byte[] value = this.value; for (int i = 0, j = 0; j < minLength; i++, j++) { - result = (value[i] & 0xFF) - string.charAt(j); + result = b2c(value[i]) - string.charAt(j); if (result != 0) { return result; } @@ -648,7 +485,6 @@ public final class AsciiString implements CharSequence, Comparable return false; } - final byte[] value = this.value; final int thisLen = value.length; final int thatLen = string.length(); if (thisLen != thatLen) { @@ -656,7 +492,7 @@ public final class AsciiString implements CharSequence, Comparable } for (int i = 0; i < thisLen; i++) { - char c1 = (char) (value[i] & 0xFF); + char c1 = b2c(value[i]); char c2 = string.charAt(i); if (c1 != c2 && toLowerCase(c1) != toLowerCase(c2)) { return false; @@ -665,24 +501,6 @@ public final class AsciiString implements CharSequence, Comparable return true; } - /** - * Converts this string to a byte array using the ASCII encoding. - * - * @return the byte array encoding of this string. - */ - public byte[] toByteArray() { - return toByteArray(0, length()); - } - - /** - * Converts this string to a byte array using the ASCII encoding. - * - * @return the byte array encoding of this string. - */ - public byte[] toByteArray(int start, int end) { - return Arrays.copyOfRange(value, start, end); - } - /** * Copies the characters in this string to a character array. * @@ -703,85 +521,13 @@ public final class AsciiString implements CharSequence, Comparable return EmptyArrays.EMPTY_CHARS; } - final byte[] value = this.value; final char[] buffer = new char[length]; for (int i = 0, j = start; i < length; i++, j++) { - buffer[i] = (char) (value[j] & 0xFF); + buffer[i] = b2c(value[j]); } return buffer; } - /** - * Copies the content of this string to a {@link ByteBuf} using {@link ByteBuf#writeBytes(byte[], int, int)}. - * - * @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, ByteBuf dst, int dstIdx, int length) { - if (dst == null) { - throw new NullPointerException("dst"); - } - - final byte[] value = this.value; - final int thisLen = value.length; - - if (srcIdx < 0 || length > thisLen - srcIdx) { - throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" - + length + ") <= srcLen(" + thisLen + ')'); - } - - dst.setBytes(dstIdx, value, srcIdx, length); - } - - /** - * Copies the content of this string to a {@link ByteBuf} using {@link ByteBuf#writeBytes(byte[], int, int)}. - * - * @param srcIdx the starting offset of characters to copy. - * @param dst the destination byte array. - * @param length the number of characters to copy. - */ - public void copy(int srcIdx, ByteBuf dst, int length) { - if (dst == null) { - throw new NullPointerException("dst"); - } - - final byte[] value = this.value; - final int thisLen = value.length; - - if (srcIdx < 0 || length > thisLen - srcIdx) { - throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" - + length + ") <= srcLen(" + thisLen + ')'); - } - - dst.writeBytes(value, srcIdx, length); - } - - /** - * 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 (dst == null) { - throw new NullPointerException("dst"); - } - - final byte[] value = this.value; - final int thisLen = value.length; - - if (srcIdx < 0 || length > thisLen - srcIdx) { - throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" - + length + ") <= srcLen(" + thisLen + ')'); - } - - System.arraycopy(value, srcIdx, dst, dstIdx, length); - } - /** * Copied the content of this string to a character array. * @@ -795,7 +541,6 @@ public final class AsciiString implements CharSequence, Comparable throw new NullPointerException("dst"); } - final byte[] value = this.value; final int thisLen = value.length; if (srcIdx < 0 || length > thisLen - srcIdx) { @@ -805,44 +550,13 @@ public final class AsciiString implements CharSequence, Comparable final int dstEnd = dstIdx + length; for (int i = srcIdx, j = dstIdx; j < dstEnd; i++, j++) { - dst[j] = (char) (value[i] & 0xFF); + dst[j] = b2c(value[i]); } } - /** - * Searches in this string for the first index of the specified character. The search for the character starts at - * the beginning and moves towards the end of this string. - * - * @param c the character to find. - * @return the index in this string of the specified character, -1 if the character isn't found. - */ - public int indexOf(int c) { - return indexOf(c, 0); - } - - /** - * Searches in this string for the index of the specified character. The search for the character starts at the - * specified offset and moves towards the end of this string. - * - * @param c the character to find. - * @param start the starting offset. - * @return the index in this string of the specified character, -1 if the character isn't found. - */ - public int indexOf(int c, int start) { - final byte[] value = this.value; - final int length = value.length; - if (start < length) { - if (start < 0) { - start = 0; - } - - for (int i = start; i < length; i++) { - if ((value[i] & 0xFF) == c) { - return i; - } - } - } - return -1; + @Override + public AsciiString subSequence(int start, int end) { + return (AsciiString) super.subSequence(start, end); } /** @@ -873,7 +587,6 @@ public final class AsciiString implements CharSequence, Comparable start = 0; } - final byte[] value = this.value; final int thisLen = value.length; int subCount = subString.length(); @@ -884,56 +597,27 @@ public final class AsciiString implements CharSequence, Comparable return -1; } - char firstChar = subString.charAt(0); - for (;;) { - int i = indexOf(firstChar, start); - if (i == -1 || subCount + i > thisLen) { - return -1; // handles subCount > count || start >= count - } - int o1 = i, o2 = 0; - while (++o2 < subCount && (value[++o1] & 0xFF) == subString.charAt(o2)) { - // Intentionally empty - } - if (o2 == subCount) { - return i; - } - start = i + 1; - } - } - - /** - * Searches in this string for the last index of the specified character. The search for the character starts at the - * end and moves towards the beginning of this string. - * - * @param c the character to find. - * @return the index in this string of the specified character, -1 if the character isn't found. - */ - public int lastIndexOf(int c) { - return lastIndexOf(c, length() - 1); - } - - /** - * Searches in this string for the index of the specified character. The search for the character starts at the - * specified offset and moves towards the beginning of this string. - * - * @param c the character to find. - * @param start the starting offset. - * @return the index in this string of the specified character, -1 if the character isn't found. - */ - public int lastIndexOf(int c, int start) { - if (start >= 0) { - final byte[] value = this.value; - final int length = value.length; - if (start >= length) { - start = length - 1; - } - for (int i = start; i >= 0; --i) { - if ((value[i] & 0xFF) == c) { + final char firstChar = subString.charAt(0); + ByteProcessor IndexOfVisitor = new IndexOfProcessor((byte) firstChar); + try { + for (;;) { + int i = forEachByte(start, thisLen - start, IndexOfVisitor); + if (i == -1 || subCount + i > thisLen) { + return -1; // handles subCount > count || start >= count + } + int o1 = i, o2 = 0; + while (++o2 < subCount && b2c(value[++o1]) == subString.charAt(o2)) { + // Intentionally empty + } + if (o2 == subCount) { return i; } + start = i + 1; } + } catch (Exception e) { + PlatformDependent.throwException(e); + return -1; } - return -1; } /** @@ -961,7 +645,6 @@ public final class AsciiString implements CharSequence, Comparable * @throws NullPointerException if {@code subString} is {@code null}. */ public int lastIndexOf(CharSequence subString, int start) { - final byte[] value = this.value; final int thisLen = value.length; final int subCount = subString.length(); @@ -976,32 +659,29 @@ public final class AsciiString implements CharSequence, Comparable start = Math.min(start, thisLen - subCount); // count and subCount are both >= 1 - char firstChar = subString.charAt(0); - for (;;) { - int i = lastIndexOf(firstChar, start); - if (i == -1) { - return -1; + final char firstChar = subString.charAt(0); + ByteProcessor IndexOfVisitor = new IndexOfProcessor((byte) firstChar); + try { + for (;;) { + int i = forEachByteDesc(start, thisLen - start, IndexOfVisitor); + if (i == -1) { + return -1; + } + int o1 = i, o2 = 0; + while (++o2 < subCount && b2c(value[++o1]) == subString.charAt(o2)) { + // Intentionally empty + } + if (o2 == subCount) { + return i; + } + start = i - 1; } - int o1 = i, o2 = 0; - while (++o2 < subCount && (value[++o1] & 0xFF) == subString.charAt(o2)) { - // Intentionally empty - } - if (o2 == subCount) { - return i; - } - start = i - 1; + } catch (Exception e) { + PlatformDependent.throwException(e); + return -1; } } - /** - * Answers if the size of this String is zero. - * - * @return true if the size of this String is zero, false otherwise - */ - public boolean isEmpty() { - return value.length == 0; - } - /** * Compares the specified string to this string and compares the specified range of characters to determine if they * are the same. @@ -1022,7 +702,6 @@ public final class AsciiString implements CharSequence, Comparable return false; } - final byte[] value = this.value; final int thisLen = value.length; if (thisStart < 0 || thisLen - thisStart < length) { return false; @@ -1034,7 +713,7 @@ public final class AsciiString implements CharSequence, Comparable final int thisEnd = thisStart + length; for (int i = thisStart, j = start; i < thisEnd; i++, j++) { - if ((value[i] & 0xFF) != string.charAt(j)) { + if (b2c(value[i]) != string.charAt(j)) { return false; } } @@ -1062,7 +741,6 @@ public final class AsciiString implements CharSequence, Comparable throw new NullPointerException("string"); } - final byte[] value = this.value; final int thisLen = value.length; if (thisStart < 0 || length > thisLen - thisStart) { return false; @@ -1073,7 +751,7 @@ public final class AsciiString implements CharSequence, Comparable int thisEnd = thisStart + length; while (thisStart < thisEnd) { - char c1 = (char) (value[thisStart++] & 0xFF); + char c1 = b2c(value[thisStart++]); char c2 = string.charAt(start++); if (c1 != c2 && toLowerCase(c1) != toLowerCase(c2)) { return false; @@ -1090,18 +768,29 @@ public final class AsciiString implements CharSequence, Comparable * @return a new string with occurrences of oldChar replaced by newChar. */ public AsciiString replace(char oldChar, char newChar) { - int index = indexOf(oldChar, 0); + if (oldChar > MAX_CHAR_VALUE) { + return this; + } + + final int index; + final byte oldCharByte = c2b(oldChar); + try { + index = forEachByte(new IndexOfProcessor(oldCharByte)); + } catch (Exception e) { + PlatformDependent.throwException(e); + return this; + } if (index == -1) { return this; } - final byte[] value = this.value; final int count = value.length; + final byte newCharByte = c2b(newChar); byte[] buffer = new byte[count]; - for (int i = 0, j = 0; i < value.length; i++, j++) { + for (int i = 0, j = 0; i < count; i++, j++) { byte b = value[i]; - if ((char) (b & 0xFF) == oldChar) { - b = (byte) newChar; + if (b == oldCharByte) { + b = newCharByte; } buffer[j] = b; } @@ -1141,7 +830,6 @@ public final class AsciiString implements CharSequence, Comparable */ public AsciiString toLowerCase() { boolean lowercased = true; - final byte[] value = this.value; int i, j; for (i = 0; i < value.length; ++i) { byte b = value[i]; @@ -1171,7 +859,6 @@ public final class AsciiString implements CharSequence, Comparable * @return a new string containing the uppercase characters equivalent to the characters in this string. */ public AsciiString toUpperCase() { - final byte[] value = this.value; boolean uppercased = true; int i, j; for (i = 0; i < value.length; ++i) { @@ -1202,7 +889,6 @@ public final class AsciiString implements CharSequence, Comparable * @return a new string with characters {@code <= \\u0020} removed from the beginning and the end. */ public AsciiString trim() { - final byte[] value = this.value; int start = 0, last = value.length; int end = last; while (start <= end && value[start] <= ' ') { @@ -1283,7 +969,6 @@ public final class AsciiString implements CharSequence, Comparable final List res = new ArrayList(); int start = 0; - final byte[] value = this.value; final int length = value.length; for (int i = start; i < length; i++) { if (charAt(i) == delim) { @@ -1331,155 +1016,70 @@ public final class AsciiString implements CharSequence, Comparable } public int parseInt() { - return parseInt(0, length(), 10); + return parseAsciiInt(); } public int parseInt(int radix) { - return parseInt(0, length(), radix); + return parseAsciiInt(radix); } public int parseInt(int start, int end) { - return parseInt(start, end, 10); + return parseAsciiInt(start, end); } 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 = charAt(i) == '-'; - if (negative && ++i == end) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - - return parseInt(i, end, radix, negative); - } - - private int parseInt(int start, int end, int radix, boolean negative) { - final byte[] value = this.value; - int max = Integer.MIN_VALUE / radix; - int result = 0; - int offset = start; - while (offset < end) { - int digit = Character.digit((char) (value[offset++] & 0xFF), radix); - if (digit == -1) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - if (max > result) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - int next = result * radix - digit; - if (next > result) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - result = next; - } - if (!negative) { - result = -result; - if (result < 0) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - } - return result; + return parseAsciiInt(start, end, radix); } public long parseLong() { - return parseLong(0, length(), 10); + return parseAsciiLong(); } public long parseLong(int radix) { - return parseLong(0, length(), radix); + return parseAsciiLong(radix); } public long parseLong(int start, int end) { - return parseLong(start, end, 10); + return parseAsciiLong(start, end); } 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 = charAt(i) == '-'; - if (negative && ++i == end) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - - return parseLong(i, end, radix, negative); + return parseAsciiLong(start, end, radix); } - private long parseLong(int start, int end, int radix, boolean negative) { - final byte[] value = this.value; - long max = Long.MIN_VALUE / radix; - long result = 0; - int offset = start; - while (offset < end) { - int digit = Character.digit((char) (value[offset++] & 0xFF), radix); - if (digit == -1) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - if (max > result) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - long next = result * radix - digit; - if (next > result) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - result = next; - } - if (!negative) { - result = -result; - if (result < 0) { - throw new NumberFormatException(subSequence(start, end).toString()); - } - } - return result; + public char parseChar(int start) { + return charAt(start); } public short parseShort() { - return parseShort(0, length(), 10); + return parseAsciiShort(); } public short parseShort(int radix) { - return parseShort(0, length(), radix); + return parseAsciiShort(radix); } public short parseShort(int start, int end) { - return parseShort(start, end, 10); + return parseAsciiShort(start, end); } 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).toString()); - } - return result; + return parseAsciiShort(start, end, radix); } public float parseFloat() { - return parseFloat(0, length()); + return parseAsciiFloat(); } public float parseFloat(int start, int end) { - return Float.parseFloat(toString(start, end)); + return parseAsciiFloat(start, end); } public double parseDouble() { - return parseDouble(0, length()); + return parseAsciiDouble(); } public double parseDouble(int start, int end) { - return Double.parseDouble(toString(start, end)); + return parseAsciiDouble(start, end); } } diff --git a/common/src/main/java/io/netty/util/ByteProcessor.java b/common/src/main/java/io/netty/util/ByteProcessor.java new file mode 100644 index 0000000000..2847813c6f --- /dev/null +++ b/common/src/main/java/io/netty/util/ByteProcessor.java @@ -0,0 +1,128 @@ +/* + * 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; + +/** + * Provides a mechanism to iterate over a collection of bytes. + */ +public interface ByteProcessor { + /** + * A {@link ByteProcessor} which finds the first appearance of a specific byte. + */ + class IndexOfProcessor implements ByteProcessor { + private final byte byteToFind; + + public IndexOfProcessor(byte byteToFind) { + this.byteToFind = byteToFind; + } + + @Override + public boolean process(byte value) { + return value != byteToFind; + } + } + + /** + * A {@link ByteProcessor} which finds the first appearance which is not of a specific byte. + */ + class IndexNotOfProcessor implements ByteProcessor { + private final byte byteToNotFind; + + public IndexNotOfProcessor(byte byteToNotFind) { + this.byteToNotFind = byteToNotFind; + } + + @Override + public boolean process(byte value) { + return value == byteToNotFind; + } + } + + /** + * Aborts on a {@code NUL (0x00)}. + */ + ByteProcessor FIND_NUL = new IndexOfProcessor((byte) 0); + + /** + * Aborts on a non-{@code NUL (0x00)}. + */ + ByteProcessor FIND_NON_NUL = new IndexNotOfProcessor((byte) 0); + + /** + * Aborts on a {@code CR ('\r')}. + */ + ByteProcessor FIND_CR = new IndexOfProcessor((byte) '\r'); + + /** + * Aborts on a non-{@code CR ('\r')}. + */ + ByteProcessor FIND_NON_CR = new IndexNotOfProcessor((byte) '\r'); + + /** + * Aborts on a {@code LF ('\n')}. + */ + ByteProcessor FIND_LF = new IndexOfProcessor((byte) '\n'); + + /** + * Aborts on a non-{@code LF ('\n')}. + */ + ByteProcessor FIND_NON_LF = new IndexNotOfProcessor((byte) '\n'); + + /** + * Aborts on a {@code CR ('\r')} or a {@code LF ('\n')}. + */ + ByteProcessor FIND_CRLF = new ByteProcessor() { + @Override + public boolean process(byte value) throws Exception { + return value != '\r' && value != '\n'; + } + }; + + /** + * Aborts on a byte which is neither a {@code CR ('\r')} nor a {@code LF ('\n')}. + */ + ByteProcessor FIND_NON_CRLF = new ByteProcessor() { + @Override + public boolean process(byte value) throws Exception { + return value == '\r' || value == '\n'; + } + }; + + /** + * Aborts on a linear whitespace (a ({@code ' '} or a {@code '\t'}). + */ + ByteProcessor FIND_LINEAR_WHITESPACE = new ByteProcessor() { + @Override + public boolean process(byte value) throws Exception { + return value != ' ' && value != '\t'; + } + }; + + /** + * Aborts on a byte which is not a linear whitespace (neither {@code ' '} nor {@code '\t'}). + */ + ByteProcessor FIND_NON_LINEAR_WHITESPACE = new ByteProcessor() { + @Override + public boolean process(byte value) throws Exception { + return value == ' ' || value == '\t'; + } + }; + + /** + * @return {@code true} if the processor wants to continue the loop and handle the next byte in the buffer. + * {@code false} if the processor wants to stop handling bytes and abort the loop. + */ + boolean process(byte value) throws Exception; +} diff --git a/common/src/main/java/io/netty/util/ByteString.java b/common/src/main/java/io/netty/util/ByteString.java new file mode 100644 index 0000000000..258dcca610 --- /dev/null +++ b/common/src/main/java/io/netty/util/ByteString.java @@ -0,0 +1,660 @@ +/* + * 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 io.netty.util.internal.StringUtil; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.util.Arrays; +import java.util.Comparator; + +/** + * 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 { + /** + * A byte wise comparator between two {@link ByteString} objects. + */ + public static final Comparator DEFAULT_COMPARATOR = new Comparator() { + @Override + public int compare(ByteString o1, ByteString o2) { + if (o1 == o2) { + return 0; + } + + int result; + int length1 = o1.length(); + int length2 = o2.length(); + int minLength = Math.min(length1, length2); + for (int i = 0, j = 0; j < minLength; i++, j++) { + result = o1.value[i] - o2.value[j]; + if (result != 0) { + return result; + } + } + + return length1 - length2; + } + }; + 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; + /** + * 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]; } + + /** + * 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) { + if (copy) { + this.value = checkNotNull(value, "value").clone(); + } else { + this.value = checkNotNull(value, "value"); + } + } + + /** + * Initialize this byte string based upon a range of a byte array. A copy will be made. + */ + public ByteString(byte[] value, int start, int length) { + this(value, start, length, true); + } + + /** + * 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. If this shared memory changes then {@link #arrayChanged()} must be called. + */ + public ByteString(byte[] value, int start, int length, boolean copy) { + if (start < 0 || start > checkNotNull(value, "value").length - length) { + throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + + ") <= " + "value.length(" + value.length + ')'); + } + + if (copy || start != 0 || length != value.length) { + this.value = Arrays.copyOfRange(value, start, start + length); + } else { + this.value = value; + } + } + + /** + * 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 = getBytes(value); + } + + /** + * Create a copy of the underlying storage from {@link value}. + * The copy will start at {@code start} and copy {@code length} bytes. + */ + public ByteString(ByteBuffer value, int start, int length) { + this.value = getBytes(value, start, length, true); + } + + /** + * Initialize a {@link ByteString} based upon the underlying storage from {@link value}. + * The copy will start at {@code start} and copy {@code length} bytes. + * if {@code copy} is true a copy will be made of the memory. + * if {@code copy} is false the underlying storage will be shared, if possible. + */ + public ByteString(ByteBuffer value, int start, int length, boolean copy) { + this.value = getBytes(value, start, length, copy); + } + + /** + * 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 = getBytes(value, charset); + } + + /** + * 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) { + this.value = getBytes(value, charset, start, 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 = getBytes(value, charset); + } + + /** + * 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) { + this.value = getBytes(value, charset, start, length); + } + + /** + * Create a copy of the underlying storage from {@link value} into a byte array. + * The copy will start at {@link ByteBuffer#position()} and copy {@link ByteBuffer#remaining()} bytes. + */ + private static byte[] getBytes(ByteBuffer value) { + return getBytes(value, value.position(), checkNotNull(value, "value").remaining()); + } + + /** + * Create a copy of the underlying storage from {@link value} into a byte array. + * The copy will start at {@code start} and copy {@code length} bytes. + */ + private static byte[] getBytes(ByteBuffer value, int start, int length) { + return getBytes(value, start, length, true); + } + + /** + * Return an array of the underlying storage from {@link value} into a byte array. + * The copy will start at {@code start} and copy {@code length} bytes. + * if {@code copy} is true a copy will be made of the memory. + * if {@code copy} is false the underlying storage will be shared, if possible. + */ + private static byte[] getBytes(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 || start != 0 || length != value.capacity()) { + int baseOffset = value.arrayOffset() + start; + return Arrays.copyOfRange(value.array(), baseOffset, baseOffset + length); + } else { + return value.array(); + } + } + + byte[] v = new byte[length]; + int oldPos = value.position(); + value.get(v, 0, length); + value.position(oldPos); + return v; + } + + /** + * Create a copy of {@link value} into a byte array using the encoding type of {@code charset}. + */ + private static byte[] getBytes(char[] value, Charset charset) { + return getBytes(value, charset, 0, checkNotNull(value, "value").length); + } + + /** + * Create a copy of {@link value} into a byte array using the encoding type of {@code charset}. + * The copy will start at index {@code start} and copy {@code length} bytes. + */ + private static byte[] getBytes(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 offset = nativeBuffer.arrayOffset(); + return Arrays.copyOfRange(nativeBuffer.array(), offset, offset + nativeBuffer.position()); + } + + /** + * Create a copy of {@link value} into a byte array using the encoding type of {@code charset}. + */ + private static byte[] getBytes(CharSequence value, Charset charset) { + return getBytes(value, charset, 0, checkNotNull(value, "value").length()); + } + + /** + * Create a copy of {@link value} into a byte array using the encoding type of {@code charset}. + * The copy will start at index {@code start} and copy {@code length} bytes. + */ + private static byte[] getBytes(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(); + return Arrays.copyOfRange(nativeBuffer.array(), offset, offset + nativeBuffer.position()); + } + + /** + * 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 forEachByte(0, value.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 > value.length - index) { + throw new IndexOutOfBoundsException("expected: " + "0 <= index(" + index + ") <= start + length(" + length + + ") <= " + "length(" + value.length + ')'); + } + + for (int i = index; i < length; ++i) { + if (!visitor.process(value[i])) { + return i; + } + } + 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 forEachByteDesc(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 > value.length - index) { + throw new IndexOutOfBoundsException("expected: " + "0 <= index(" + index + ") <= start + length(" + length + + ") <= " + "length(" + value.length + ')'); + } + + for (int i = index + length - 1; i >= index; --i) { + if (!visitor.process(value[i])) { + return i; + } + } + return -1; + } + + public final byte byteAt(int index) { + return value[index]; + } + + public final boolean isEmpty() { + return value.length == 0; + } + + public final int length() { + return value.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 final 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. + */ + public final byte[] array() { + return value; + } + + /** + * 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, end); + } + + /** + * 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) { + final int thisLen = value.length; + + if (srcIdx < 0 || length > thisLen - srcIdx) { + throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + + length + ") <= srcLen(" + thisLen + ')'); + } + + System.arraycopy(value, srcIdx, checkNotNull(dst, "dst"), dstIdx, length); + } + + @Override + public int hashCode() { + if (hash != 0) { + return hash; + } + + for (int i = 0; i < value.length; ++i) { + hash = hash * HASH_CODE_PRIME ^ value[i] & HASH_CODE_PRIME; + } + + return hash; + } + + /** + * Copies a range of characters into a new string. + * + * @param start the offset of the first character. + * @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 final ByteString subSequence(int start) { + return subSequence(start, length()); + } + + public ByteString subSequence(int start, int end) { + if (start < 0 || start > end || end > length()) { + throw new IndexOutOfBoundsException("expected: 0 <= start(" + start + ") <= end (" + end + ") <= length(" + + length() + ')'); + } + + if (start == 0 && end == value.length) { + return this; + } + + if (end == start) { + return EMPTY_STRING; + } + + return new ByteString(value, start, end - start, false); + } + + 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).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 offset = start; + while (offset < end) { + int digit = Character.digit((char) (value[offset++] & 0xFF), radix); + if (digit == -1) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + if (max > result) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + int next = result * radix - digit; + if (next > result) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + result = next; + } + if (!negative) { + result = -result; + if (result < 0) { + throw new NumberFormatException(subSequence(start, end).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).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 offset = start; + while (offset < end) { + int digit = Character.digit((char) (value[offset++] & 0xFF), radix); + if (digit == -1) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + if (max > result) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + long next = result * radix - digit; + if (next > result) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + result = next; + } + if (!negative) { + result = -result; + if (result < 0) { + throw new NumberFormatException(subSequence(start, end).toString()); + } + } + return result; + } + + public final char parseChar() { + return parseChar(0); + } + + public char parseChar(int start) { + if (start + 1 >= value.length) { + throw new IndexOutOfBoundsException("2 bytes required to convert to character. index " + + start + " would go out of bounds."); + } + return (char) (((value[start] & 0xFF) << 8) | (value[start + 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).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 that = (ByteString) obj; + if (length() != that.length() || hashCode() != that.hashCode()) { + return false; + } + + final int end = value.length; + for (int i = 0, j = 0; i < end; i++, j++) { + if (value[i] != that.value[j]) { + return false; + } + } + + return true; + } + + /** + * 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; + } + + return new String(value, start, length, charset); + } +} diff --git a/common/src/main/java/io/netty/util/internal/StringUtil.java b/common/src/main/java/io/netty/util/internal/StringUtil.java index ca69b8f707..3d743d6876 100644 --- a/common/src/main/java/io/netty/util/internal/StringUtil.java +++ b/common/src/main/java/io/netty/util/internal/StringUtil.java @@ -34,6 +34,7 @@ public final class StringUtil { public static final char LINE_FEED = '\n'; public static final char CARRIAGE_RETURN = '\r'; public static final String EMPTY_STRING = ""; + public static final byte UPPER_CASE_TO_LOWER_CASE_ASCII_OFFSET = (int) 'a' - (int) 'A'; private static final String[] BYTE2HEX_PAD = new String[256]; private static final String[] BYTE2HEX_NOPAD = new String[256]; /** diff --git a/codec/src/test/java/io/netty/handler/codec/AsciiStringTest.java b/common/src/test/java/io/netty/util/AsciiStringTest.java similarity index 69% rename from codec/src/test/java/io/netty/handler/codec/AsciiStringTest.java rename to common/src/test/java/io/netty/util/AsciiStringTest.java index 0d459bc1fc..f21b046245 100644 --- a/codec/src/test/java/io/netty/handler/codec/AsciiStringTest.java +++ b/common/src/test/java/io/netty/util/AsciiStringTest.java @@ -13,15 +13,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.handler.codec; +package io.netty.util; import static org.junit.Assert.assertArrayEquals; -import io.netty.util.CharsetUtil; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; import java.nio.charset.Charset; -import java.nio.charset.CharsetEncoder; import org.junit.Assert; import org.junit.Test; @@ -41,8 +37,8 @@ public class AsciiStringTest { final Charset[] charsets = CharsetUtil.values(); for (int i = 0; i < charsets.length; ++i) { final Charset charset = charsets[i]; - byte[] expected = getBytesWithEncoder(bString, charset); - byte[] actual = AsciiString.getBytes(b, charset); + byte[] expected = bString.getBytes(charset); + byte[] actual = new ByteString(b, charset).toByteArray(); assertArrayEquals("failure for " + charset, expected, actual); } } @@ -58,7 +54,7 @@ public class AsciiStringTest { for (int i = 0; i < charsets.length; ++i) { final Charset charset = charsets[i]; byte[] expected = bString.getBytes(charset); - byte[] actual = AsciiString.getBytes(bString, charset); + byte[] actual = new ByteString(bString, charset).toByteArray(); assertArrayEquals("failure for " + charset, expected, actual); } } @@ -72,12 +68,8 @@ public class AsciiStringTest { final String bString = b.toString(); // The AsciiString class actually limits the Charset to ISO_8859_1 byte[] expected = bString.getBytes(CharsetUtil.ISO_8859_1); - final Charset[] charsets = CharsetUtil.values(); - for (int i = 0; i < charsets.length; ++i) { - final Charset charset = charsets[i]; - byte[] actual = AsciiString.getBytes(new AsciiString(bString), charset); - assertArrayEquals("failure for " + charset, expected, actual); - } + byte[] actual = new AsciiString(bString).toByteArray(); + assertArrayEquals(expected, actual); } @Test @@ -86,11 +78,4 @@ public class AsciiStringTest { AsciiString ascii = new AsciiString(string.toCharArray()); Assert.assertEquals(string, ascii.toString()); } - - private static byte[] getBytesWithEncoder(CharSequence value, Charset charset) { - final CharsetEncoder encoder = CharsetUtil.getEncoder(charset); - final ByteBuffer nativeBuffer = ByteBuffer.allocate((int) (encoder.maxBytesPerChar() * value.length())); - encoder.encode(CharBuffer.wrap(value), nativeBuffer, true); - return nativeBuffer.array(); - } } diff --git a/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServerHandler.java b/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServerHandler.java index 40064d680c..aaf20f312c 100644 --- a/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServerHandler.java +++ b/example/src/main/java/io/netty/example/http/helloworld/HttpHelloWorldServerHandler.java @@ -15,6 +15,12 @@ */ package io.netty.example.http.helloworld; +import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION; +import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; +import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; +import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE; +import static io.netty.handler.codec.http.HttpResponseStatus.OK; +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerAdapter; @@ -25,10 +31,6 @@ import io.netty.handler.codec.http.HttpHeaderUtil; import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http.HttpRequest; -import static io.netty.handler.codec.http.HttpHeaderNames.*; -import static io.netty.handler.codec.http.HttpResponseStatus.*; -import static io.netty.handler.codec.http.HttpVersion.*; - public class HttpHelloWorldServerHandler extends ChannelHandlerAdapter { private static final byte[] CONTENT = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' }; diff --git a/example/src/main/java/io/netty/example/http2/server/HelloWorldHttp2Handler.java b/example/src/main/java/io/netty/example/http2/server/HelloWorldHttp2Handler.java index 5850b7c0b6..61fff9e4b9 100644 --- a/example/src/main/java/io/netty/example/http2/server/HelloWorldHttp2Handler.java +++ b/example/src/main/java/io/netty/example/http2/server/HelloWorldHttp2Handler.java @@ -22,7 +22,6 @@ import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.netty.handler.logging.LogLevel.INFO; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.HttpServerUpgradeHandler; import io.netty.handler.codec.http2.DefaultHttp2Connection; import io.netty.handler.codec.http2.DefaultHttp2FrameReader; @@ -39,6 +38,7 @@ import io.netty.handler.codec.http2.Http2FrameWriter; import io.netty.handler.codec.http2.Http2Headers; import io.netty.handler.codec.http2.Http2InboundFrameLogger; import io.netty.handler.codec.http2.Http2OutboundFrameLogger; +import io.netty.util.AsciiString; import io.netty.util.CharsetUtil; /** diff --git a/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java b/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java index 527d238c3c..2008f3b1e0 100644 --- a/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java +++ b/handler-proxy/src/main/java/io/netty/handler/proxy/HttpProxyHandler.java @@ -20,7 +20,6 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; -import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.base64.Base64; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; @@ -31,6 +30,7 @@ import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.LastHttpContent; +import io.netty.util.AsciiString; import io.netty.util.CharsetUtil; import java.net.InetSocketAddress;