From 2374e17c6ed3b037f2419dd33acdb481536b751d Mon Sep 17 00:00:00 2001 From: Scott Mitchell Date: Thu, 18 Sep 2014 19:04:35 -0400 Subject: [PATCH] Netty Headers Class Restructure and Algorithm Updates Motivation: Headers within netty do not cleanly share a common class hierarchy. As a result some header types support some operations and don't support others. The consolidation of the class hierarchy will allow for maintenance and scalability for new codec. The existing hierarchy also has a few short comings such as it is not clear when data conversions are happening. This could result unintentionally getting back a collection or iterator where a conversion on each entry must happen. The current headers algorithm also prepends all elements which means to find the first element or return a collection in insertion order often requires a complete traversal followed by a collections.reverse call. Modifications: -Provide a generic base class which provides all the implementation for headers in netty -Provide an extension to this class which allows for name type conversions to happen (to accommodate legacy CharSequence to String conversions) -Update the headers interface to clarify when conversions will happen. -Update the headers data structure so that appends are done to avoid unnecessary iteration or collection reversal. Result: -More unified class hierarchy for headers in netty -Improved headers data structure and algorithms -headers API more clearly identify when conversions are required. --- .../codec/http/DefaultHttpHeaders.java | 366 +++-- .../codec/http/DefaultHttpMessage.java | 2 +- .../codec/http/DefaultLastHttpContent.java | 43 +- .../handler/codec/http/EmptyHttpHeaders.java | 156 +- .../codec/http/HttpClientUpgradeHandler.java | 5 +- .../codec/http/HttpContentCompressor.java | 2 +- .../codec/http/HttpContentDecoder.java | 2 +- .../handler/codec/http/HttpHeaderUtil.java | 22 +- .../netty/handler/codec/http/HttpHeaders.java | 434 +++-- .../codec/http/HttpHeadersEncoder.java | 10 +- .../codec/http/HttpObjectAggregator.java | 2 +- .../handler/codec/http/HttpObjectDecoder.java | 10 +- .../handler/codec/http/HttpObjectEncoder.java | 8 +- .../codec/http/HttpServerUpgradeHandler.java | 27 +- .../handler/codec/http/cors/CorsConfig.java | 4 +- .../handler/codec/http/cors/CorsHandler.java | 10 +- .../http/multipart/HttpPostBodyUtil.java | 19 +- .../HttpPostMultipartRequestDecoder.java | 2 +- .../multipart/HttpPostRequestDecoder.java | 2 +- .../multipart/HttpPostRequestEncoder.java | 6 +- .../websocketx/WebSocketClientHandshaker.java | 2 +- .../WebSocketClientHandshaker00.java | 6 +- .../WebSocketClientHandshaker07.java | 6 +- .../WebSocketClientHandshaker08.java | 6 +- .../WebSocketClientHandshaker13.java | 6 +- .../WebSocketServerHandshaker00.java | 8 +- .../WebSocketServerHandshaker07.java | 4 +- .../WebSocketServerHandshaker08.java | 4 +- .../WebSocketServerHandshaker13.java | 4 +- .../WebSocketServerHandshakerFactory.java | 2 +- .../WebSocketClientExtensionHandler.java | 4 +- .../WebSocketServerExtensionHandler.java | 4 +- .../codec/spdy/DefaultSpdyHeaders.java | 211 ++- .../codec/spdy/DefaultSpdyHeadersFrame.java | 2 +- .../codec/spdy/SpdyHeaderBlockRawEncoder.java | 15 +- .../netty/handler/codec/spdy/SpdyHeaders.java | 83 +- .../handler/codec/spdy/SpdyHttpDecoder.java | 36 +- .../handler/codec/spdy/SpdyHttpEncoder.java | 24 +- .../spdy/SpdyHttpResponseStreamIdHandler.java | 2 +- .../codec/http/HttpContentCompressorTest.java | 6 +- .../codec/http/HttpContentEncoderTest.java | 6 +- .../codec/http/HttpHeaderUtilTest.java | 2 +- .../codec/http/HttpObjectAggregatorTest.java | 8 +- .../codec/http/HttpRequestDecoderTest.java | 2 +- .../codec/http/HttpResponseDecoderTest.java | 10 +- .../codec/http/HttpResponseEncoderTest.java | 3 +- .../codec/http/HttpServerCodecTest.java | 7 +- .../codec/http/cors/CorsConfigTest.java | 6 +- .../codec/http/cors/CorsHandlerTest.java | 61 +- .../multipart/HttpPostRequestEncoderTest.java | 58 +- .../WebSocketClientExtensionHandlerTest.java | 14 +- .../WebSocketServerExtensionHandlerTest.java | 4 +- ...WebSocketServerCompressionHandlerTest.java | 12 +- .../codec/spdy/SpdySessionHandlerTest.java | 8 +- .../codec/http2/DefaultHttp2Headers.java | 160 +- .../http2/DefaultHttp2HeadersEncoder.java | 16 +- .../DelegatingDecompressorFrameListener.java | 10 +- .../codec/http2/EmptyHttp2Headers.java | 159 +- .../handler/codec/http2/Http2Headers.java | 73 +- .../codec/http2/Http2ServerUpgradeCodec.java | 8 +- .../http2/Http2ToHttpConnectionHandler.java | 28 +- .../netty/handler/codec/http2/HttpUtil.java | 220 ++- .../http2/InboundHttp2ToHttpAdapter.java | 2 +- .../InboundHttp2ToHttpPriorityAdapter.java | 29 +- ...faultHttp2ToHttpConnectionHandlerTest.java | 2 +- .../http2/InboundHttp2ToHttpAdapterTest.java | 80 +- .../codec/stomp/DefaultStompHeaders.java | 153 +- .../handler/codec/stomp/StompHeaders.java | 83 +- .../codec/stomp/StompSubframeAggregator.java | 3 +- .../codec/stomp/StompSubframeDecoder.java | 10 +- .../codec/stomp/StompSubframeEncoder.java | 8 +- .../handler/codec/AsciiHeadersEncoder.java | 11 +- .../io/netty/handler/codec/AsciiString.java | 738 ++++----- .../io/netty/handler/codec/BinaryHeaders.java | 405 +---- .../handler/codec/ConvertibleHeaders.java | 112 ++ .../handler/codec/DefaultBinaryHeaders.java | 539 +++--- .../codec/DefaultConvertibleHeaders.java | 181 ++ .../netty/handler/codec/DefaultHeaders.java | 1459 +++++++++++++++++ .../handler/codec/DefaultTextHeaders.java | 952 ++++------- .../handler/codec/EmptyBinaryHeaders.java | 265 +-- .../codec/EmptyConvertibleHeaders.java | 71 + .../io/netty/handler/codec/EmptyHeaders.java | 536 ++++++ .../netty/handler/codec/EmptyTextHeaders.java | 337 ++-- .../io/netty/handler/codec/HeaderMap.java | 839 ---------- .../java/io/netty/handler/codec/Headers.java | 1085 ++++++++++++ .../handler/codec/TextHeaderProcessor.java | 21 - .../io/netty/handler/codec/TextHeaders.java | 529 ++---- .../netty/handler/codec/AsciiStringTest.java | 88 + ...est.java => DefaultBinaryHeadersTest.java} | 152 +- .../main/java/io/netty/util/CharsetUtil.java | 9 +- .../util/collection/CollectionUtils.java | 52 + .../file/HttpStaticFileServerHandler.java | 2 +- .../HttpHelloWorldServerHandler.java | 2 +- .../http/snoop/HttpSnoopClientHandler.java | 4 +- .../http/snoop/HttpSnoopServerHandler.java | 14 +- .../example/http/upload/HttpUploadClient.java | 2 +- .../http/upload/HttpUploadClientHandler.java | 4 +- .../http/upload/HttpUploadServerHandler.java | 10 +- .../example/http2/client/Http2Client.java | 4 +- .../http2/client/HttpResponseHandler.java | 5 +- .../http2/server/HelloWorldHttp1Handler.java | 2 +- .../client/HttpResponseClientHandler.java | 4 +- .../client/SpdyClientStreamIdHandler.java | 2 +- .../spdy/server/SpdyServerHandler.java | 2 +- .../example/stomp/StompClientHandler.java | 2 +- .../netty/handler/proxy/HttpProxyServer.java | 8 +- 106 files changed, 7094 insertions(+), 4166 deletions(-) create mode 100644 codec/src/main/java/io/netty/handler/codec/ConvertibleHeaders.java create mode 100644 codec/src/main/java/io/netty/handler/codec/DefaultConvertibleHeaders.java create mode 100644 codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java create mode 100644 codec/src/main/java/io/netty/handler/codec/EmptyConvertibleHeaders.java create mode 100644 codec/src/main/java/io/netty/handler/codec/EmptyHeaders.java delete mode 100644 codec/src/main/java/io/netty/handler/codec/HeaderMap.java create mode 100644 codec/src/main/java/io/netty/handler/codec/Headers.java delete mode 100644 codec/src/main/java/io/netty/handler/codec/TextHeaderProcessor.java create mode 100644 codec/src/test/java/io/netty/handler/codec/AsciiStringTest.java rename codec/src/test/java/io/netty/handler/codec/{HeaderMapTest.java => DefaultBinaryHeadersTest.java} (62%) create mode 100644 common/src/main/java/io/netty/util/collection/CollectionUtils.java 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 163af9c889..42a9b9d8ed 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 @@ -17,7 +17,6 @@ package io.netty.handler.codec.http; import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.DefaultTextHeaders; -import io.netty.handler.codec.TextHeaderProcessor; import io.netty.handler.codec.TextHeaders; import java.util.Calendar; @@ -45,28 +44,82 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader LOOKUP_TABLE['='] = -1; } - protected final boolean validate; + private static final class HttpHeadersValidationConverter extends DefaultTextValueTypeConverter { + private final boolean validate; + + public HttpHeadersValidationConverter(boolean validate) { + this.validate = validate; + } + + @Override + public CharSequence convert(Object value) { + if (value == null) { + throw new NullPointerException("value"); + } + + CharSequence seq; + if (value instanceof CharSequence) { + seq = (CharSequence) value; + } else if (value instanceof Number) { + seq = value.toString(); + } else if (value instanceof Date) { + seq = HttpHeaderDateFormat.get().format((Date) value); + } else if (value instanceof Calendar) { + seq = HttpHeaderDateFormat.get().format(((Calendar) value).getTime()); + } else { + seq = value.toString(); + } + + if (validate) { + if (value instanceof AsciiString) { + validateValue((AsciiString) seq); + } else { + validateValue(seq); + } + } + + return seq; + } + } + + static class HttpHeadersNameConverter implements NameConverter { + protected final boolean validate; + + public HttpHeadersNameConverter(boolean validate) { + this.validate = validate; + } + + @Override + public CharSequence convertName(CharSequence name) { + if (validate) { + if (name instanceof AsciiString) { + validateName((AsciiString) name); + } else { + validateName(name); + } + } + + return name; + } + } + + private static final HttpHeadersValidationConverter + VALIDATE_OBJECT_CONVERTER = new HttpHeadersValidationConverter(true); + private static final HttpHeadersValidationConverter + NO_VALIDATE_OBJECT_CONVERTER = new HttpHeadersValidationConverter(false); + private static final HttpHeadersNameConverter VALIDATE_NAME_CONVERTER = new HttpHeadersNameConverter(true); + private static final HttpHeadersNameConverter NO_VALIDATE_NAME_CONVERTER = new HttpHeadersNameConverter(false); public DefaultHttpHeaders() { this(true); } public DefaultHttpHeaders(boolean validate) { - this.validate = validate; + this(true, validate ? VALIDATE_NAME_CONVERTER : NO_VALIDATE_NAME_CONVERTER); } - @Override - protected CharSequence convertName(CharSequence name) { - name = super.convertName(name); - if (validate) { - if (name instanceof AsciiString) { - validateName((AsciiString) name); - } else { - validateName(name); - } - } - - return name; + protected DefaultHttpHeaders(boolean validate, NameConverter nameConverter) { + super(true, validate ? VALIDATE_OBJECT_CONVERTER : NO_VALIDATE_OBJECT_CONVERTER, nameConverter); } private static void validateName(AsciiString name) { @@ -79,8 +132,7 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader // 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); + throw new IllegalArgumentException("a header name cannot contain non-ASCII characters: " + name); } // Check for prohibited characters. @@ -90,13 +142,12 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader private static void validateName(CharSequence name) { // Go through each characters in the name - for (int index = 0; index < name.length(); index ++) { + for (int index = 0; index < name.length(); index++) { char character = name.charAt(index); // Check to see if the character is not an ASCII character if (character > 127) { - throw new IllegalArgumentException( - "a header name cannot contain non-ASCII characters: " + name); + throw new IllegalArgumentException("a header name cannot contain non-ASCII characters: " + name); } // Check for prohibited characters. @@ -106,144 +157,171 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader private static void validateNameChar(CharSequence name, int character) { 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); + throw new IllegalArgumentException("a header name cannot contain the following prohibited characters: " + + "=,;: \\t\\r\\n\\v\\f: " + name); } } - @Override - protected CharSequence convertValue(Object value) { - if (value == null) { - throw new NullPointerException("value"); - } - - CharSequence seq; - if (value instanceof CharSequence) { - seq = (CharSequence) value; - } else if (value instanceof Number) { - seq = value.toString(); - } else if (value instanceof Date) { - seq = HttpHeaderDateFormat.get().format((Date) value); - } else if (value instanceof Calendar) { - seq = HttpHeaderDateFormat.get().format(((Calendar) value).getTime()); - } else { - seq = value.toString(); - } - - if (validate) { - if (value instanceof AsciiString) { - validateValue((AsciiString) seq); - } else { - validateValue(seq); - } - } - - 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 ++) { + for (int index = start; index < end; index++) { state = validateValueChar(seq, state, (char) (array[index] & 0xFF)); } if (state != 0) { - throw new IllegalArgumentException( - "a header value must not end with '\\r' or '\\n':" + seq); + throw new IllegalArgumentException("a header value must not end with '\\r' or '\\n':" + seq); } } private static void validateValue(CharSequence seq) { int state = 0; // Start looping through each of the character - for (int index = 0; index < seq.length(); index ++) { + for (int index = 0; index < seq.length(); index++) { state = validateValueChar(seq, state, seq.charAt(index)); } if (state != 0) { - throw new IllegalArgumentException( - "a header value must not end with '\\r' or '\\n':" + seq); + throw new IllegalArgumentException("a header value must not end with '\\r' or '\\n':" + seq); } } private static int validateValueChar(CharSequence seq, int state, char character) { - /* - * State: - * - * 0: Previous character was neither CR nor LF - * 1: The previous character was CR - * 2: The previous character was LF - */ + /* + * 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) { // Check the absolutely prohibited characters. switch (character) { - case 0x0b: // Vertical tab - throw new IllegalArgumentException( - "a header value contains a prohibited character '\\v': " + seq); - case '\f': - throw new IllegalArgumentException( - "a header value contains a prohibited character '\\f': " + seq); + case 0x0b: // Vertical tab + throw new IllegalArgumentException("a header value contains a prohibited character '\\v': " + seq); + case '\f': + throw new IllegalArgumentException("a header value contains a prohibited character '\\f': " + seq); } } // Check the CRLF (HT | SP) pattern switch (state) { - case 0: - switch (character) { - case '\r': - state = 1; - break; - case '\n': - state = 2; - break; - } - break; - case 1: - switch (character) { - case '\n': - state = 2; - break; - default: - throw new IllegalArgumentException( - "only '\\n' is allowed after '\\r': " + seq); - } - break; - case 2: - switch (character) { - case '\t': case ' ': - state = 0; - break; - default: - throw new IllegalArgumentException( - "only ' ' and '\\t' are allowed after '\\n': " + seq); - } + case 0: + switch (character) { + case '\r': + state = 1; + break; + case '\n': + state = 2; + break; + } + break; + case 1: + switch (character) { + case '\n': + state = 2; + break; + default: + throw new IllegalArgumentException("only '\\n' is allowed after '\\r': " + seq); + } + break; + case 2: + switch (character) { + case '\t': + case ' ': + state = 0; + break; + default: + throw new IllegalArgumentException("only ' ' and '\\t' are allowed after '\\n': " + seq); + } } return state; } @Override - public HttpHeaders add(CharSequence name, Object value) { + public HttpHeaders add(CharSequence name, CharSequence value) { super.add(name, value); return this; } @Override - public HttpHeaders add(CharSequence name, Iterable values) { + public HttpHeaders add(CharSequence name, Iterable values) { super.add(name, values); return this; } @Override - public HttpHeaders add(CharSequence name, Object... values) { + public HttpHeaders add(CharSequence name, CharSequence... values) { super.add(name, values); return this; } + @Override + public HttpHeaders addObject(CharSequence name, Object value) { + super.addObject(name, value); + return this; + } + + @Override + public HttpHeaders addObject(CharSequence name, Iterable values) { + super.addObject(name, values); + return this; + } + + @Override + public HttpHeaders addObject(CharSequence name, Object... values) { + super.addObject(name, values); + return this; + } + + @Override + public HttpHeaders addBoolean(CharSequence name, boolean value) { + super.addBoolean(name, value); + return this; + } + + @Override + public HttpHeaders addChar(CharSequence name, char value) { + super.addChar(name, value); + return this; + } + + @Override + public HttpHeaders addByte(CharSequence name, byte value) { + super.addByte(name, value); + return this; + } + + @Override + public HttpHeaders addShort(CharSequence name, short value) { + super.addShort(name, value); + return this; + } + + @Override + public HttpHeaders addInt(CharSequence name, int value) { + super.addInt(name, value); + return this; + } + + @Override + public HttpHeaders addLong(CharSequence name, long value) { + super.addLong(name, value); + return this; + } + + @Override + public HttpHeaders addFloat(CharSequence name, float value) { + super.addFloat(name, value); + return this; + } + + @Override + public HttpHeaders addDouble(CharSequence name, double value) { + super.addDouble(name, value); + return this; + } + @Override public HttpHeaders add(TextHeaders headers) { super.add(headers); @@ -251,23 +329,89 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader } @Override - public HttpHeaders set(CharSequence name, Object value) { + public HttpHeaders set(CharSequence name, CharSequence value) { super.set(name, value); return this; } @Override - public HttpHeaders set(CharSequence name, Object... values) { + public HttpHeaders set(CharSequence name, Iterable values) { super.set(name, values); return this; } @Override - public HttpHeaders set(CharSequence name, Iterable values) { + public HttpHeaders set(CharSequence name, CharSequence... values) { super.set(name, values); return this; } + @Override + public HttpHeaders setObject(CharSequence name, Object value) { + super.setObject(name, value); + return this; + } + + @Override + public HttpHeaders setObject(CharSequence name, Iterable values) { + super.setObject(name, values); + return this; + } + + @Override + public HttpHeaders setObject(CharSequence name, Object... values) { + super.setObject(name, values); + return this; + } + + @Override + public HttpHeaders setBoolean(CharSequence name, boolean value) { + super.setBoolean(name, value); + return this; + } + + @Override + public HttpHeaders setChar(CharSequence name, char value) { + super.setChar(name, value); + return this; + } + + @Override + public HttpHeaders setByte(CharSequence name, byte value) { + super.setByte(name, value); + return this; + } + + @Override + public HttpHeaders setShort(CharSequence name, short value) { + super.setShort(name, value); + return this; + } + + @Override + public HttpHeaders setInt(CharSequence name, int value) { + super.setInt(name, value); + return this; + } + + @Override + public HttpHeaders setLong(CharSequence name, long value) { + super.setLong(name, value); + return this; + } + + @Override + public HttpHeaders setFloat(CharSequence name, float value) { + super.setFloat(name, value); + return this; + } + + @Override + public HttpHeaders setDouble(CharSequence name, double value) { + super.setDouble(name, value); + return this; + } + @Override public HttpHeaders set(TextHeaders headers) { super.set(headers); @@ -275,14 +419,14 @@ public class DefaultHttpHeaders extends DefaultTextHeaders implements HttpHeader } @Override - public HttpHeaders clear() { - super.clear(); + public HttpHeaders setAll(TextHeaders headers) { + super.setAll(headers); return this; } @Override - public HttpHeaders forEachEntry(TextHeaderProcessor processor) { - super.forEachEntry(processor); + public HttpHeaders clear() { + super.clear(); return this; } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpMessage.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpMessage.java index a83fd1b98b..66428c0690 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpMessage.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpMessage.java @@ -108,7 +108,7 @@ public abstract class DefaultHttpMessage extends DefaultHttpObject implements Ht } void appendHeaders(StringBuilder buf, HttpHeaders headers) { - for (Map.Entry e: headers) { + for (Map.Entry e: headers) { buf.append(e.getKey()); buf.append(": "); buf.append(e.getValue()); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultLastHttpContent.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultLastHttpContent.java index 2d23ddc728..aed25ff0cd 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultLastHttpContent.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultLastHttpContent.java @@ -15,6 +15,9 @@ */ package io.netty.handler.codec.http; +import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH; +import static io.netty.handler.codec.http.HttpHeaders.Names.TRAILER; +import static io.netty.handler.codec.http.HttpHeaders.Names.TRANSFER_ENCODING; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.handler.codec.AsciiString; @@ -99,7 +102,7 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt } private void appendHeaders(StringBuilder buf) { - for (Map.Entry e: trailingHeaders()) { + for (Map.Entry e : trailingHeaders()) { buf.append(e.getKey()); buf.append(": "); buf.append(e.getValue()); @@ -108,22 +111,32 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt } private static final class TrailingHttpHeaders extends DefaultHttpHeaders { - TrailingHttpHeaders(boolean validate) { - super(validate); + private static final class TrailingHttpHeadersNameConverter extends HttpHeadersNameConverter { + public TrailingHttpHeadersNameConverter(boolean validate) { + super(validate); + } + + @Override + public CharSequence convertName(CharSequence name) { + name = super.convertName(name); + if (validate) { + if (AsciiString.equalsIgnoreCase(CONTENT_LENGTH, name) + || AsciiString.equalsIgnoreCase(TRANSFER_ENCODING, name) + || AsciiString.equalsIgnoreCase(TRAILER, name)) { + throw new IllegalArgumentException("prohibited trailing header: " + name); + } + } + return name; + } } - @Override - protected CharSequence convertName(CharSequence name) { - name = super.convertName(name); - if (validate) { - if (AsciiString.equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH, name) || - AsciiString.equalsIgnoreCase(HttpHeaders.Names.TRANSFER_ENCODING, name) || - AsciiString.equalsIgnoreCase(HttpHeaders.Names.TRAILER, name)) { - throw new IllegalArgumentException( - "prohibited trailing header: " + name); - } - } - return name; + private static final TrailingHttpHeadersNameConverter + VALIDATE_NAME_CONVERTER = new TrailingHttpHeadersNameConverter(true); + private static final TrailingHttpHeadersNameConverter + NO_VALIDATE_NAME_CONVERTER = new TrailingHttpHeadersNameConverter(false); + + TrailingHttpHeaders(boolean validate) { + super(validate, validate ? VALIDATE_NAME_CONVERTER : NO_VALIDATE_NAME_CONVERTER); } } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/EmptyHttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/EmptyHttpHeaders.java index f73cdd6ab6..47cce001c4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/EmptyHttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/EmptyHttpHeaders.java @@ -17,33 +17,99 @@ package io.netty.handler.codec.http; import io.netty.handler.codec.EmptyTextHeaders; -import io.netty.handler.codec.TextHeaderProcessor; import io.netty.handler.codec.TextHeaders; public class EmptyHttpHeaders extends EmptyTextHeaders implements HttpHeaders { public static final EmptyHttpHeaders INSTANCE = new EmptyHttpHeaders(); - protected EmptyHttpHeaders() { } + protected EmptyHttpHeaders() { + } @Override - public HttpHeaders add(CharSequence name, Object value) { + public HttpHeaders add(CharSequence name, CharSequence value) { super.add(name, value); return this; } @Override - public HttpHeaders add(CharSequence name, Iterable values) { + public HttpHeaders add(CharSequence name, Iterable values) { super.add(name, values); return this; } @Override - public HttpHeaders add(CharSequence name, Object... values) { + public HttpHeaders add(CharSequence name, CharSequence... values) { super.add(name, values); return this; } + @Override + public HttpHeaders addObject(CharSequence name, Object value) { + super.addObject(name, value); + return this; + } + + @Override + public HttpHeaders addObject(CharSequence name, Iterable values) { + super.addObject(name, values); + return this; + } + + @Override + public HttpHeaders addObject(CharSequence name, Object... values) { + super.addObject(name, values); + return this; + } + + @Override + public HttpHeaders addBoolean(CharSequence name, boolean value) { + super.addBoolean(name, value); + return this; + } + + @Override + public HttpHeaders addChar(CharSequence name, char value) { + super.addChar(name, value); + return this; + } + + @Override + public HttpHeaders addByte(CharSequence name, byte value) { + super.addByte(name, value); + return this; + } + + @Override + public HttpHeaders addShort(CharSequence name, short value) { + super.addShort(name, value); + return this; + } + + @Override + public HttpHeaders addInt(CharSequence name, int value) { + super.addInt(name, value); + return this; + } + + @Override + public HttpHeaders addLong(CharSequence name, long value) { + super.addLong(name, value); + return this; + } + + @Override + public HttpHeaders addFloat(CharSequence name, float value) { + super.addFloat(name, value); + return this; + } + + @Override + public HttpHeaders addDouble(CharSequence name, double value) { + super.addDouble(name, value); + return this; + } + @Override public HttpHeaders add(TextHeaders headers) { super.add(headers); @@ -51,23 +117,89 @@ public class EmptyHttpHeaders extends EmptyTextHeaders implements HttpHeaders { } @Override - public HttpHeaders set(CharSequence name, Object value) { + public HttpHeaders set(CharSequence name, CharSequence value) { super.set(name, value); return this; } @Override - public HttpHeaders set(CharSequence name, Object... values) { + public HttpHeaders set(CharSequence name, Iterable values) { super.set(name, values); return this; } @Override - public HttpHeaders set(CharSequence name, Iterable values) { + public HttpHeaders set(CharSequence name, CharSequence... values) { super.set(name, values); return this; } + @Override + public HttpHeaders setObject(CharSequence name, Object value) { + super.setObject(name, value); + return this; + } + + @Override + public HttpHeaders setObject(CharSequence name, Iterable values) { + super.setObject(name, values); + return this; + } + + @Override + public HttpHeaders setObject(CharSequence name, Object... values) { + super.setObject(name, values); + return this; + } + + @Override + public HttpHeaders setBoolean(CharSequence name, boolean value) { + super.setBoolean(name, value); + return this; + } + + @Override + public HttpHeaders setChar(CharSequence name, char value) { + super.setChar(name, value); + return this; + } + + @Override + public HttpHeaders setByte(CharSequence name, byte value) { + super.setByte(name, value); + return this; + } + + @Override + public HttpHeaders setShort(CharSequence name, short value) { + super.setShort(name, value); + return this; + } + + @Override + public HttpHeaders setInt(CharSequence name, int value) { + super.setInt(name, value); + return this; + } + + @Override + public HttpHeaders setLong(CharSequence name, long value) { + super.setLong(name, value); + return this; + } + + @Override + public HttpHeaders setFloat(CharSequence name, float value) { + super.setFloat(name, value); + return this; + } + + @Override + public HttpHeaders setDouble(CharSequence name, double value) { + super.setDouble(name, value); + return this; + } + @Override public HttpHeaders set(TextHeaders headers) { super.set(headers); @@ -75,14 +207,14 @@ public class EmptyHttpHeaders extends EmptyTextHeaders implements HttpHeaders { } @Override - public HttpHeaders clear() { - super.clear(); + public HttpHeaders setAll(TextHeaders headers) { + super.setAll(headers); return this; } @Override - public HttpHeaders forEachEntry(TextHeaderProcessor processor) { - super.forEachEntry(processor); + public HttpHeaders clear() { + super.clear(); return this; } } 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 584791a672..1a29d1dc41 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,6 +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 java.util.Collection; import java.util.LinkedHashSet; @@ -177,12 +178,12 @@ public class HttpClientUpgradeHandler extends HttpObjectAggregator { return; } - String upgradeHeader = response.headers().get(UPGRADE); + CharSequence upgradeHeader = response.headers().get(UPGRADE); if (upgradeHeader == null) { throw new IllegalStateException( "Switching Protocols response missing UPGRADE header"); } - if (!upgradeCodec.protocol().equalsIgnoreCase(upgradeHeader)) { + if (AsciiString.equalsIgnoreCase(upgradeCodec.protocol(), upgradeHeader)) { throw new IllegalStateException( "Switching Protocols response with unexpected UPGRADE protocol: " + upgradeHeader); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentCompressor.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentCompressor.java index 10ca6b825e..2957287f5a 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentCompressor.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentCompressor.java @@ -95,7 +95,7 @@ public class HttpContentCompressor extends HttpContentEncoder { @Override protected Result beginEncode(HttpResponse headers, CharSequence acceptEncoding) throws Exception { - String contentEncoding = headers.headers().get(HttpHeaders.Names.CONTENT_ENCODING); + CharSequence contentEncoding = headers.headers().get(HttpHeaders.Names.CONTENT_ENCODING); if (contentEncoding != null && !AsciiString.equalsIgnoreCase(HttpHeaders.Values.IDENTITY, contentEncoding)) { return null; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java index 49a7d48d2c..eae4f769e8 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java @@ -87,7 +87,7 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder values = m.headers().getAllUnconverted(Names.TRANSFER_ENCODING); + List values = m.headers().getAll(Names.TRANSFER_ENCODING); if (values.isEmpty()) { return; } 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 b56eeb88f3..65f14720d6 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 @@ -16,7 +16,6 @@ package io.netty.handler.codec.http; import io.netty.handler.codec.AsciiString; -import io.netty.handler.codec.TextHeaderProcessor; import io.netty.handler.codec.TextHeaders; @@ -25,326 +24,332 @@ import io.netty.handler.codec.TextHeaders; * commonly used utility methods that accesses an {@link HttpMessage}. */ public interface HttpHeaders extends TextHeaders { - /** * Standard HTTP header names. + *

+ * These are all defined as lowercase to support HTTP/2 requirements while also not + * violating HTTP/1.x requirements. New header names should always be lowercase. */ final class Names { /** - * {@code "Accept"} + * {@code "accept"} */ - public static final AsciiString ACCEPT = new AsciiString("Accept"); + public static final AsciiString ACCEPT = new AsciiString("accept"); /** - * {@code "Accept-Charset"} + * {@code "accept-charset"} */ - public static final AsciiString ACCEPT_CHARSET = new AsciiString("Accept-Charset"); + public static final AsciiString ACCEPT_CHARSET = new AsciiString("accept-charset"); /** - * {@code "Accept-Encoding"} + * {@code "accept-encoding"} */ - public static final AsciiString ACCEPT_ENCODING = new AsciiString("Accept-Encoding"); + public static final AsciiString ACCEPT_ENCODING = new AsciiString("accept-encoding"); /** - * {@code "Accept-Language"} + * {@code "accept-language"} */ - public static final AsciiString ACCEPT_LANGUAGE = new AsciiString("Accept-Language"); + public static final AsciiString ACCEPT_LANGUAGE = new AsciiString("accept-language"); /** - * {@code "Accept-Ranges"} + * {@code "accept-ranges"} */ - public static final AsciiString ACCEPT_RANGES = new AsciiString("Accept-Ranges"); + public static final AsciiString ACCEPT_RANGES = new AsciiString("accept-ranges"); /** - * {@code "Accept-Patch"} + * {@code "accept-patch"} */ - public static final AsciiString ACCEPT_PATCH = new AsciiString("Accept-Patch"); + public static final AsciiString ACCEPT_PATCH = new AsciiString("accept-patch"); /** - * {@code "Access-Control-Allow-Credentials"} + * {@code "access-control-allow-credentials"} */ public static final AsciiString ACCESS_CONTROL_ALLOW_CREDENTIALS = - new AsciiString("Access-Control-Allow-Credentials"); + new AsciiString("access-control-allow-credentials"); /** - * {@code "Access-Control-Allow-Headers"} + * {@code "access-control-allow-headers"} */ public static final AsciiString ACCESS_CONTROL_ALLOW_HEADERS = - new AsciiString("Access-Control-Allow-Headers"); + new AsciiString("access-control-allow-headers"); /** - * {@code "Access-Control-Allow-Methods"} + * {@code "access-control-allow-methods"} */ public static final AsciiString ACCESS_CONTROL_ALLOW_METHODS = - new AsciiString("Access-Control-Allow-Methods"); + new AsciiString("access-control-allow-methods"); /** - * {@code "Access-Control-Allow-Origin"} + * {@code "access-control-allow-origin"} */ public static final AsciiString ACCESS_CONTROL_ALLOW_ORIGIN = - new AsciiString("Access-Control-Allow-Origin"); + new AsciiString("access-control-allow-origin"); /** - * {@code "Access-Control-Expose-Headers"} + * {@code "access-control-expose-headers"} */ public static final AsciiString ACCESS_CONTROL_EXPOSE_HEADERS = - new AsciiString("Access-Control-Expose-Headers"); + new AsciiString("access-control-expose-headers"); /** - * {@code "Access-Control-Max-Age"} + * {@code "access-control-max-age"} */ - public static final AsciiString ACCESS_CONTROL_MAX_AGE = new AsciiString("Access-Control-Max-Age"); + public static final AsciiString ACCESS_CONTROL_MAX_AGE = new AsciiString("access-control-max-age"); /** - * {@code "Access-Control-Request-Headers"} + * {@code "access-control-request-headers"} */ public static final AsciiString ACCESS_CONTROL_REQUEST_HEADERS = - new AsciiString("Access-Control-Request-Headers"); + new AsciiString("access-control-request-headers"); /** - * {@code "Access-Control-Request-Method"} + * {@code "access-control-request-method"} */ public static final AsciiString ACCESS_CONTROL_REQUEST_METHOD = - new AsciiString("Access-Control-Request-Method"); + new AsciiString("access-control-request-method"); /** - * {@code "Age"} + * {@code "age"} */ - public static final AsciiString AGE = new AsciiString("Age"); + public static final AsciiString AGE = new AsciiString("age"); /** - * {@code "Allow"} + * {@code "allow"} */ - public static final AsciiString ALLOW = new AsciiString("Allow"); + public static final AsciiString ALLOW = new AsciiString("allow"); /** - * {@code "Authorization"} + * {@code "authorization"} */ - public static final AsciiString AUTHORIZATION = new AsciiString("Authorization"); + public static final AsciiString AUTHORIZATION = new AsciiString("authorization"); /** - * {@code "Cache-Control"} + * {@code "cache-control"} */ - public static final AsciiString CACHE_CONTROL = new AsciiString("Cache-Control"); + public static final AsciiString CACHE_CONTROL = new AsciiString("cache-control"); /** - * {@code "Connection"} + * {@code "connection"} */ - public static final AsciiString CONNECTION = new AsciiString("Connection"); + public static final AsciiString CONNECTION = new AsciiString("connection"); /** - * {@code "Content-Base"} + * {@code "content-base"} */ - public static final AsciiString CONTENT_BASE = new AsciiString("Content-Base"); + public static final AsciiString CONTENT_BASE = new AsciiString("content-base"); /** - * {@code "Content-Encoding"} + * {@code "content-encoding"} */ - public static final AsciiString CONTENT_ENCODING = new AsciiString("Content-Encoding"); + public static final AsciiString CONTENT_ENCODING = new AsciiString("content-encoding"); /** - * {@code "Content-Language"} + * {@code "content-language"} */ - public static final AsciiString CONTENT_LANGUAGE = new AsciiString("Content-Language"); + public static final AsciiString CONTENT_LANGUAGE = new AsciiString("content-language"); /** - * {@code "Content-Length"} + * {@code "content-length"} */ - public static final AsciiString CONTENT_LENGTH = new AsciiString("Content-Length"); + public static final AsciiString CONTENT_LENGTH = new AsciiString("content-length"); /** - * {@code "Content-Location"} + * {@code "content-location"} */ - public static final AsciiString CONTENT_LOCATION = new AsciiString("Content-Location"); + public static final AsciiString CONTENT_LOCATION = new AsciiString("content-location"); /** - * {@code "Content-Transfer-Encoding"} + * {@code "content-transfer-encoding"} */ - public static final AsciiString CONTENT_TRANSFER_ENCODING = new AsciiString("Content-Transfer-Encoding"); + public static final AsciiString CONTENT_TRANSFER_ENCODING = new AsciiString("content-transfer-encoding"); /** - * {@code "Content-MD5"} + * {@code "content-disposition"} */ - public static final AsciiString CONTENT_MD5 = new AsciiString("Content-MD5"); + public static final AsciiString CONTENT_DISPOSITION = new AsciiString("content-disposition"); /** - * {@code "Content-Range"} + * {@code "content-md5"} */ - public static final AsciiString CONTENT_RANGE = new AsciiString("Content-Range"); + public static final AsciiString CONTENT_MD5 = new AsciiString("content-md5"); /** - * {@code "Content-Type"} + * {@code "content-range"} */ - public static final AsciiString CONTENT_TYPE = new AsciiString("Content-Type"); + public static final AsciiString CONTENT_RANGE = new AsciiString("content-range"); /** - * {@code "Cookie"} + * {@code "content-type"} */ - public static final AsciiString COOKIE = new AsciiString("Cookie"); + public static final AsciiString CONTENT_TYPE = new AsciiString("content-type"); /** - * {@code "Date"} + * {@code "cookie"} */ - public static final AsciiString DATE = new AsciiString("Date"); + public static final AsciiString COOKIE = new AsciiString("cookie"); /** - * {@code "ETag"} + * {@code "date"} */ - public static final AsciiString ETAG = new AsciiString("ETag"); + public static final AsciiString DATE = new AsciiString("date"); /** - * {@code "Expect"} + * {@code "etag"} */ - public static final AsciiString EXPECT = new AsciiString("Expect"); + public static final AsciiString ETAG = new AsciiString("etag"); /** - * {@code "Expires"} + * {@code "expect"} */ - public static final AsciiString EXPIRES = new AsciiString("Expires"); + public static final AsciiString EXPECT = new AsciiString("expect"); /** - * {@code "From"} + * {@code "expires"} */ - public static final AsciiString FROM = new AsciiString("From"); + public static final AsciiString EXPIRES = new AsciiString("expires"); /** - * {@code "Host"} + * {@code "from"} */ - public static final AsciiString HOST = new AsciiString("Host"); + public static final AsciiString FROM = new AsciiString("from"); /** - * {@code "If-Match"} + * {@code "host"} */ - public static final AsciiString IF_MATCH = new AsciiString("If-Match"); + public static final AsciiString HOST = new AsciiString("host"); /** - * {@code "If-Modified-Since"} + * {@code "if-match"} */ - public static final AsciiString IF_MODIFIED_SINCE = new AsciiString("If-Modified-Since"); + public static final AsciiString IF_MATCH = new AsciiString("if-match"); /** - * {@code "If-None-Match"} + * {@code "if-modified-since"} */ - public static final AsciiString IF_NONE_MATCH = new AsciiString("If-None-Match"); + public static final AsciiString IF_MODIFIED_SINCE = new AsciiString("if-modified-since"); /** - * {@code "If-Range"} + * {@code "if-none-match"} */ - public static final AsciiString IF_RANGE = new AsciiString("If-Range"); + public static final AsciiString IF_NONE_MATCH = new AsciiString("if-none-match"); /** - * {@code "If-Unmodified-Since"} + * {@code "if-range"} */ - public static final AsciiString IF_UNMODIFIED_SINCE = new AsciiString("If-Unmodified-Since"); + public static final AsciiString IF_RANGE = new AsciiString("if-range"); /** - * {@code "Last-Modified"} + * {@code "if-unmodified-since"} */ - public static final AsciiString LAST_MODIFIED = new AsciiString("Last-Modified"); + public static final AsciiString IF_UNMODIFIED_SINCE = new AsciiString("if-unmodified-since"); /** - * {@code "Location"} + * {@code "last-modified"} */ - public static final AsciiString LOCATION = new AsciiString("Location"); + public static final AsciiString LAST_MODIFIED = new AsciiString("last-modified"); /** - * {@code "Max-Forwards"} + * {@code "location"} */ - public static final AsciiString MAX_FORWARDS = new AsciiString("Max-Forwards"); + public static final AsciiString LOCATION = new AsciiString("location"); /** - * {@code "Origin"} + * {@code "max-forwards"} */ - public static final AsciiString ORIGIN = new AsciiString("Origin"); + public static final AsciiString MAX_FORWARDS = new AsciiString("max-forwards"); /** - * {@code "Pragma"} + * {@code "origin"} */ - public static final AsciiString PRAGMA = new AsciiString("Pragma"); + public static final AsciiString ORIGIN = new AsciiString("origin"); /** - * {@code "Proxy-Authenticate"} + * {@code "pragma"} */ - public static final AsciiString PROXY_AUTHENTICATE = new AsciiString("Proxy-Authenticate"); + public static final AsciiString PRAGMA = new AsciiString("pragma"); /** - * {@code "Proxy-Authorization"} + * {@code "proxy-authenticate"} */ - public static final AsciiString PROXY_AUTHORIZATION = new AsciiString("Proxy-Authorization"); + public static final AsciiString PROXY_AUTHENTICATE = new AsciiString("proxy-authenticate"); /** - * {@code "Range"} + * {@code "proxy-authorization"} */ - public static final AsciiString RANGE = new AsciiString("Range"); + public static final AsciiString PROXY_AUTHORIZATION = new AsciiString("proxy-authorization"); /** - * {@code "Referer"} + * {@code "range"} */ - public static final AsciiString REFERER = new AsciiString("Referer"); + public static final AsciiString RANGE = new AsciiString("range"); /** - * {@code "Retry-After"} + * {@code "referer"} */ - public static final AsciiString RETRY_AFTER = new AsciiString("Retry-After"); + public static final AsciiString REFERER = new AsciiString("referer"); /** - * {@code "Sec-WebSocket-Key1"} + * {@code "retry-after"} */ - public static final AsciiString SEC_WEBSOCKET_KEY1 = new AsciiString("Sec-WebSocket-Key1"); + public static final AsciiString RETRY_AFTER = new AsciiString("retry-after"); /** - * {@code "Sec-WebSocket-Key2"} + * {@code "sec-websocket-key1"} */ - public static final AsciiString SEC_WEBSOCKET_KEY2 = new AsciiString("Sec-WebSocket-Key2"); + public static final AsciiString SEC_WEBSOCKET_KEY1 = new AsciiString("sec-websocket-key1"); /** - * {@code "Sec-WebSocket-Location"} + * {@code "sec-websocket-key2"} */ - public static final AsciiString SEC_WEBSOCKET_LOCATION = new AsciiString("Sec-WebSocket-Location"); + public static final AsciiString SEC_WEBSOCKET_KEY2 = new AsciiString("sec-websocket-key2"); /** - * {@code "Sec-WebSocket-Origin"} + * {@code "sec-websocket-location"} */ - public static final AsciiString SEC_WEBSOCKET_ORIGIN = new AsciiString("Sec-WebSocket-Origin"); + public static final AsciiString SEC_WEBSOCKET_LOCATION = new AsciiString("sec-websocket-location"); /** - * {@code "Sec-WebSocket-Protocol"} + * {@code "sec-websocket-origin"} */ - public static final AsciiString SEC_WEBSOCKET_PROTOCOL = new AsciiString("Sec-WebSocket-Protocol"); + public static final AsciiString SEC_WEBSOCKET_ORIGIN = new AsciiString("sec-websocket-origin"); /** - * {@code "Sec-WebSocket-Version"} + * {@code "sec-websocket-protocol"} */ - public static final AsciiString SEC_WEBSOCKET_VERSION = new AsciiString("Sec-WebSocket-Version"); + public static final AsciiString SEC_WEBSOCKET_PROTOCOL = new AsciiString("sec-websocket-protocol"); /** - * {@code "Sec-WebSocket-Key"} + * {@code "sec-websocket-version"} */ - public static final AsciiString SEC_WEBSOCKET_KEY = new AsciiString("Sec-WebSocket-Key"); + public static final AsciiString SEC_WEBSOCKET_VERSION = new AsciiString("sec-websocket-version"); /** - * {@code "Sec-WebSocket-Accept"} + * {@code "sec-websocket-key"} */ - public static final AsciiString SEC_WEBSOCKET_ACCEPT = new AsciiString("Sec-WebSocket-Accept"); + public static final AsciiString SEC_WEBSOCKET_KEY = new AsciiString("sec-websocket-key"); /** - * {@code "Sec-WebSocket-Protocol"} + * {@code "sec-websocket-accept"} */ - public static final AsciiString SEC_WEBSOCKET_EXTENSIONS = new AsciiString("Sec-WebSocket-Extensions"); + public static final AsciiString SEC_WEBSOCKET_ACCEPT = new AsciiString("sec-websocket-accept"); /** - * {@code "Server"} + * {@code "sec-websocket-protocol"} */ - public static final AsciiString SERVER = new AsciiString("Server"); + public static final AsciiString SEC_WEBSOCKET_EXTENSIONS = new AsciiString("sec-websocket-extensions"); /** - * {@code "Set-Cookie"} + * {@code "server"} */ - public static final AsciiString SET_COOKIE = new AsciiString("Set-Cookie"); + public static final AsciiString SERVER = new AsciiString("server"); /** - * {@code "Set-Cookie2"} + * {@code "set-cookie"} */ - public static final AsciiString SET_COOKIE2 = new AsciiString("Set-Cookie2"); + public static final AsciiString SET_COOKIE = new AsciiString("set-cookie"); /** - * {@code "TE"} + * {@code "set-cookie2"} */ - public static final AsciiString TE = new AsciiString("TE"); + public static final AsciiString SET_COOKIE2 = new AsciiString("set-cookie2"); /** - * {@code "Trailer"} + * {@code "te"} */ - public static final AsciiString TRAILER = new AsciiString("Trailer"); + public static final AsciiString TE = new AsciiString("te"); /** - * {@code "Transfer-Encoding"} + * {@code "trailer"} */ - public static final AsciiString TRANSFER_ENCODING = new AsciiString("Transfer-Encoding"); + public static final AsciiString TRAILER = new AsciiString("trailer"); /** - * {@code "Upgrade"} + * {@code "transfer-encoding"} */ - public static final AsciiString UPGRADE = new AsciiString("Upgrade"); + public static final AsciiString TRANSFER_ENCODING = new AsciiString("transfer-encoding"); /** - * {@code "User-Agent"} + * {@code "upgrade"} */ - public static final AsciiString USER_AGENT = new AsciiString("User-Agent"); + public static final AsciiString UPGRADE = new AsciiString("upgrade"); /** - * {@code "Vary"} + * {@code "user-agent"} */ - public static final AsciiString VARY = new AsciiString("Vary"); + public static final AsciiString USER_AGENT = new AsciiString("user-agent"); /** - * {@code "Via"} + * {@code "vary"} */ - public static final AsciiString VIA = new AsciiString("Via"); + public static final AsciiString VARY = new AsciiString("vary"); /** - * {@code "Warning"} + * {@code "via"} */ - public static final AsciiString WARNING = new AsciiString("Warning"); + public static final AsciiString VIA = new AsciiString("via"); /** - * {@code "WebSocket-Location"} + * {@code "warning"} */ - public static final AsciiString WEBSOCKET_LOCATION = new AsciiString("WebSocket-Location"); + public static final AsciiString WARNING = new AsciiString("warning"); /** - * {@code "WebSocket-Origin"} + * {@code "websocket-location"} */ - public static final AsciiString WEBSOCKET_ORIGIN = new AsciiString("WebSocket-Origin"); + public static final AsciiString WEBSOCKET_LOCATION = new AsciiString("websocket-location"); /** - * {@code "WebSocket-Protocol"} + * {@code "websocket-origin"} */ - public static final AsciiString WEBSOCKET_PROTOCOL = new AsciiString("WebSocket-Protocol"); + public static final AsciiString WEBSOCKET_ORIGIN = new AsciiString("websocket-origin"); /** - * {@code "WWW-Authenticate"} + * {@code "websocket-protocol"} */ - public static final AsciiString WWW_AUTHENTICATE = new AsciiString("WWW-Authenticate"); + public static final AsciiString WEBSOCKET_PROTOCOL = new AsciiString("websocket-protocol"); /** - * {@code "Keep-Alive"} + * {@code "www-authenticate"} + */ + public static final AsciiString WWW_AUTHENTICATE = new AsciiString("www-authenticate"); + /** + * {@code "keep-alive"} * @deprecated use {@link #CONNECTION} */ @Deprecated - public static final AsciiString KEEP_ALIVE = new AsciiString("Keep-Alive"); + public static final AsciiString KEEP_ALIVE = new AsciiString("keep-alive"); /** - * {@code "Proxy-Connection"} + * {@code "proxy-connection"} * @deprecated use {@link #CONNECTION} */ @Deprecated - public static final AsciiString PROXY_CONNECTION = new AsciiString("Proxy-Connection"); + public static final AsciiString PROXY_CONNECTION = new AsciiString("proxy-connection"); private Names() { } @@ -357,8 +362,16 @@ public interface HttpHeaders extends TextHeaders { /** * {@code "application/x-www-form-urlencoded"} */ - public static final AsciiString APPLICATION_X_WWW_FORM_URLENCODED = + public static final AsciiString APPLICATION_X_WWW_FORM_URLENCODED = new AsciiString("application/x-www-form-urlencoded"); + /** + * {@code "application/octet-stream"} + */ + public static final AsciiString APPLICATION_OCTET_STREAM = new AsciiString("application/octet-stream"); + /** + * {@code "text/plain"} + */ + public static final AsciiString TEXT_PLAIN = new AsciiString("text/plain"); /** * {@code "base64"} */ @@ -435,6 +448,10 @@ public interface HttpHeaders extends TextHeaders { * {@code "multipart/form-data"} */ public static final AsciiString MULTIPART_FORM_DATA = new AsciiString("multipart/form-data"); + /** + * {@code "multipart/mixed"} + */ + public static final AsciiString MULTIPART_MIXED = new AsciiString("multipart/mixed"); /** * {@code "must-revalidate"} */ @@ -491,38 +508,129 @@ public interface HttpHeaders extends TextHeaders { * {@code "WebSocket"} */ public static final AsciiString WEBSOCKET = new AsciiString("WebSocket"); + /** + * {@code "name"} + * See {@link #HttpHeaders.Names.CONTENT_DISPOSITION} + */ + public static final AsciiString NAME = new AsciiString("name"); + /** + * {@code "filename"} + * See {@link #HttpHeaders.Names.CONTENT_DISPOSITION} + */ + public static final AsciiString FILENAME = new AsciiString("filename"); + /** + * {@code "form-data"} + * See {@link #HttpHeaders.Names.CONTENT_DISPOSITION} + */ + public static final AsciiString FORM_DATA = new AsciiString("form-data"); + /** + * {@code "attachment"} + * See {@link #HttpHeaders.Names.CONTENT_DISPOSITION} + */ + public static final AsciiString ATTACHMENT = new AsciiString("attachment"); + /** + * {@code "file"} + * See {@link #HttpHeaders.Names.CONTENT_DISPOSITION} + */ + public static final AsciiString FILE = new AsciiString("file"); private Values() { } } @Override - HttpHeaders add(CharSequence name, Object value); + HttpHeaders add(CharSequence name, CharSequence value); @Override - HttpHeaders add(CharSequence name, Iterable values); + HttpHeaders add(CharSequence name, Iterable values); @Override - HttpHeaders add(CharSequence name, Object... values); + HttpHeaders add(CharSequence name, CharSequence... values); + + @Override + HttpHeaders addObject(CharSequence name, Object value); + + @Override + HttpHeaders addObject(CharSequence name, Iterable values); + + @Override + HttpHeaders addObject(CharSequence name, Object... values); + + @Override + HttpHeaders addBoolean(CharSequence name, boolean value); + + @Override + HttpHeaders addByte(CharSequence name, byte value); + + @Override + HttpHeaders addChar(CharSequence name, char value); + + @Override + HttpHeaders addShort(CharSequence name, short value); + + @Override + HttpHeaders addInt(CharSequence name, int value); + + @Override + HttpHeaders addLong(CharSequence name, long value); + + @Override + HttpHeaders addFloat(CharSequence name, float value); + + @Override + HttpHeaders addDouble(CharSequence name, double value); @Override HttpHeaders add(TextHeaders headers); @Override - HttpHeaders set(CharSequence name, Object value); + HttpHeaders set(CharSequence name, CharSequence value); @Override - HttpHeaders set(CharSequence name, Iterable values); + HttpHeaders set(CharSequence name, Iterable values); @Override - HttpHeaders set(CharSequence name, Object... values); + HttpHeaders set(CharSequence name, CharSequence... values); + + @Override + HttpHeaders setObject(CharSequence name, Object value); + + @Override + HttpHeaders setObject(CharSequence name, Iterable values); + + @Override + HttpHeaders setObject(CharSequence name, Object... values); + + @Override + HttpHeaders setBoolean(CharSequence name, boolean value); + + @Override + HttpHeaders setByte(CharSequence name, byte value); + + @Override + HttpHeaders setChar(CharSequence name, char value); + + @Override + HttpHeaders setShort(CharSequence name, short value); + + @Override + HttpHeaders setInt(CharSequence name, int value); + + @Override + HttpHeaders setLong(CharSequence name, long value); + + @Override + HttpHeaders setFloat(CharSequence name, float value); + + @Override + HttpHeaders setDouble(CharSequence name, double value); @Override HttpHeaders set(TextHeaders headers); @Override - HttpHeaders clear(); + HttpHeaders setAll(TextHeaders headers); @Override - HttpHeaders forEachEntry(TextHeaderProcessor processor); + HttpHeaders clear(); } 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 fccd251509..6e2e344ec9 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 @@ -18,9 +18,11 @@ package io.netty.handler.codec.http; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.AsciiString; -import io.netty.handler.codec.TextHeaderProcessor; +import io.netty.handler.codec.TextHeaders.EntryVisitor; -final class HttpHeadersEncoder implements TextHeaderProcessor { +import java.util.Map.Entry; + +final class HttpHeadersEncoder implements EntryVisitor { private final ByteBuf buf; @@ -29,7 +31,9 @@ final class HttpHeadersEncoder implements TextHeaderProcessor { } @Override - public boolean process(CharSequence name, CharSequence value) throws Exception { + public boolean visit(Entry entry) throws Exception { + final CharSequence name = entry.getKey(); + final CharSequence value = entry.getValue(); final ByteBuf buf = this.buf; final int nameLen = name.length(); final int valueLen = value.length(); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectAggregator.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectAggregator.java index 3fd375859c..ef962647e5 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectAggregator.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectAggregator.java @@ -58,7 +58,7 @@ public class HttpObjectAggregator HttpVersion.HTTP_1_1, HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, Unpooled.EMPTY_BUFFER); static { - TOO_LARGE.headers().set(HttpHeaders.Names.CONTENT_LENGTH, 0); + TOO_LARGE.headers().setInt(HttpHeaders.Names.CONTENT_LENGTH, 0); } /** 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 567202a063..68bbd69e1d 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 @@ -550,11 +550,15 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder { do { char firstChar = line.charAt(0); if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) { - List current = trailer.trailingHeaders().getAll(lastHeader); + List current = trailer.trailingHeaders().getAll(lastHeader); if (!current.isEmpty()) { int lastPos = current.size() - 1; - String newString = current.get(lastPos) + line.toString().trim(); - current.set(lastPos, newString); + String lineTrimmed = line.toString().trim(); + CharSequence currentLastPos = current.get(lastPos); + StringBuilder b = new StringBuilder(currentLastPos.length() + lineTrimmed.length()); + b.append(currentLastPos); + b.append(lineTrimmed); + current.set(lastPos, b.toString()); } else { // Content-Length, Transfer-Encoding, or Trailer } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java index 0918929d2a..9a7d0b3b2d 100755 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java @@ -20,6 +20,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.FileRegion; import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.util.CharsetUtil; +import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; import java.util.List; @@ -137,7 +138,12 @@ public abstract class HttpObjectEncoder extends MessageTo } else { ByteBuf buf = ctx.alloc().buffer(); buf.writeBytes(ZERO_CRLF); - headers.forEachEntry(new HttpHeadersEncoder(buf)); + try { + headers.forEachEntry(new HttpHeadersEncoder(buf)); + } catch (Exception ex) { + buf.release(); + PlatformDependent.throwException(ex); + } buf.writeBytes(CRLF); out.add(buf); } 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 0e93a76f02..fad6fcee7a 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 @@ -14,9 +14,15 @@ */ package io.netty.handler.codec.http; +import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION; +import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH; +import static io.netty.handler.codec.http.HttpHeaders.Names.UPGRADE; +import static io.netty.handler.codec.http.HttpResponseStatus.SWITCHING_PROTOCOLS; +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; 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.ReferenceCountUtil; import io.netty.util.ReferenceCounted; @@ -29,11 +35,6 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; -import static io.netty.handler.codec.http.HttpHeaders.Names.*; -import static io.netty.handler.codec.http.HttpResponseStatus.*; -import static io.netty.handler.codec.http.HttpVersion.*; -import static java.lang.String.*; - /** * A server-side handler that receives HTTP requests and optionally performs a protocol switch if * the requested protocol is supported. Once an upgrade is performed, this handler removes itself @@ -249,7 +250,7 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator { */ private boolean upgrade(final ChannelHandlerContext ctx, final FullHttpRequest request) { // Select the best protocol based on those requested in the UPGRADE header. - String upgradeHeader = request.headers().get(UPGRADE); + CharSequence upgradeHeader = request.headers().get(UPGRADE); final UpgradeCodec upgradeCodec = selectUpgradeCodec(upgradeHeader); if (upgradeCodec == null) { // None of the requested protocols are supported, don't upgrade. @@ -257,15 +258,15 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator { } // Make sure the CONNECTION header is present. - String connectionHeader = request.headers().get(CONNECTION); + CharSequence connectionHeader = request.headers().get(CONNECTION); if (connectionHeader == null) { return false; } // Make sure the CONNECTION header contains UPGRADE as well as all protocol-specific headers. Collection requiredHeaders = upgradeCodec.requiredUpgradeHeaders(); - Set values = splitHeader(connectionHeader); - if (!values.contains(UPGRADE.toString()) || !values.containsAll(requiredHeaders)) { + Set values = splitHeader(connectionHeader); + if (!values.contains(UPGRADE) || !values.containsAll(requiredHeaders)) { return false; } @@ -314,8 +315,8 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator { * Looks up the most desirable supported upgrade codec from the list of choices in the UPGRADE * header. If no suitable codec was found, returns {@code null}. */ - private UpgradeCodec selectUpgradeCodec(String upgradeHeader) { - Set requestedProtocols = splitHeader(upgradeHeader); + private UpgradeCodec selectUpgradeCodec(CharSequence upgradeHeader) { + Set requestedProtocols = splitHeader(upgradeHeader); // Retain only the protocols that are in the protocol map. Maintain the original insertion // order into the protocolMap, so that the first one in the remaining set is the most @@ -345,9 +346,9 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator { * Splits a comma-separated header value. The returned set is case-insensitive and contains each * part with whitespace removed. */ - private static Set splitHeader(String header) { + private static Set splitHeader(CharSequence header) { StringBuilder builder = new StringBuilder(header.length()); - Set protocols = new TreeSet(CASE_INSENSITIVE_ORDER); + Set protocols = new TreeSet(AsciiString.CHARSEQUENCE_CASE_INSENSITIVE_ORDER); for (int i = 0; i < header.length(); ++i) { char c = header.charAt(i); if (Character.isWhitespace(c)) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsConfig.java b/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsConfig.java index c212a0dc2e..2a271dffad 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsConfig.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsConfig.java @@ -209,9 +209,9 @@ public final class CorsConfig { for (Entry> entry : this.preflightHeaders.entrySet()) { final Object value = getValue(entry.getValue()); if (value instanceof Iterable) { - preflightHeaders.add(entry.getKey(), (Iterable) value); + preflightHeaders.addObject(entry.getKey(), (Iterable) value); } else { - preflightHeaders.add(entry.getKey(), value); + preflightHeaders.addObject(entry.getKey(), value); } } return preflightHeaders; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsHandler.java index bd13ed69c8..f088e83dd7 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsHandler.java @@ -86,7 +86,7 @@ public class CorsHandler extends ChannelHandlerAdapter { } private boolean setOrigin(final HttpResponse response) { - final String origin = request.headers().get(ORIGIN); + final CharSequence origin = request.headers().get(ORIGIN); if (origin != null) { if ("null".equals(origin) && config.isNullOriginAllowed()) { setAnyOrigin(response); @@ -116,7 +116,7 @@ public class CorsHandler extends ChannelHandlerAdapter { return true; } - final String origin = request.headers().get(ORIGIN); + final CharSequence origin = request.headers().get(ORIGIN); if (origin == null) { // Not a CORS request so we cannot validate it. It may be a non CORS request. return true; @@ -141,7 +141,7 @@ public class CorsHandler extends ChannelHandlerAdapter { setOrigin(response, "*"); } - private static void setOrigin(final HttpResponse response, final String origin) { + private static void setOrigin(final HttpResponse response, final CharSequence origin) { response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN, origin); } @@ -165,7 +165,7 @@ public class CorsHandler extends ChannelHandlerAdapter { } private void setAllowMethods(final HttpResponse response) { - response.headers().set(ACCESS_CONTROL_ALLOW_METHODS, config.allowedRequestMethods()); + response.headers().setObject(ACCESS_CONTROL_ALLOW_METHODS, config.allowedRequestMethods()); } private void setAllowHeaders(final HttpResponse response) { @@ -173,7 +173,7 @@ public class CorsHandler extends ChannelHandlerAdapter { } private void setMaxAge(final HttpResponse response) { - response.headers().set(ACCESS_CONTROL_MAX_AGE, config.maxAge()); + response.headers().setLong(ACCESS_CONTROL_MAX_AGE, config.maxAge()); } @Override diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostBodyUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostBodyUtil.java index 7076a8029c..169b2d872c 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostBodyUtil.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostBodyUtil.java @@ -16,6 +16,7 @@ package io.netty.handler.codec.http.multipart; import io.netty.buffer.ByteBuf; +import io.netty.handler.codec.http.HttpHeaders; import io.netty.util.CharsetUtil; import java.nio.charset.Charset; @@ -29,31 +30,31 @@ final class HttpPostBodyUtil { /** * HTTP content disposition header name. */ - public static final String CONTENT_DISPOSITION = "Content-Disposition"; + public static final String CONTENT_DISPOSITION = HttpHeaders.Names.CONTENT_DISPOSITION.toString(); - public static final String NAME = "name"; + public static final String NAME = HttpHeaders.Values.NAME.toString(); - public static final String FILENAME = "filename"; + public static final String FILENAME = HttpHeaders.Values.FILENAME.toString(); /** * Content-disposition value for form data. */ - public static final String FORM_DATA = "form-data"; + public static final String FORM_DATA = HttpHeaders.Values.FORM_DATA.toString(); /** * Content-disposition value for file attachment. */ - public static final String ATTACHMENT = "attachment"; + public static final String ATTACHMENT = HttpHeaders.Values.ATTACHMENT.toString(); /** * Content-disposition value for file attachment. */ - public static final String FILE = "file"; + public static final String FILE = HttpHeaders.Values.FILE.toString(); /** * HTTP content type body attribute for multiple uploads. */ - public static final String MULTIPART_MIXED = "multipart/mixed"; + public static final String MULTIPART_MIXED = HttpHeaders.Values.MULTIPART_MIXED.toString(); /** * Charset for 8BIT @@ -68,12 +69,12 @@ final class HttpPostBodyUtil { /** * Default Content-Type in binary form */ - public static final String DEFAULT_BINARY_CONTENT_TYPE = "application/octet-stream"; + public static final String DEFAULT_BINARY_CONTENT_TYPE = HttpHeaders.Values.APPLICATION_OCTET_STREAM.toString(); /** * Default Content-Type in Text form */ - public static final String DEFAULT_TEXT_CONTENT_TYPE = "text/plain"; + public static final String DEFAULT_TEXT_CONTENT_TYPE = HttpHeaders.Values.TEXT_PLAIN.toString(); /** * Allowed mechanism for multipart diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java index 52142f074b..4f126ff363 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java @@ -181,7 +181,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest this.factory = factory; // Fill default values - setMultipart(this.request.headers().get(HttpHeaders.Names.CONTENT_TYPE)); + setMultipart(this.request.headers().getAndConvert(HttpHeaders.Names.CONTENT_TYPE)); if (request instanceof HttpContent) { // Offer automatically if the given request is als type of HttpContent // See #1089 diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java index ee4f9b3d83..0c1aa458ca 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java @@ -140,7 +140,7 @@ public class HttpPostRequestDecoder implements InterfaceHttpPostRequestDecoder { */ public static boolean isMultipart(HttpRequest request) { if (request.headers().contains(HttpHeaders.Names.CONTENT_TYPE)) { - return getMultipartDataBoundary(request.headers().get(HttpHeaders.Names.CONTENT_TYPE)) != null; + return getMultipartDataBoundary(request.headers().getAndConvert(HttpHeaders.Names.CONTENT_TYPE)) != null; } else { return false; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java index 71862573d6..74e6067467 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java @@ -711,8 +711,8 @@ public class HttpPostRequestEncoder implements ChunkedInput { } HttpHeaders headers = request.headers(); - List contentTypes = headers.getAll(HttpHeaders.Names.CONTENT_TYPE); - List transferEncoding = headers.getAll(HttpHeaders.Names.TRANSFER_ENCODING); + List contentTypes = headers.getAllAndConvert(HttpHeaders.Names.CONTENT_TYPE); + List transferEncoding = headers.getAll(HttpHeaders.Names.TRANSFER_ENCODING); if (contentTypes != null) { headers.remove(HttpHeaders.Names.CONTENT_TYPE); for (String contentType : contentTypes) { @@ -747,7 +747,7 @@ public class HttpPostRequestEncoder implements ChunkedInput { isChunked = true; if (transferEncoding != null) { headers.remove(HttpHeaders.Names.TRANSFER_ENCODING); - for (String v : transferEncoding) { + for (CharSequence v : transferEncoding) { if (AsciiString.equalsIgnoreCase(v, HttpHeaders.Values.CHUNKED)) { // ignore } else { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java index 59e3ed597a..b3ccf300f3 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java @@ -203,7 +203,7 @@ public abstract class WebSocketClientHandshaker { // Verify the subprotocol that we received from the server. // This must be one of our expected subprotocols - or null/empty if we didn't want to speak a subprotocol - String receivedProtocol = response.headers().get(HttpHeaders.Names.SEC_WEBSOCKET_PROTOCOL); + String receivedProtocol = response.headers().getAndConvert(HttpHeaders.Names.SEC_WEBSOCKET_PROTOCOL); receivedProtocol = receivedProtocol != null ? receivedProtocol.trim() : null; String expectedProtocol = expectedSubprotocol != null ? expectedSubprotocol : ""; boolean protocolValid = false; 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 2bd4f4234c..8b84c53e7e 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 @@ -163,7 +163,7 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker { // Set Content-Length to workaround some known defect. // See also: http://www.ietf.org/mail-archive/web/hybi/current/msg02149.html - headers.set(Names.CONTENT_LENGTH, key3.length); + headers.setInt(Names.CONTENT_LENGTH, key3.length); request.content().writeBytes(key3); return request; } @@ -198,13 +198,13 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker { HttpHeaders headers = response.headers(); - String upgrade = headers.get(Names.UPGRADE); + CharSequence upgrade = headers.get(Names.UPGRADE); if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); } - String connection = headers.get(Names.CONNECTION); + CharSequence connection = headers.get(Names.CONNECTION); if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) { throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java index 5a62126c28..76b690316c 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java @@ -173,17 +173,17 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker { throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.status()); } - String upgrade = headers.get(Names.UPGRADE); + CharSequence upgrade = headers.get(Names.UPGRADE); if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); } - String connection = headers.get(Names.CONNECTION); + CharSequence connection = headers.get(Names.CONNECTION); if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) { throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection); } - String accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT); + CharSequence accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT); if (accept == null || !accept.equals(expectedChallengeResponseString)) { throw new WebSocketHandshakeException(String.format( "Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString)); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java index f614522b6f..821d2d31f5 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java @@ -174,17 +174,17 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker { throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.status()); } - String upgrade = headers.get(Names.UPGRADE); + CharSequence upgrade = headers.get(Names.UPGRADE); if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); } - String connection = headers.get(Names.CONNECTION); + CharSequence connection = headers.get(Names.CONNECTION); if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) { throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection); } - String accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT); + CharSequence accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT); if (accept == null || !accept.equals(expectedChallengeResponseString)) { throw new WebSocketHandshakeException(String.format( "Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString)); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java index 21748899c4..25e25c9bf4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java @@ -184,17 +184,17 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.status()); } - String upgrade = headers.get(Names.UPGRADE); + CharSequence upgrade = headers.get(Names.UPGRADE); if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); } - String connection = headers.get(Names.CONNECTION); + CharSequence connection = headers.get(Names.CONNECTION); if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, connection)) { throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection); } - String accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT); + CharSequence accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT); if (accept == null || !accept.equals(expectedChallengeResponseString)) { throw new WebSocketHandshakeException(String.format( "Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString)); 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 6f30d248f3..1f97bcb256 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 @@ -133,7 +133,7 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { // New handshake getMethod with a challenge: res.headers().add(SEC_WEBSOCKET_ORIGIN, req.headers().get(ORIGIN)); res.headers().add(SEC_WEBSOCKET_LOCATION, uri()); - String subprotocols = req.headers().get(SEC_WEBSOCKET_PROTOCOL); + String subprotocols = req.headers().getAndConvert(SEC_WEBSOCKET_PROTOCOL); if (subprotocols != null) { String selectedSubprotocol = selectSubprotocol(subprotocols); if (selectedSubprotocol == null) { @@ -146,8 +146,8 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { } // Calculate the answer of the challenge. - String key1 = req.headers().get(SEC_WEBSOCKET_KEY1); - String key2 = req.headers().get(SEC_WEBSOCKET_KEY2); + String key1 = req.headers().getAndConvert(SEC_WEBSOCKET_KEY1); + String key2 = req.headers().getAndConvert(SEC_WEBSOCKET_KEY2); int a = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key1).replaceAll("")) / BEGINNING_SPACE.matcher(key1).replaceAll("").length()); int b = (int) (Long.parseLong(BEGINNING_DIGIT.matcher(key2).replaceAll("")) / @@ -162,7 +162,7 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { // Old Hixie 75 handshake getMethod with no challenge: res.headers().add(WEBSOCKET_ORIGIN, req.headers().get(ORIGIN)); res.headers().add(WEBSOCKET_LOCATION, uri()); - String protocol = req.headers().get(WEBSOCKET_PROTOCOL); + String protocol = req.headers().getAndConvert(WEBSOCKET_PROTOCOL); if (protocol != null) { res.headers().add(WEBSOCKET_PROTOCOL, selectSubprotocol(protocol)); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java index 3b951474bc..0e76202dcd 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java @@ -105,7 +105,7 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker { res.headers().add(headers); } - String key = req.headers().get(Names.SEC_WEBSOCKET_KEY); + CharSequence key = req.headers().get(Names.SEC_WEBSOCKET_KEY); if (key == null) { throw new WebSocketHandshakeException("not a WebSocket request: missing key"); } @@ -120,7 +120,7 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker { res.headers().add(Names.UPGRADE, WEBSOCKET); res.headers().add(Names.CONNECTION, Names.UPGRADE); res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept); - String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL); + String subprotocols = req.headers().getAndConvert(Names.SEC_WEBSOCKET_PROTOCOL); if (subprotocols != null) { String selectedSubprotocol = selectSubprotocol(subprotocols); if (selectedSubprotocol == null) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java index 0ffaae58c9..c7882f24e2 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java @@ -104,7 +104,7 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker { res.headers().add(headers); } - String key = req.headers().get(Names.SEC_WEBSOCKET_KEY); + CharSequence key = req.headers().get(Names.SEC_WEBSOCKET_KEY); if (key == null) { throw new WebSocketHandshakeException("not a WebSocket request: missing key"); } @@ -119,7 +119,7 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker { res.headers().add(Names.UPGRADE, WEBSOCKET); res.headers().add(Names.CONNECTION, Names.UPGRADE); res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept); - String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL); + String subprotocols = req.headers().getAndConvert(Names.SEC_WEBSOCKET_PROTOCOL); if (subprotocols != null) { String selectedSubprotocol = selectSubprotocol(subprotocols); if (selectedSubprotocol == null) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java index 07652c6434..937a8df8cd 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java @@ -102,7 +102,7 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker { res.headers().add(headers); } - String key = req.headers().get(Names.SEC_WEBSOCKET_KEY); + CharSequence key = req.headers().get(Names.SEC_WEBSOCKET_KEY); if (key == null) { throw new WebSocketHandshakeException("not a WebSocket request: missing key"); } @@ -117,7 +117,7 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker { res.headers().add(Names.UPGRADE, WEBSOCKET); res.headers().add(Names.CONNECTION, Names.UPGRADE); res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept); - String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL); + String subprotocols = req.headers().getAndConvert(Names.SEC_WEBSOCKET_PROTOCOL); if (subprotocols != null) { String selectedSubprotocol = selectSubprotocol(subprotocols); if (selectedSubprotocol == null) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java index 5dc49da672..d140680e4c 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java @@ -86,7 +86,7 @@ public class WebSocketServerHandshakerFactory { */ public WebSocketServerHandshaker newHandshaker(HttpRequest req) { - String version = req.headers().get(Names.SEC_WEBSOCKET_VERSION); + CharSequence version = req.headers().get(Names.SEC_WEBSOCKET_VERSION); if (version != null) { if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) { // Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification). diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandler.java index 41c8f5b0a1..d225e3c119 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandler.java @@ -63,7 +63,7 @@ public class WebSocketClientExtensionHandler extends ChannelHandlerAdapter { public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { if (msg instanceof HttpRequest && WebSocketExtensionUtil.isWebsocketUpgrade((HttpRequest) msg)) { HttpRequest request = (HttpRequest) msg; - String headerValue = request.headers().get(HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS); + String headerValue = request.headers().getAndConvert(HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS); for (WebSocketClientExtensionHandshaker extentionHandshaker : extensionHandshakers) { WebSocketExtensionData extensionData = extentionHandshaker.newRequestData(); @@ -84,7 +84,7 @@ public class WebSocketClientExtensionHandler extends ChannelHandlerAdapter { HttpResponse response = (HttpResponse) msg; if (WebSocketExtensionUtil.isWebsocketUpgrade(response)) { - String extensionsHeader = response.headers().get(HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS); + String extensionsHeader = response.headers().getAndConvert(HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS); if (extensionsHeader != null) { List extensions = diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandler.java index 9466724383..5a69bebcd8 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandler.java @@ -69,7 +69,7 @@ public class WebSocketServerExtensionHandler extends ChannelHandlerAdapter { HttpRequest request = (HttpRequest) msg; if (WebSocketExtensionUtil.isWebsocketUpgrade(request)) { - String extensionsHeader = request.headers().get(HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS); + String extensionsHeader = request.headers().getAndConvert(HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS); if (extensionsHeader != null) { List extensions = @@ -107,7 +107,7 @@ public class WebSocketServerExtensionHandler extends ChannelHandlerAdapter { if (msg instanceof HttpResponse && WebSocketExtensionUtil.isWebsocketUpgrade((HttpResponse) msg) && validExtensions != null) { HttpResponse response = (HttpResponse) msg; - String headerValue = response.headers().get(HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS); + String headerValue = response.headers().getAndConvert(HttpHeaders.Names.SEC_WEBSOCKET_EXTENSIONS); for (WebSocketServerExtension extension : validExtensions) { WebSocketExtensionData extensionData = extension.newReponseData(); 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 a00aab5579..d53a896db3 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeaders.java @@ -17,60 +17,129 @@ package io.netty.handler.codec.spdy; import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.DefaultTextHeaders; -import io.netty.handler.codec.TextHeaderProcessor; +import io.netty.handler.codec.Headers; import io.netty.handler.codec.TextHeaders; import java.util.Locale; - public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeaders { - @Override - protected CharSequence convertName(CharSequence name) { - name = super.convertName(name); - if (name instanceof AsciiString) { - name = ((AsciiString) name).toLowerCase(); - } else { - name = name.toString().toLowerCase(Locale.US); + private static final Headers.ValueConverter SPDY_VALUE_CONVERTER = + new DefaultTextValueTypeConverter() { + @Override + public CharSequence convert(Object value) { + CharSequence seq; + if (value instanceof CharSequence) { + seq = (CharSequence) value; + } else { + seq = value.toString(); + } + + SpdyCodecUtil.validateHeaderValue(seq); + return seq; } - SpdyCodecUtil.validateHeaderName(name); - return name; + }; + + private static final NameConverter SPDY_NAME_CONVERTER = new NameConverter() { + @Override + public CharSequence convertName(CharSequence name) { + if (name instanceof AsciiString) { + name = ((AsciiString) name).toLowerCase(); + } else { + name = name.toString().toLowerCase(Locale.US); + } + SpdyCodecUtil.validateHeaderName(name); + return name; + } + }; + + public DefaultSpdyHeaders() { + super(true, SPDY_VALUE_CONVERTER, SPDY_NAME_CONVERTER); } @Override - protected CharSequence convertValue(Object value) { - if (value == null) { - throw new NullPointerException("value"); - } - - CharSequence seq; - if (value instanceof CharSequence) { - seq = (CharSequence) value; - } else { - seq = value.toString(); - } - - SpdyCodecUtil.validateHeaderValue(seq); - return seq; - } - - @Override - public SpdyHeaders add(CharSequence name, Object value) { + public SpdyHeaders add(CharSequence name, CharSequence value) { super.add(name, value); return this; } @Override - public SpdyHeaders add(CharSequence name, Iterable values) { + public SpdyHeaders add(CharSequence name, Iterable values) { super.add(name, values); return this; } @Override - public SpdyHeaders add(CharSequence name, Object... values) { + public SpdyHeaders add(CharSequence name, CharSequence... values) { super.add(name, values); return this; } + @Override + public SpdyHeaders addObject(CharSequence name, Object value) { + super.addObject(name, value); + return this; + } + + @Override + public SpdyHeaders addObject(CharSequence name, Iterable values) { + super.addObject(name, values); + return this; + } + + @Override + public SpdyHeaders addObject(CharSequence name, Object... values) { + super.addObject(name, values); + return this; + } + + @Override + public SpdyHeaders addBoolean(CharSequence name, boolean value) { + super.addBoolean(name, value); + return this; + } + + @Override + public SpdyHeaders addChar(CharSequence name, char value) { + super.addChar(name, value); + return this; + } + + @Override + public SpdyHeaders addByte(CharSequence name, byte value) { + super.addByte(name, value); + return this; + } + + @Override + public SpdyHeaders addShort(CharSequence name, short value) { + super.addShort(name, value); + return this; + } + + @Override + public SpdyHeaders addInt(CharSequence name, int value) { + super.addInt(name, value); + return this; + } + + @Override + public SpdyHeaders addLong(CharSequence name, long value) { + super.addLong(name, value); + return this; + } + + @Override + public SpdyHeaders addFloat(CharSequence name, float value) { + super.addFloat(name, value); + return this; + } + + @Override + public SpdyHeaders addDouble(CharSequence name, double value) { + super.addDouble(name, value); + return this; + } + @Override public SpdyHeaders add(TextHeaders headers) { super.add(headers); @@ -78,23 +147,89 @@ public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeader } @Override - public SpdyHeaders set(CharSequence name, Object value) { + public SpdyHeaders set(CharSequence name, CharSequence value) { super.set(name, value); return this; } @Override - public SpdyHeaders set(CharSequence name, Object... values) { + public SpdyHeaders set(CharSequence name, Iterable values) { super.set(name, values); return this; } @Override - public SpdyHeaders set(CharSequence name, Iterable values) { + public SpdyHeaders set(CharSequence name, CharSequence... values) { super.set(name, values); return this; } + @Override + public SpdyHeaders setObject(CharSequence name, Object value) { + super.setObject(name, value); + return this; + } + + @Override + public SpdyHeaders setObject(CharSequence name, Iterable values) { + super.setObject(name, values); + return this; + } + + @Override + public SpdyHeaders setObject(CharSequence name, Object... values) { + super.setObject(name, values); + return this; + } + + @Override + public SpdyHeaders setBoolean(CharSequence name, boolean value) { + super.setBoolean(name, value); + return this; + } + + @Override + public SpdyHeaders setChar(CharSequence name, char value) { + super.setChar(name, value); + return this; + } + + @Override + public SpdyHeaders setByte(CharSequence name, byte value) { + super.setByte(name, value); + return this; + } + + @Override + public SpdyHeaders setShort(CharSequence name, short value) { + super.setShort(name, value); + return this; + } + + @Override + public SpdyHeaders setInt(CharSequence name, int value) { + super.setInt(name, value); + return this; + } + + @Override + public SpdyHeaders setLong(CharSequence name, long value) { + super.setLong(name, value); + return this; + } + + @Override + public SpdyHeaders setFloat(CharSequence name, float value) { + super.setFloat(name, value); + return this; + } + + @Override + public SpdyHeaders setDouble(CharSequence name, double value) { + super.setDouble(name, value); + return this; + } + @Override public SpdyHeaders set(TextHeaders headers) { super.set(headers); @@ -102,14 +237,14 @@ public class DefaultSpdyHeaders extends DefaultTextHeaders implements SpdyHeader } @Override - public SpdyHeaders clear() { - super.clear(); + public SpdyHeaders setAll(TextHeaders headers) { + super.setAll(headers); return this; } @Override - public SpdyHeaders forEachEntry(TextHeaderProcessor processor) { - super.forEachEntry(processor); + public SpdyHeaders clear() { + super.clear(); return this; } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeadersFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeadersFrame.java index 65c05ebdad..e18be4cac7 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeadersFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeadersFrame.java @@ -98,7 +98,7 @@ public class DefaultSpdyHeadersFrame extends DefaultSpdyStreamFrame } protected void appendHeaders(StringBuilder buf) { - for (Map.Entry e: headers()) { + for (Map.Entry e: headers()) { buf.append(" "); buf.append(e.getKey()); buf.append(": "); 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 d2af96c2c0..a5a5692541 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockRawEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockRawEncoder.java @@ -15,14 +15,15 @@ */ package io.netty.handler.codec.spdy; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_MAX_NV_LENGTH; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; +import io.netty.handler.codec.AsciiString; +import io.netty.util.CharsetUtil; import java.util.Set; -import static io.netty.handler.codec.spdy.SpdyCodecUtil.*; - public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder { private final int version; @@ -44,7 +45,7 @@ public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder { @Override public ByteBuf encode(ByteBufAllocator alloc, SpdyHeadersFrame frame) throws Exception { - Set names = frame.headers().names(); + Set names = frame.headers().names(); int numHeaders = names.size(); if (numHeaders == 0) { return Unpooled.EMPTY_BUFFER; @@ -55,15 +56,15 @@ public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder { } ByteBuf headerBlock = alloc.heapBuffer(); writeLengthField(headerBlock, numHeaders); - for (String name: names) { - byte[] nameBytes = name.getBytes("UTF-8"); + for (CharSequence name: names) { + byte[] nameBytes = AsciiString.getBytes(name, CharsetUtil.UTF_8); writeLengthField(headerBlock, nameBytes.length); headerBlock.writeBytes(nameBytes); int savedIndex = headerBlock.writerIndex(); int valueLength = 0; writeLengthField(headerBlock, valueLength); - for (String value: frame.headers().getAll(name)) { - byte[] valueBytes = value.getBytes("UTF-8"); + for (CharSequence value: frame.headers().getAll(name)) { + byte[] valueBytes = AsciiString.getBytes(value, CharsetUtil.UTF_8); if (valueBytes.length > 0) { headerBlock.writeBytes(valueBytes); headerBlock.writeByte(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 ec220db5b5..fa342855f0 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 @@ -16,7 +16,6 @@ package io.netty.handler.codec.spdy; import io.netty.handler.codec.AsciiString; -import io.netty.handler.codec.TextHeaderProcessor; import io.netty.handler.codec.TextHeaders; /** @@ -58,32 +57,98 @@ public interface SpdyHeaders extends TextHeaders { } @Override - SpdyHeaders add(CharSequence name, Object value); + SpdyHeaders add(CharSequence name, CharSequence value); @Override - SpdyHeaders add(CharSequence name, Iterable values); + SpdyHeaders add(CharSequence name, Iterable values); @Override - SpdyHeaders add(CharSequence name, Object... values); + SpdyHeaders add(CharSequence name, CharSequence... values); + + @Override + SpdyHeaders addObject(CharSequence name, Object value); + + @Override + SpdyHeaders addObject(CharSequence name, Iterable values); + + @Override + SpdyHeaders addObject(CharSequence name, Object... values); + + @Override + SpdyHeaders addBoolean(CharSequence name, boolean value); + + @Override + SpdyHeaders addByte(CharSequence name, byte value); + + @Override + SpdyHeaders addChar(CharSequence name, char value); + + @Override + SpdyHeaders addShort(CharSequence name, short value); + + @Override + SpdyHeaders addInt(CharSequence name, int value); + + @Override + SpdyHeaders addLong(CharSequence name, long value); + + @Override + SpdyHeaders addFloat(CharSequence name, float value); + + @Override + SpdyHeaders addDouble(CharSequence name, double value); @Override SpdyHeaders add(TextHeaders headers); @Override - SpdyHeaders set(CharSequence name, Object value); + SpdyHeaders set(CharSequence name, CharSequence value); @Override - SpdyHeaders set(CharSequence name, Iterable values); + SpdyHeaders set(CharSequence name, Iterable values); @Override - SpdyHeaders set(CharSequence name, Object... values); + SpdyHeaders set(CharSequence name, CharSequence... values); + + @Override + SpdyHeaders setBoolean(CharSequence name, boolean value); + + @Override + SpdyHeaders setByte(CharSequence name, byte value); + + @Override + SpdyHeaders setChar(CharSequence name, char value); + + @Override + SpdyHeaders setShort(CharSequence name, short value); + + @Override + SpdyHeaders setInt(CharSequence name, int value); + + @Override + SpdyHeaders setLong(CharSequence name, long value); + + @Override + SpdyHeaders setFloat(CharSequence name, float value); + + @Override + SpdyHeaders setDouble(CharSequence name, double value); + + @Override + SpdyHeaders setObject(CharSequence name, Object value); + + @Override + SpdyHeaders setObject(CharSequence name, Iterable values); + + @Override + SpdyHeaders setObject(CharSequence name, Object... values); @Override SpdyHeaders set(TextHeaders headers); @Override - SpdyHeaders clear(); + SpdyHeaders setAll(TextHeaders headers); @Override - SpdyHeaders forEachEntry(TextHeaderProcessor processor); + SpdyHeaders clear(); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpDecoder.java index 9d7d6e6c76..4ef02df2ac 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpDecoder.java @@ -145,7 +145,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder { return; } - String URL = spdySynStreamFrame.headers().get(PATH); + CharSequence URL = spdySynStreamFrame.headers().get(PATH); spdySynStreamFrame.headers().remove(PATH); // If a client receives a SYN_STREAM without a 'url' header @@ -171,9 +171,9 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder { createHttpResponse(ctx, spdySynStreamFrame, validateHeaders); // Set the Stream-ID, Associated-To-Stream-ID, Priority, and URL as headers - httpResponseWithEntity.headers().set(Names.STREAM_ID, streamId); - httpResponseWithEntity.headers().set(Names.ASSOCIATED_TO_STREAM_ID, associatedToStreamId); - httpResponseWithEntity.headers().set(Names.PRIORITY, spdySynStreamFrame.priority()); + httpResponseWithEntity.headers().setInt(Names.STREAM_ID, streamId); + httpResponseWithEntity.headers().setInt(Names.ASSOCIATED_TO_STREAM_ID, associatedToStreamId); + httpResponseWithEntity.headers().setByte(Names.PRIORITY, spdySynStreamFrame.priority()); httpResponseWithEntity.headers().set(Names.URL, URL); if (spdySynStreamFrame.isLast()) { @@ -197,8 +197,8 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder { SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId); spdySynReplyFrame.setLast(true); SpdyHeaders frameHeaders = spdySynReplyFrame.headers(); - frameHeaders.set(STATUS, HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE); - frameHeaders.set(VERSION, HttpVersion.HTTP_1_0); + frameHeaders.setInt(STATUS, HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE.code()); + frameHeaders.setObject(VERSION, HttpVersion.HTTP_1_0); ctx.writeAndFlush(spdySynReplyFrame); return; } @@ -207,7 +207,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder { FullHttpRequest httpRequestWithEntity = createHttpRequest(spdyVersion, spdySynStreamFrame); // Set the Stream-ID as a header - httpRequestWithEntity.headers().set(Names.STREAM_ID, streamId); + httpRequestWithEntity.headers().setInt(Names.STREAM_ID, streamId); if (spdySynStreamFrame.isLast()) { out.add(httpRequestWithEntity); @@ -222,8 +222,8 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder { SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId); spdySynReplyFrame.setLast(true); SpdyHeaders frameHeaders = spdySynReplyFrame.headers(); - frameHeaders.set(STATUS, HttpResponseStatus.BAD_REQUEST); - frameHeaders.set(VERSION, HttpVersion.HTTP_1_0); + frameHeaders.setInt(STATUS, HttpResponseStatus.BAD_REQUEST.code()); + frameHeaders.setObject(VERSION, HttpVersion.HTTP_1_0); ctx.writeAndFlush(spdySynReplyFrame); } } @@ -246,7 +246,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder { FullHttpResponse httpResponseWithEntity = createHttpResponse(ctx, spdySynReplyFrame, validateHeaders); // Set the Stream-ID as a header - httpResponseWithEntity.headers().set(Names.STREAM_ID, streamId); + httpResponseWithEntity.headers().setInt(Names.STREAM_ID, streamId); if (spdySynReplyFrame.isLast()) { HttpHeaderUtil.setContentLength(httpResponseWithEntity, 0); @@ -276,7 +276,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder { // Ignore trailers in a truncated HEADERS frame. if (!spdyHeadersFrame.isTruncated()) { - for (Map.Entry e: spdyHeadersFrame.headers()) { + for (Map.Entry e: spdyHeadersFrame.headers()) { fullHttpMessage.headers().add(e.getKey(), e.getValue()); } } @@ -327,9 +327,9 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder { throws Exception { // Create the first line of the request from the name/value pairs SpdyHeaders headers = requestFrame.headers(); - HttpMethod method = HttpMethod.valueOf(headers.get(METHOD)); - String url = headers.get(PATH); - HttpVersion httpVersion = HttpVersion.valueOf(headers.get(VERSION)); + HttpMethod method = HttpMethod.valueOf(headers.getAndConvert(METHOD)); + String url = headers.getAndConvert(PATH); + HttpVersion httpVersion = HttpVersion.valueOf(headers.getAndConvert(VERSION)); headers.remove(METHOD); headers.remove(PATH); headers.remove(VERSION); @@ -340,11 +340,11 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder { headers.remove(SCHEME); // Replace the SPDY host header with the HTTP host header - String host = headers.get(HOST); + CharSequence host = headers.get(HOST); headers.remove(HOST); req.headers().set(HttpHeaders.Names.HOST, host); - for (Map.Entry e: requestFrame.headers()) { + for (Map.Entry e: requestFrame.headers()) { req.headers().add(e.getKey(), e.getValue()); } @@ -363,12 +363,12 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder { // Create the first line of the response from the name/value pairs SpdyHeaders headers = responseFrame.headers(); HttpResponseStatus status = HttpResponseStatus.parseLine(headers.get(STATUS)); - HttpVersion version = HttpVersion.valueOf(headers.get(VERSION)); + HttpVersion version = HttpVersion.valueOf(headers.getAndConvert(VERSION)); headers.remove(STATUS); headers.remove(VERSION); FullHttpResponse res = new DefaultFullHttpResponse(version, status, ctx.alloc().buffer(), validateHeaders); - for (Map.Entry e: responseFrame.headers()) { + for (Map.Entry e: responseFrame.headers()) { res.headers().add(e.getKey(), e.getValue()); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpEncoder.java index 9f85be6630..49e946dedb 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpEncoder.java @@ -185,7 +185,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { } else { // Create SPDY HEADERS frame out of trailers SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(currentStreamId); - for (Map.Entry entry: trailers) { + for (Map.Entry entry: trailers) { spdyHeadersFrame.headers().add(entry.getKey(), entry.getValue()); } @@ -212,8 +212,8 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { int streamID = httpHeaders.getInt(Names.STREAM_ID); int associatedToStreamId = httpHeaders.getInt(Names.ASSOCIATED_TO_STREAM_ID, 0); byte priority = (byte) httpHeaders.getInt(Names.PRIORITY, 0); - String URL = httpHeaders.get(Names.URL); - String scheme = httpHeaders.get(Names.SCHEME); + CharSequence URL = httpHeaders.get(Names.URL); + CharSequence scheme = httpHeaders.get(Names.SCHEME); httpHeaders.remove(Names.STREAM_ID); httpHeaders.remove(Names.ASSOCIATED_TO_STREAM_ID); httpHeaders.remove(Names.PRIORITY); @@ -234,21 +234,21 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { SpdyHeaders frameHeaders = spdySynStreamFrame.headers(); if (httpMessage instanceof FullHttpRequest) { HttpRequest httpRequest = (HttpRequest) httpMessage; - frameHeaders.set(METHOD, httpRequest.method()); + frameHeaders.setObject(METHOD, httpRequest.method()); frameHeaders.set(PATH, httpRequest.uri()); - frameHeaders.set(VERSION, httpMessage.protocolVersion()); + frameHeaders.setObject(VERSION, httpMessage.protocolVersion()); } if (httpMessage instanceof HttpResponse) { HttpResponse httpResponse = (HttpResponse) httpMessage; - frameHeaders.set(STATUS, httpResponse.status()); + frameHeaders.setInt(STATUS, httpResponse.status().code()); frameHeaders.set(PATH, URL); - frameHeaders.set(VERSION, httpMessage.protocolVersion()); + frameHeaders.setObject(VERSION, httpMessage.protocolVersion()); spdySynStreamFrame.setUnidirectional(true); } // Replace the HTTP host header with the SPDY host header if (spdyVersion >= 3) { - CharSequence host = httpHeaders.getUnconverted(HttpHeaders.Names.HOST); + CharSequence host = httpHeaders.get(HttpHeaders.Names.HOST); httpHeaders.remove(HttpHeaders.Names.HOST); frameHeaders.set(HOST, host); } @@ -260,7 +260,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { frameHeaders.set(SCHEME, scheme); // Transfer the remaining HTTP headers - for (Map.Entry entry: httpHeaders) { + for (Map.Entry entry: httpHeaders) { frameHeaders.add(entry.getKey(), entry.getValue()); } currentStreamId = spdySynStreamFrame.streamId(); @@ -286,11 +286,11 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID); SpdyHeaders frameHeaders = spdySynReplyFrame.headers(); // Unfold the first line of the response into name/value pairs - frameHeaders.set(STATUS, httpResponse.status()); - frameHeaders.set(VERSION, httpResponse.protocolVersion()); + frameHeaders.setInt(STATUS, httpResponse.status().code()); + frameHeaders.setObject(VERSION, httpResponse.protocolVersion()); // Transfer the remaining HTTP headers - for (Map.Entry entry: httpHeaders) { + for (Map.Entry entry: httpHeaders) { spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue()); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpResponseStreamIdHandler.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpResponseStreamIdHandler.java index 1221c75d5e..4ad32e4dc2 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpResponseStreamIdHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpResponseStreamIdHandler.java @@ -44,7 +44,7 @@ public class SpdyHttpResponseStreamIdHandler extends protected void encode(ChannelHandlerContext ctx, HttpMessage msg, List out) throws Exception { Integer id = ids.poll(); if (id != null && id.intValue() != NO_ID && !msg.headers().contains(SpdyHttpHeaders.Names.STREAM_ID)) { - msg.headers().set(Names.STREAM_ID, id); + msg.headers().setInt(Names.STREAM_ID, id); } out.add(ReferenceCountUtil.retain(msg)); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentCompressorTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentCompressorTest.java index c63f04c596..05bdff7542 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentCompressorTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentCompressorTest.java @@ -197,7 +197,7 @@ public class HttpContentCompressorTest { FullHttpResponse res = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer("Hello, World", CharsetUtil.US_ASCII)); - res.headers().set(Names.CONTENT_LENGTH, res.content().readableBytes()); + res.headers().setInt(Names.CONTENT_LENGTH, res.content().readableBytes()); ch.writeOutbound(res); assertEncodedResponse(ch); @@ -309,8 +309,8 @@ public class HttpContentCompressorTest { HttpResponse res = (HttpResponse) o; assertThat(res, is(not(instanceOf(HttpContent.class)))); - assertThat(res.headers().get(Names.TRANSFER_ENCODING), is("chunked")); + assertThat(res.headers().getAndConvert(Names.TRANSFER_ENCODING), is("chunked")); assertThat(res.headers().get(Names.CONTENT_LENGTH), is(nullValue())); - assertThat(res.headers().get(Names.CONTENT_ENCODING), is("gzip")); + assertThat(res.headers().getAndConvert(Names.CONTENT_ENCODING), is("gzip")); } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentEncoderTest.java index 879500c9e7..3ffe22f73e 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentEncoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentEncoderTest.java @@ -161,7 +161,7 @@ public class HttpContentEncoderTest { FullHttpResponse res = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(new byte[42])); - res.headers().set(Names.CONTENT_LENGTH, 42); + res.headers().setInt(Names.CONTENT_LENGTH, 42); ch.writeOutbound(res); assertEncodedResponse(ch); @@ -260,8 +260,8 @@ public class HttpContentEncoderTest { HttpResponse res = (HttpResponse) o; assertThat(res, is(not(instanceOf(HttpContent.class)))); - assertThat(res.headers().get(Names.TRANSFER_ENCODING), is("chunked")); + assertThat(res.headers().getAndConvert(Names.TRANSFER_ENCODING), is("chunked")); assertThat(res.headers().get(Names.CONTENT_LENGTH), is(nullValue())); - assertThat(res.headers().get(Names.CONTENT_ENCODING), is("test")); + assertThat(res.headers().getAndConvert(Names.CONTENT_ENCODING), is("test")); } } 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 b0a81178ee..c0ee22d501 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 @@ -43,7 +43,7 @@ public class HttpHeaderUtilTest { assertEquals("1", headers.get("Foo")); - List values = headers.getAll("Foo"); + List values = headers.getAll("Foo"); assertEquals(2, values.size()); assertEquals("1", values.get(0)); assertEquals("2", values.get(1)); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpObjectAggregatorTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpObjectAggregatorTest.java index 32c7a0c0cd..97e6b4aa0f 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpObjectAggregatorTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpObjectAggregatorTest.java @@ -42,7 +42,7 @@ public class HttpObjectAggregatorTest { EmbeddedChannel embedder = new EmbeddedChannel(aggr); HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost"); - message.headers().set("X-Test", true); + message.headers().setBoolean("X-Test", true); HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII)); HttpContent chunk3 = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER); @@ -80,12 +80,12 @@ public class HttpObjectAggregatorTest { HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024); EmbeddedChannel embedder = new EmbeddedChannel(aggr); HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost"); - message.headers().set("X-Test", true); + message.headers().setBoolean("X-Test", true); HttpHeaderUtil.setTransferEncodingChunked(message, true); HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII)); LastHttpContent trailer = new DefaultLastHttpContent(); - trailer.trailingHeaders().set("X-Trailer", true); + trailer.trailingHeaders().setObject("X-Trailer", true); assertFalse(embedder.writeInbound(message)); assertFalse(embedder.writeInbound(chunk1)); @@ -281,7 +281,7 @@ public class HttpObjectAggregatorTest { EmbeddedChannel embedder = new EmbeddedChannel(aggr); HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "http://localhost"); - message.headers().set("X-Test", true); + message.headers().setBoolean("X-Test", true); message.headers().set("Transfer-Encoding", "Chunked"); HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); HttpContent chunk2 = new DefaultHttpContent(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII)); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java index 8fa745115d..4342a9acaf 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java @@ -98,7 +98,7 @@ public class HttpRequestDecoderTest { } private static void checkHeader(HttpHeaders headers, String name, String value) { - List header1 = headers.getAll(name); + List header1 = headers.getAll(name); assertEquals(1, header1.size()); assertEquals(value, header1.get(0)); } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java index 03947a00fe..0c5ae67d95 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java @@ -184,7 +184,7 @@ public class HttpResponseDecoderTest { HttpResponse res = ch.readInbound(); assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); assertThat(res.status(), is(HttpResponseStatus.OK)); - assertThat(res.headers().get(Names.TRANSFER_ENCODING), is("chunked")); + assertThat(res.headers().getAndConvert(Names.TRANSFER_ENCODING), is("chunked")); assertThat(ch.readInbound(), is(nullValue())); // Close the connection without sending anything. @@ -205,7 +205,7 @@ public class HttpResponseDecoderTest { HttpResponse res = ch.readInbound(); assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); assertThat(res.status(), is(HttpResponseStatus.OK)); - assertThat(res.headers().get(Names.TRANSFER_ENCODING), is("chunked")); + assertThat(res.headers().getAndConvert(Names.TRANSFER_ENCODING), is("chunked")); // Read the partial content. HttpContent content = ch.readInbound(); @@ -275,7 +275,7 @@ public class HttpResponseDecoderTest { HttpResponse res = ch.readInbound(); assertThat(res.protocolVersion(), sameInstance(HttpVersion.HTTP_1_1)); assertThat(res.status(), is(HttpResponseStatus.OK)); - assertThat(res.headers().get("X-Header"), is("h2=h2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT")); + assertThat(res.headers().getAndConvert("X-Header"), is("h2=h2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT")); assertThat(ch.readInbound(), is(nullValue())); ch.writeInbound(Unpooled.wrappedBuffer(new byte[1024])); @@ -313,7 +313,7 @@ public class HttpResponseDecoderTest { assertThat(lastContent.content().isReadable(), is(false)); HttpHeaders headers = lastContent.trailingHeaders(); assertEquals(1, headers.names().size()); - List values = headers.getAll("Set-Cookie"); + List values = headers.getAll("Set-Cookie"); assertEquals(2, values.size()); assertTrue(values.contains("t1=t1v1")); assertTrue(values.contains("t2=t2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT")); @@ -363,7 +363,7 @@ public class HttpResponseDecoderTest { assertThat(lastContent.content().isReadable(), is(false)); HttpHeaders headers = lastContent.trailingHeaders(); assertEquals(1, headers.names().size()); - List values = headers.getAll("Set-Cookie"); + List values = headers.getAll("Set-Cookie"); assertEquals(2, values.size()); assertTrue(values.contains("t1=t1v1")); assertTrue(values.contains("t2=t2v2; Expires=Wed, 09-Jun-2021 10:18:14 GMT")); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseEncoderTest.java index ff2b26065a..a1f51e3333 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseEncoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseEncoderTest.java @@ -39,7 +39,8 @@ public class HttpResponseEncoderTest { ByteBuf buffer = channel.readOutbound(); - assertEquals("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n", buffer.toString(CharsetUtil.US_ASCII)); + assertEquals("HTTP/1.1 200 OK\r\n" + HttpHeaders.Names.TRANSFER_ENCODING + ": " + + HttpHeaders.Values.CHUNKED + "\r\n\r\n", buffer.toString(CharsetUtil.US_ASCII)); buffer.release(); assertTrue(channel.writeOutbound(FILE_REGION)); buffer = channel.readOutbound(); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerCodecTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerCodecTest.java index f0d0faff2d..6c2df6e4be 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerCodecTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerCodecTest.java @@ -92,7 +92,7 @@ public class HttpServerCodecTest { // Ensure the aggregator generates a full request. FullHttpRequest req = ch.readInbound(); - assertThat(req.headers().get(CONTENT_LENGTH), is("1")); + assertThat(req.headers().getAndConvert(CONTENT_LENGTH), is("1")); assertThat(req.content().readableBytes(), is(1)); assertThat(req.content().readByte(), is((byte) 42)); req.release(); @@ -103,12 +103,13 @@ public class HttpServerCodecTest { // Send the actual response. FullHttpResponse res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CREATED); res.content().writeBytes("OK".getBytes(CharsetUtil.UTF_8)); - res.headers().set(CONTENT_LENGTH, 2); + res.headers().setInt(CONTENT_LENGTH, 2); ch.writeOutbound(res); // Ensure the encoder handles the response after handling 100 Continue. ByteBuf encodedRes = ch.readOutbound(); - assertThat(encodedRes.toString(CharsetUtil.UTF_8), is("HTTP/1.1 201 Created\r\nContent-Length: 2\r\n\r\nOK")); + assertThat(encodedRes.toString(CharsetUtil.UTF_8), is("HTTP/1.1 201 Created\r\n" + + CONTENT_LENGTH + ": 2\r\n\r\nOK")); encodedRes.release(); ch.finish(); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsConfigTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsConfigTest.java index 6e2ffc1e86..fc7e233870 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsConfigTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsConfigTest.java @@ -95,20 +95,20 @@ public class CorsConfigTest { @Test public void preflightResponseHeadersSingleValue() { final CorsConfig cors = withAnyOrigin().preflightResponseHeader("SingleValue", "value").build(); - assertThat(cors.preflightResponseHeaders().get("SingleValue"), equalTo("value")); + assertThat(cors.preflightResponseHeaders().getAndConvert("SingleValue"), equalTo("value")); } @Test public void preflightResponseHeadersMultipleValues() { final CorsConfig cors = withAnyOrigin().preflightResponseHeader("MultipleValues", "value1", "value2").build(); - assertThat(cors.preflightResponseHeaders().getAll("MultipleValues"), hasItems("value1", "value2")); + assertThat(cors.preflightResponseHeaders().getAllAndConvert("MultipleValues"), hasItems("value1", "value2")); } @Test public void defaultPreflightResponseHeaders() { final CorsConfig cors = withAnyOrigin().build(); assertThat(cors.preflightResponseHeaders().get(Names.DATE), is(notNullValue())); - assertThat(cors.preflightResponseHeaders().get(Names.CONTENT_LENGTH), is("0")); + assertThat(cors.preflightResponseHeaders().getAndConvert(Names.CONTENT_LENGTH), is("0")); } @Test diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsHandlerTest.java index c5b91adeec..ad33637cc9 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsHandlerTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsHandlerTest.java @@ -47,14 +47,14 @@ public class CorsHandlerTest { @Test public void simpleRequestWithAnyOrigin() { final HttpResponse response = simpleRequest(CorsConfig.withAnyOrigin().build(), "http://localhost:7777"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is("*")); + assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is("*")); } @Test public void simpleRequestWithOrigin() { final String origin = "http://localhost:8888"; final HttpResponse response = simpleRequest(CorsConfig.withOrigin(origin).build(), origin); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin)); + assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin)); } @Test @@ -63,9 +63,9 @@ public class CorsHandlerTest { final String origin2 = "https://localhost:8888"; final String[] origins = {origin1, origin2}; final HttpResponse response1 = simpleRequest(CorsConfig.withOrigins(origins).build(), origin1); - assertThat(response1.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin1)); + assertThat(response1.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin1)); final HttpResponse response2 = simpleRequest(CorsConfig.withOrigins(origins).build(), origin2); - assertThat(response2.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin2)); + assertThat(response2.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is(origin2)); } @Test @@ -81,9 +81,9 @@ public class CorsHandlerTest { .allowedRequestMethods(GET, DELETE) .build(); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888")); - assertThat(response.headers().getAll(ACCESS_CONTROL_ALLOW_METHODS), hasItems("GET", "DELETE")); - assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); + assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888")); + assertThat(response.headers().getAllAndConvert(ACCESS_CONTROL_ALLOW_METHODS), hasItems("GET", "DELETE")); + assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString())); } @Test @@ -93,19 +93,20 @@ public class CorsHandlerTest { .allowedRequestHeaders("content-type", "xheader1") .build(); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888")); - assertThat(response.headers().getAll(ACCESS_CONTROL_ALLOW_METHODS), hasItems("OPTIONS", "GET")); - assertThat(response.headers().getAll(ACCESS_CONTROL_ALLOW_HEADERS), hasItems("content-type", "xheader1")); - assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); + assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is("http://localhost:8888")); + assertThat(response.headers().getAllAndConvert(ACCESS_CONTROL_ALLOW_METHODS), hasItems("OPTIONS", "GET")); + assertThat(response.headers().getAllAndConvert(ACCESS_CONTROL_ALLOW_HEADERS), + hasItems("content-type", "xheader1")); + assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString())); } @Test public void preflightRequestWithDefaultHeaders() { final CorsConfig config = CorsConfig.withOrigin("http://localhost:8888").build(); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); - assertThat(response.headers().get(CONTENT_LENGTH), is("0")); + assertThat(response.headers().getAndConvert(CONTENT_LENGTH), is("0")); assertThat(response.headers().get(DATE), is(notNullValue())); - assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); + assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString())); } @Test @@ -114,8 +115,8 @@ public class CorsHandlerTest { .preflightResponseHeader("CustomHeader", "somevalue") .build(); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); - assertThat(response.headers().get("CustomHeader"), equalTo("somevalue")); - assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); + assertThat(response.headers().getAndConvert("CustomHeader"), equalTo("somevalue")); + assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString())); } @Test @@ -124,8 +125,8 @@ public class CorsHandlerTest { .preflightResponseHeader("CustomHeader", "value1", "value2") .build(); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); - assertThat(response.headers().getAll("CustomHeader"), hasItems("value1", "value2")); - assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); + assertThat(response.headers().getAllAndConvert("CustomHeader"), hasItems("value1", "value2")); + assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString())); } @Test @@ -134,8 +135,8 @@ public class CorsHandlerTest { .preflightResponseHeader("CustomHeader", Arrays.asList("value1", "value2")) .build(); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); - assertThat(response.headers().getAll("CustomHeader"), hasItems("value1", "value2")); - assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); + assertThat(response.headers().getAllAndConvert("CustomHeader"), hasItems("value1", "value2")); + assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString())); } @Test @@ -148,8 +149,8 @@ public class CorsHandlerTest { } }).build(); final HttpResponse response = preflightRequest(config, "http://localhost:8888", "content-type, xheader1"); - assertThat(response.headers().get("GenHeader"), equalTo("generatedValue")); - assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); + assertThat(response.headers().getAndConvert("GenHeader"), equalTo("generatedValue")); + assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString())); } @Test @@ -157,7 +158,7 @@ public class CorsHandlerTest { final String origin = "null"; final CorsConfig config = CorsConfig.withOrigin(origin).allowNullOrigin().build(); final HttpResponse response = preflightRequest(config, origin, "content-type, xheader1"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), is(equalTo("*"))); + assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), is(equalTo("*"))); } @Test @@ -165,7 +166,7 @@ public class CorsHandlerTest { final String origin = "null"; final CorsConfig config = CorsConfig.withOrigin(origin).allowCredentials().build(); final HttpResponse response = preflightRequest(config, origin, "content-type, xheader1"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_CREDENTIALS), is(equalTo("true"))); + assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_CREDENTIALS), is(equalTo("true"))); } @Test @@ -180,15 +181,15 @@ public class CorsHandlerTest { public void simpleRequestCustomHeaders() { final CorsConfig config = CorsConfig.withAnyOrigin().exposeHeaders("custom1", "custom2").build(); final HttpResponse response = simpleRequest(config, "http://localhost:7777"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("*")); - assertThat(response.headers().getAll(ACCESS_CONTROL_EXPOSE_HEADERS), hasItems("custom1", "custom1")); + assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("*")); + assertThat(response.headers().getAllAndConvert(ACCESS_CONTROL_EXPOSE_HEADERS), hasItems("custom1", "custom1")); } @Test public void simpleRequestAllowCredentials() { final CorsConfig config = CorsConfig.withAnyOrigin().allowCredentials().build(); final HttpResponse response = simpleRequest(config, "http://localhost:7777"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true")); + assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true")); } @Test @@ -202,16 +203,16 @@ public class CorsHandlerTest { public void anyOriginAndAllowCredentialsShouldEchoRequestOrigin() { final CorsConfig config = CorsConfig.withAnyOrigin().allowCredentials().build(); final HttpResponse response = simpleRequest(config, "http://localhost:7777"); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true")); - assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("http://localhost:7777")); - assertThat(response.headers().get(VARY), equalTo(ORIGIN.toString())); + assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_CREDENTIALS), equalTo("true")); + assertThat(response.headers().getAndConvert(ACCESS_CONTROL_ALLOW_ORIGIN), equalTo("http://localhost:7777")); + assertThat(response.headers().getAndConvert(VARY), equalTo(ORIGIN.toString())); } @Test public void simpleRequestExposeHeaders() { final CorsConfig config = CorsConfig.withAnyOrigin().exposeHeaders("one", "two").build(); final HttpResponse response = simpleRequest(config, "http://localhost:7777"); - assertThat(response.headers().getAll(ACCESS_CONTROL_EXPOSE_HEADERS), hasItems("one", "two")); + assertThat(response.headers().getAllAndConvert(ACCESS_CONTROL_EXPOSE_HEADERS), hasItems("one", "two")); } @Test diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoderTest.java index 26273e6160..a74e1c1ce3 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoderTest.java @@ -18,11 +18,13 @@ package io.netty.handler.codec.http.multipart; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.DefaultFullHttpRequest; +import static io.netty.handler.codec.http.HttpHeaders.Names.*; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder.EncoderMode; import io.netty.util.CharsetUtil; import io.netty.util.internal.StringUtil; + import org.junit.Test; import java.io.File; @@ -47,15 +49,15 @@ public class HttpPostRequestEncoderTest { String content = getRequestBody(encoder); String expected = "--" + multipartDataBoundary + "\r\n" + - "Content-Disposition: form-data; name=\"foo\"" + "\r\n" + - "Content-Type: text/plain; charset=UTF-8" + "\r\n" + + CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" + + CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" + "\r\n" + "bar" + "\r\n" + "--" + multipartDataBoundary + "\r\n" + - "Content-Disposition: form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" + - "Content-Type: text/plain" + "\r\n" + - "Content-Transfer-Encoding: binary" + "\r\n" + + CONTENT_DISPOSITION + ": form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" + + CONTENT_TYPE + ": text/plain" + "\r\n" + + CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" + "\r\n" + "File 01" + StringUtil.NEWLINE + "\r\n" + @@ -83,25 +85,25 @@ public class HttpPostRequestEncoderTest { String content = getRequestBody(encoder); String expected = "--" + multipartDataBoundary + "\r\n" + - "Content-Disposition: form-data; name=\"foo\"" + "\r\n" + - "Content-Type: text/plain; charset=UTF-8" + "\r\n" + + CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" + + CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" + "\r\n" + "bar" + "\r\n" + "--" + multipartDataBoundary + "\r\n" + - "Content-Disposition: form-data; name=\"quux\"" + "\r\n" + - "Content-Type: multipart/mixed; boundary=" + multipartMixedBoundary + "\r\n" + + CONTENT_DISPOSITION + ": form-data; name=\"quux\"" + "\r\n" + + CONTENT_TYPE + ": multipart/mixed; boundary=" + multipartMixedBoundary + "\r\n" + "\r\n" + "--" + multipartMixedBoundary + "\r\n" + - "Content-Disposition: attachment; filename=\"file-02.txt\"" + "\r\n" + - "Content-Type: text/plain" + "\r\n" + - "Content-Transfer-Encoding: binary" + "\r\n" + + CONTENT_DISPOSITION + ": attachment; filename=\"file-02.txt\"" + "\r\n" + + CONTENT_TYPE + ": text/plain" + "\r\n" + + CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" + "\r\n" + "File 01" + StringUtil.NEWLINE + "\r\n" + "--" + multipartMixedBoundary + "\r\n" + - "Content-Disposition: attachment; filename=\"file-02.txt\"" + "\r\n" + - "Content-Type: text/plain" + "\r\n" + - "Content-Transfer-Encoding: binary" + "\r\n" + + CONTENT_DISPOSITION + ": attachment; filename=\"file-02.txt\"" + "\r\n" + + CONTENT_TYPE + ": text/plain" + "\r\n" + + CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" + "\r\n" + "File 02" + StringUtil.NEWLINE + "\r\n" + @@ -130,20 +132,20 @@ public class HttpPostRequestEncoderTest { String content = getRequestBody(encoder); String expected = "--" + multipartDataBoundary + "\r\n" + - "Content-Disposition: form-data; name=\"foo\"" + "\r\n" + - "Content-Type: text/plain; charset=UTF-8" + "\r\n" + + CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" + + CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" + "\r\n" + "bar" + "\r\n" + "--" + multipartDataBoundary + "\r\n" + - "Content-Disposition: form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" + - "Content-Type: text/plain" + "\r\n" + - "Content-Transfer-Encoding: binary" + "\r\n" + + CONTENT_DISPOSITION + ": form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" + + CONTENT_TYPE + ": text/plain" + "\r\n" + + CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" + "\r\n" + "File 01" + StringUtil.NEWLINE + "\r\n" + "--" + multipartDataBoundary + "\r\n" + - "Content-Disposition: form-data; name=\"quux\"; filename=\"file-02.txt\"" + "\r\n" + - "Content-Type: text/plain" + "\r\n" + - "Content-Transfer-Encoding: binary" + "\r\n" + + CONTENT_DISPOSITION + ": form-data; name=\"quux\"; filename=\"file-02.txt\"" + "\r\n" + + CONTENT_TYPE + ": text/plain" + "\r\n" + + CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" + "\r\n" + "File 02" + StringUtil.NEWLINE + "\r\n" + @@ -169,15 +171,15 @@ public class HttpPostRequestEncoderTest { String content = getRequestBody(encoder); String expected = "--" + multipartDataBoundary + "\r\n" + - "Content-Disposition: form-data; name=\"foo\"" + "\r\n" + - "Content-Type: text/plain; charset=UTF-8" + "\r\n" + + CONTENT_DISPOSITION + ": form-data; name=\"foo\"" + "\r\n" + + CONTENT_TYPE + ": text/plain; charset=UTF-8" + "\r\n" + "\r\n" + "bar" + "\r\n" + "--" + multipartDataBoundary + "\r\n" + - "Content-Disposition: form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" + - "Content-Type: text/plain" + "\r\n" + - "Content-Transfer-Encoding: binary" + "\r\n" + + CONTENT_DISPOSITION + ": form-data; name=\"quux\"; filename=\"file-01.txt\"" + "\r\n" + + CONTENT_TYPE + ": text/plain" + "\r\n" + + CONTENT_TRANSFER_ENCODING + ": binary" + "\r\n" + "\r\n" + "File 01" + StringUtil.NEWLINE + "\r\n" + diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandlerTest.java index acaa8e7be7..f0d623c7fc 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandlerTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandlerTest.java @@ -69,14 +69,14 @@ public class WebSocketClientExtensionHandlerTest { HttpRequest req2 = ch.readOutbound(); List reqExts = WebSocketExtensionUtil.extractExtensions( - req2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); + req2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS)); HttpResponse res = newUpgradeResponse("main"); ch.writeInbound(res); HttpResponse res2 = ch.readInbound(); List resExts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); + res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS)); // test assertEquals(2, reqExts.size()); @@ -119,14 +119,14 @@ public class WebSocketClientExtensionHandlerTest { HttpRequest req2 = ch.readOutbound(); List reqExts = WebSocketExtensionUtil.extractExtensions( - req2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); + req2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS)); HttpResponse res = newUpgradeResponse("fallback"); ch.writeInbound(res); HttpResponse res2 = ch.readInbound(); List resExts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); + res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS)); // test assertEquals(2, reqExts.size()); @@ -182,14 +182,14 @@ public class WebSocketClientExtensionHandlerTest { HttpRequest req2 = ch.readOutbound(); List reqExts = WebSocketExtensionUtil.extractExtensions( - req2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); + req2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS)); HttpResponse res = newUpgradeResponse("main, fallback"); ch.writeInbound(res); HttpResponse res2 = ch.readInbound(); List resExts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); + res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS)); // test assertEquals(2, reqExts.size()); @@ -239,7 +239,7 @@ public class WebSocketClientExtensionHandlerTest { HttpRequest req2 = ch.readOutbound(); List reqExts = WebSocketExtensionUtil.extractExtensions( - req2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); + req2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS)); HttpResponse res = newUpgradeResponse("main, fallback"); ch.writeInbound(res); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandlerTest.java index cf1bd420ec..7fb0125872 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandlerTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandlerTest.java @@ -79,7 +79,7 @@ public class WebSocketServerExtensionHandlerTest { HttpResponse res2 = ch.readOutbound(); List resExts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); + res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS)); // test assertEquals(1, resExts.size()); @@ -130,7 +130,7 @@ public class WebSocketServerExtensionHandlerTest { HttpResponse res2 = ch.readOutbound(); List resExts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); + res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS)); // test assertEquals(2, resExts.size()); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/WebSocketServerCompressionHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/WebSocketServerCompressionHandlerTest.java index 0b9b48fcee..7182e519e0 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/WebSocketServerCompressionHandlerTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/compression/WebSocketServerCompressionHandlerTest.java @@ -45,7 +45,7 @@ public class WebSocketServerCompressionHandlerTest { HttpResponse res2 = ch.readOutbound(); List exts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); + res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS)); Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); Assert.assertTrue(exts.get(0).parameters().isEmpty()); @@ -66,7 +66,7 @@ public class WebSocketServerCompressionHandlerTest { HttpResponse res2 = ch.readOutbound(); List exts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); + res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS)); Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); Assert.assertEquals("10", exts.get(0).parameters().get(CLIENT_MAX_WINDOW)); @@ -87,7 +87,7 @@ public class WebSocketServerCompressionHandlerTest { HttpResponse res2 = ch.readOutbound(); List exts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); + res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS)); Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); Assert.assertTrue(exts.get(0).parameters().isEmpty()); @@ -108,7 +108,7 @@ public class WebSocketServerCompressionHandlerTest { HttpResponse res2 = ch.readOutbound(); List exts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); + res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS)); Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); Assert.assertEquals("10", exts.get(0).parameters().get(SERVER_MAX_WINDOW)); @@ -163,7 +163,7 @@ public class WebSocketServerCompressionHandlerTest { HttpResponse res2 = ch.readOutbound(); List exts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); + res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS)); Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); Assert.assertTrue(exts.get(0).parameters().isEmpty()); @@ -185,7 +185,7 @@ public class WebSocketServerCompressionHandlerTest { HttpResponse res2 = ch.readOutbound(); List exts = WebSocketExtensionUtil.extractExtensions( - res2.headers().get(Names.SEC_WEBSOCKET_EXTENSIONS)); + res2.headers().getAndConvert(Names.SEC_WEBSOCKET_EXTENSIONS)); Assert.assertEquals(PERMESSAGE_DEFLATE_EXTENSION, exts.get(0).name()); Assert.assertTrue(exts.get(0).parameters().isEmpty()); diff --git a/codec-http/src/test/java/io/netty/handler/codec/spdy/SpdySessionHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/spdy/SpdySessionHandlerTest.java index c65a1245de..4c8a54bf85 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/spdy/SpdySessionHandlerTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/spdy/SpdySessionHandlerTest.java @@ -81,9 +81,9 @@ public class SpdySessionHandlerTest { SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg; assertEquals(streamId, spdyHeadersFrame.streamId()); assertEquals(last, spdyHeadersFrame.isLast()); - for (String name: headers.names()) { - List expectedValues = headers.getAll(name); - List receivedValues = spdyHeadersFrame.headers().getAll(name); + for (CharSequence name: headers.names()) { + List expectedValues = headers.getAll(name); + List receivedValues = spdyHeadersFrame.headers().getAll(name); assertTrue(receivedValues.containsAll(expectedValues)); receivedValues.removeAll(expectedValues); assertTrue(receivedValues.isEmpty()); @@ -357,7 +357,7 @@ public class SpdySessionHandlerTest { int streamId = spdySynStreamFrame.streamId(); SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId); spdySynReplyFrame.setLast(spdySynStreamFrame.isLast()); - for (Map.Entry entry: spdySynStreamFrame.headers()) { + for (Map.Entry entry: spdySynStreamFrame.headers()) { spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue()); } 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 6c38739f67..d891d30369 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 @@ -19,10 +19,6 @@ import io.netty.handler.codec.BinaryHeaders; import io.netty.handler.codec.DefaultBinaryHeaders; public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2Headers { - - public DefaultHttp2Headers() { - } - @Override public Http2Headers add(AsciiString name, AsciiString value) { super.add(name, value); @@ -30,7 +26,7 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He } @Override - public Http2Headers add(AsciiString name, Iterable values) { + public Http2Headers add(AsciiString name, Iterable values) { super.add(name, values); return this; } @@ -41,6 +37,72 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He return this; } + @Override + public Http2Headers addObject(AsciiString name, Object value) { + super.addObject(name, value); + return this; + } + + @Override + public Http2Headers addObject(AsciiString name, Iterable values) { + super.addObject(name, values); + return this; + } + + @Override + public Http2Headers addObject(AsciiString name, Object... values) { + super.addObject(name, values); + return this; + } + + @Override + public Http2Headers addBoolean(AsciiString name, boolean value) { + super.addBoolean(name, value); + return this; + } + + @Override + public Http2Headers addChar(AsciiString name, char value) { + super.addChar(name, value); + return this; + } + + @Override + public Http2Headers addByte(AsciiString name, byte value) { + super.addByte(name, value); + return this; + } + + @Override + public Http2Headers addShort(AsciiString name, short value) { + super.addShort(name, value); + return this; + } + + @Override + public Http2Headers addInt(AsciiString name, int value) { + super.addInt(name, value); + return this; + } + + @Override + public Http2Headers addLong(AsciiString name, long value) { + super.addLong(name, value); + return this; + } + + @Override + public Http2Headers addFloat(AsciiString name, float value) { + super.addFloat(name, value); + return this; + } + + @Override + public Http2Headers addDouble(AsciiString name, double value) { + super.addDouble(name, value); + return this; + } + @Override public Http2Headers add(BinaryHeaders headers) { super.add(headers); @@ -54,7 +116,7 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He } @Override - public Http2Headers set(AsciiString name, Iterable values) { + public Http2Headers set(AsciiString name, Iterable values) { super.set(name, values); return this; } @@ -65,6 +127,72 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He return this; } + @Override + public Http2Headers setObject(AsciiString name, Object value) { + super.setObject(name, value); + return this; + } + + @Override + public Http2Headers setObject(AsciiString name, Iterable values) { + super.setObject(name, values); + return this; + } + + @Override + public Http2Headers setObject(AsciiString name, Object... values) { + super.setObject(name, values); + return this; + } + + @Override + public Http2Headers setBoolean(AsciiString name, boolean value) { + super.setBoolean(name, value); + return this; + } + + @Override + public Http2Headers setChar(AsciiString name, char value) { + super.setChar(name, value); + return this; + } + + @Override + public Http2Headers setByte(AsciiString name, byte value) { + super.setByte(name, value); + return this; + } + + @Override + public Http2Headers setShort(AsciiString name, short value) { + super.setShort(name, value); + return this; + } + + @Override + public Http2Headers setInt(AsciiString name, int value) { + super.setInt(name, value); + return this; + } + + @Override + public Http2Headers setLong(AsciiString name, long value) { + super.setLong(name, value); + return this; + } + + @Override + public Http2Headers setFloat(AsciiString name, float value) { + super.setFloat(name, value); + return this; + } + + @Override + public Http2Headers setDouble(AsciiString name, double value) { + super.setDouble(name, value); + return this; + } + @Override public Http2Headers set(BinaryHeaders headers) { super.set(headers); @@ -83,26 +211,6 @@ public class DefaultHttp2Headers extends DefaultBinaryHeaders implements Http2He return this; } - @Override - public Http2Headers forEachEntry(final BinaryHeaders.BinaryHeaderVisitor visitor) { - super.forEachEntry(visitor); - return this; - } - - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Http2Headers)) { - return false; - } - - return super.equals((BinaryHeaders) o); - } - @Override public Http2Headers method(AsciiString value) { set(PseudoHeaderName.METHOD.value(), value); 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 dd78c0b404..21987cb198 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 @@ -20,12 +20,13 @@ import static io.netty.handler.codec.http2.Http2Exception.protocolError; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufOutputStream; import io.netty.handler.codec.AsciiString; -import io.netty.handler.codec.BinaryHeaders; +import io.netty.handler.codec.BinaryHeaders.EntryVisitor; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Collections; +import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; @@ -71,18 +72,21 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2Hea encodeHeader(name, value, stream); } } - headers.forEachEntry(new BinaryHeaders.BinaryHeaderVisitor() { + + headers.forEachEntry(new EntryVisitor() { @Override - public boolean visit(AsciiString name, AsciiString value) throws Exception { + public boolean visit(Entry entry) throws Exception { + final AsciiString name = entry.getKey(); + final AsciiString value = entry.getValue(); if (!Http2Headers.PseudoHeaderName.isPseudoHeader(name)) { encodeHeader(name, value, stream); } return true; } }); - } catch (IOException e) { - throw Http2Exception.format(Http2Error.COMPRESSION_ERROR, - "Failed encoding headers block: %s", e.getMessage()); + } catch (Exception e) { + throw Http2Exception.format(Http2Error.COMPRESSION_ERROR, "Failed encoding headers block: %s", + e.getMessage()); } finally { try { stream.close(); 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 4c9df927cd..ae4aa47918 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 @@ -35,8 +35,6 @@ import io.netty.handler.codec.compression.ZlibWrapper; * stream. */ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecorator { - private static final AsciiString CONTENT_ENCODING_LOWER_CASE = CONTENT_ENCODING.toLowerCase(); - private static final AsciiString CONTENT_LENGTH_LOWER_CASE = CONTENT_LENGTH.toLowerCase(); private static final Http2ConnectionAdapter CLEAN_UP_LISTENER = new Http2ConnectionAdapter() { @Override public void streamRemoved(Http2Stream stream) { @@ -171,7 +169,7 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor if (decompressor == null) { if (!endOfStream) { // Determine the content encoding. - AsciiString contentEncoding = headers.get(CONTENT_ENCODING_LOWER_CASE); + AsciiString contentEncoding = headers.get(CONTENT_ENCODING); if (contentEncoding == null) { contentEncoding = IDENTITY; } @@ -182,9 +180,9 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor // so that the message looks like a decoded message. AsciiString targetContentEncoding = getTargetContentEncoding(contentEncoding); if (IDENTITY.equalsIgnoreCase(targetContentEncoding)) { - headers.remove(CONTENT_ENCODING_LOWER_CASE); + headers.remove(CONTENT_ENCODING); } else { - headers.set(CONTENT_ENCODING_LOWER_CASE, targetContentEncoding); + headers.set(CONTENT_ENCODING, targetContentEncoding); } } } @@ -195,7 +193,7 @@ public class DelegatingDecompressorFrameListener extends Http2FrameListenerDecor // The content length will be for the compressed data. Since we will decompress the data // this content-length will not be correct. Instead of queuing messages or delaying sending // header frames...just remove the content-length header - headers.remove(CONTENT_LENGTH_LOWER_CASE); + headers.remove(CONTENT_LENGTH); } } 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 c60aff603f..c0a462dc16 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 @@ -26,67 +26,194 @@ public final class EmptyHttp2Headers extends EmptyBinaryHeaders implements Http2 } @Override - public EmptyHttp2Headers add(AsciiString name, AsciiString value) { + public Http2Headers add(AsciiString name, AsciiString value) { super.add(name, value); return this; } @Override - public EmptyHttp2Headers add(AsciiString name, Iterable values) { + public Http2Headers add(AsciiString name, Iterable values) { super.add(name, values); return this; } @Override - public EmptyHttp2Headers add(AsciiString name, AsciiString... values) { + public Http2Headers add(AsciiString name, AsciiString... values) { super.add(name, values); return this; } @Override - public EmptyHttp2Headers add(BinaryHeaders headers) { + public Http2Headers addObject(AsciiString name, Object value) { + super.addObject(name, value); + return this; + } + + @Override + public Http2Headers addObject(AsciiString name, Iterable values) { + super.addObject(name, values); + return this; + } + + @Override + public Http2Headers addObject(AsciiString name, Object... values) { + super.addObject(name, values); + return this; + } + + @Override + public Http2Headers addBoolean(AsciiString name, boolean value) { + super.addBoolean(name, value); + return this; + } + + @Override + public Http2Headers addChar(AsciiString name, char value) { + super.addChar(name, value); + return this; + } + + @Override + public Http2Headers addByte(AsciiString name, byte value) { + super.addByte(name, value); + return this; + } + + @Override + public Http2Headers addShort(AsciiString name, short value) { + super.addShort(name, value); + return this; + } + + @Override + public Http2Headers addInt(AsciiString name, int value) { + super.addInt(name, value); + return this; + } + + @Override + public Http2Headers addLong(AsciiString name, long value) { + super.addLong(name, value); + return this; + } + + @Override + public Http2Headers addFloat(AsciiString name, float value) { + super.addFloat(name, value); + return this; + } + + @Override + public Http2Headers addDouble(AsciiString name, double value) { + super.addDouble(name, value); + return this; + } + + @Override + public Http2Headers add(BinaryHeaders headers) { super.add(headers); return this; } @Override - public EmptyHttp2Headers set(AsciiString name, AsciiString value) { + public Http2Headers set(AsciiString name, AsciiString value) { super.set(name, value); return this; } @Override - public EmptyHttp2Headers set(AsciiString name, Iterable values) { + public Http2Headers set(AsciiString name, Iterable values) { super.set(name, values); return this; } @Override - public EmptyHttp2Headers set(AsciiString name, AsciiString... values) { + public Http2Headers set(AsciiString name, AsciiString... values) { super.set(name, values); return this; } @Override - public EmptyHttp2Headers set(BinaryHeaders headers) { + public Http2Headers setObject(AsciiString name, Object value) { + super.setObject(name, value); + return this; + } + + @Override + public Http2Headers setObject(AsciiString name, Iterable values) { + super.setObject(name, values); + return this; + } + + @Override + public Http2Headers setObject(AsciiString name, Object... values) { + super.setObject(name, values); + return this; + } + + @Override + public Http2Headers setBoolean(AsciiString name, boolean value) { + super.setBoolean(name, value); + return this; + } + + @Override + public Http2Headers setChar(AsciiString name, char value) { + super.setChar(name, value); + return this; + } + + @Override + public Http2Headers setByte(AsciiString name, byte value) { + super.setByte(name, value); + return this; + } + + @Override + public Http2Headers setShort(AsciiString name, short value) { + super.setShort(name, value); + return this; + } + + @Override + public Http2Headers setInt(AsciiString name, int value) { + super.setInt(name, value); + return this; + } + + @Override + public Http2Headers setLong(AsciiString name, long value) { + super.setLong(name, value); + return this; + } + + @Override + public Http2Headers setFloat(AsciiString name, float value) { + super.setFloat(name, value); + return this; + } + + @Override + public Http2Headers setDouble(AsciiString name, double value) { + super.setDouble(name, value); + return this; + } + + @Override + public Http2Headers set(BinaryHeaders headers) { super.set(headers); return this; } @Override - public EmptyHttp2Headers setAll(BinaryHeaders headers) { + public Http2Headers setAll(BinaryHeaders headers) { super.setAll(headers); return this; } @Override - public EmptyHttp2Headers clear() { - return this; - } - - @Override - public EmptyHttp2Headers forEachEntry(BinaryHeaderVisitor visitor) { - super.forEachEntry(visitor); + public Http2Headers clear() { + super.clear(); return this; } 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 a82e00669d..22b9467e07 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 @@ -84,11 +84,44 @@ public interface Http2Headers extends BinaryHeaders { Http2Headers add(AsciiString name, AsciiString value); @Override - Http2Headers add(AsciiString name, Iterable values); + Http2Headers add(AsciiString name, Iterable values); @Override Http2Headers add(AsciiString name, AsciiString... values); + @Override + Http2Headers addObject(AsciiString name, Object value); + + @Override + Http2Headers addObject(AsciiString name, Iterable values); + + @Override + Http2Headers addObject(AsciiString name, Object... values); + + @Override + Http2Headers addBoolean(AsciiString name, boolean value); + + @Override + Http2Headers addByte(AsciiString name, byte value); + + @Override + Http2Headers addChar(AsciiString name, char value); + + @Override + Http2Headers addShort(AsciiString name, short value); + + @Override + Http2Headers addInt(AsciiString name, int value); + + @Override + Http2Headers addLong(AsciiString name, long value); + + @Override + Http2Headers addFloat(AsciiString name, float value); + + @Override + Http2Headers addDouble(AsciiString name, double value); + @Override Http2Headers add(BinaryHeaders headers); @@ -96,11 +129,44 @@ public interface Http2Headers extends BinaryHeaders { Http2Headers set(AsciiString name, AsciiString value); @Override - Http2Headers set(AsciiString name, Iterable values); + Http2Headers set(AsciiString name, Iterable values); @Override Http2Headers set(AsciiString name, AsciiString... values); + @Override + Http2Headers setObject(AsciiString name, Object value); + + @Override + Http2Headers setObject(AsciiString name, Iterable values); + + @Override + Http2Headers setObject(AsciiString name, Object... values); + + @Override + Http2Headers setBoolean(AsciiString name, boolean value); + + @Override + Http2Headers setByte(AsciiString name, byte value); + + @Override + Http2Headers setChar(AsciiString name, char value); + + @Override + Http2Headers setShort(AsciiString name, short value); + + @Override + Http2Headers setInt(AsciiString name, int value); + + @Override + Http2Headers setLong(AsciiString name, long value); + + @Override + Http2Headers setFloat(AsciiString name, float value); + + @Override + Http2Headers setDouble(AsciiString name, double value); + @Override Http2Headers set(BinaryHeaders headers); @@ -110,9 +176,6 @@ public interface Http2Headers extends BinaryHeaders { @Override Http2Headers clear(); - @Override - Http2Headers forEachEntry(BinaryHeaderVisitor visitor); - /** * Sets the {@link PseudoHeaderName#METHOD} header or {@code null} if there is no such header */ 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 a125121018..0efbe0d4da 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 @@ -25,6 +25,7 @@ import static io.netty.util.internal.ObjectUtil.checkNotNull; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.base64.Base64; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -86,8 +87,7 @@ public class Http2ServerUpgradeCodec implements HttpServerUpgradeHandler.Upgrade try { // Decode the HTTP2-Settings header and set the settings on the handler to make // sure everything is fine with the request. - String settingsHeader = upgradeRequest.headers().get(HTTP_UPGRADE_SETTINGS_HEADER); - settings = decodeSettingsHeader(ctx, settingsHeader); + settings = decodeSettingsHeader(ctx, upgradeRequest.headers().get(HTTP_UPGRADE_SETTINGS_HEADER)); connectionHandler.onHttpServerUpgrade(settings); // Everything looks good, no need to modify the response. } catch (Throwable e) { @@ -108,9 +108,9 @@ public class Http2ServerUpgradeCodec implements HttpServerUpgradeHandler.Upgrade /** * Decodes the settings header and returns a {@link Http2Settings} object. */ - private Http2Settings decodeSettingsHeader(ChannelHandlerContext ctx, String settingsHeader) + private Http2Settings decodeSettingsHeader(ChannelHandlerContext ctx, CharSequence settingsHeader) throws Http2Exception { - ByteBuf header = Unpooled.wrappedBuffer(settingsHeader.getBytes(CharsetUtil.UTF_8)); + ByteBuf header = Unpooled.wrappedBuffer(AsciiString.getBytes(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/Http2ToHttpConnectionHandler.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ToHttpConnectionHandler.java index ad161e8e41..4ac74e4fe6 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ToHttpConnectionHandler.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ToHttpConnectionHandler.java @@ -51,23 +51,10 @@ public class Http2ToHttpConnectionHandler extends Http2ConnectionHandler { * * @param httpHeaders The HTTP/1.x headers object to look for the stream id * @return The stream id to use with this {@link HttpHeaders} object - * @throws Http2Exception If the {@code httpHeaders} object specifies an invalid stream id + * @throws Exception If the {@code httpHeaders} object specifies an invalid stream id */ - private int getStreamId(HttpHeaders httpHeaders) throws Http2Exception { - int streamId = 0; - String value = httpHeaders.get(HttpUtil.ExtensionHeaderNames.STREAM_ID.text()); - if (value == null) { - streamId = connection().local().nextStreamId(); - } else { - try { - streamId = Integer.parseInt(value); - } catch (NumberFormatException e) { - throw Http2Exception.format(Http2Error.INTERNAL_ERROR, "Invalid user-specified stream id value '%s'", - value); - } - } - - return streamId; + private int getStreamId(HttpHeaders httpHeaders) throws Exception { + return httpHeaders.getInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), connection().local().nextStreamId()); } /** @@ -83,7 +70,7 @@ public class Http2ToHttpConnectionHandler extends Http2ConnectionHandler { int streamId = 0; try { streamId = getStreamId(httpMsg.headers()); - } catch (Http2Exception e) { + } catch (Exception e) { httpMsg.release(); promise.setFailure(e); return; @@ -91,16 +78,17 @@ public class Http2ToHttpConnectionHandler extends Http2ConnectionHandler { // Convert and write the headers. Http2Headers http2Headers = HttpUtil.toHttp2Headers(httpMsg); + Http2ConnectionEncoder encoder = encoder(); if (hasData) { ChannelPromiseAggregator promiseAggregator = new ChannelPromiseAggregator(promise); ChannelPromise headerPromise = ctx.newPromise(); ChannelPromise dataPromise = ctx.newPromise(); promiseAggregator.add(headerPromise, dataPromise); - encoder().writeHeaders(ctx, streamId, http2Headers, 0, false, headerPromise); - encoder().writeData(ctx, streamId, httpMsg.content(), 0, true, dataPromise); + encoder.writeHeaders(ctx, streamId, http2Headers, 0, false, headerPromise); + encoder.writeData(ctx, streamId, httpMsg.content(), 0, true, dataPromise); } else { - encoder().writeHeaders(ctx, streamId, http2Headers, 0, true, promise); + encoder.writeHeaders(ctx, streamId, http2Headers, 0, true, promise); } } else { ctx.write(msg, promise); 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 b319b73aaf..07b4009e6f 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 @@ -16,7 +16,7 @@ package io.netty.handler.codec.http2; import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.BinaryHeaders; -import io.netty.handler.codec.TextHeaderProcessor; +import io.netty.handler.codec.TextHeaders.EntryVisitor; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpMessage; @@ -29,109 +29,109 @@ 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.internal.PlatformDependent; import java.net.URI; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; /** * Provides utility methods and constants for the HTTP/2 to HTTP conversion */ -@SuppressWarnings("deprecation") public final class HttpUtil { /** * The set of headers that should not be directly copied when converting headers from HTTP to HTTP/2. */ - private static final Set HTTP_TO_HTTP2_HEADER_BLACKLIST = new HashSet(); - static { - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaders.Names.CONNECTION.toLowerCase()); - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaders.Names.KEEP_ALIVE.toLowerCase()); - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaders.Names.PROXY_CONNECTION.toLowerCase()); - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaders.Names.TRANSFER_ENCODING.toLowerCase()); - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaders.Names.HOST.toLowerCase()); - // These are already defined as lower-case. - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.STREAM_ID.text()); - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.AUTHORITY.text()); - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.SCHEME.text()); - HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.PATH.text()); - } + @SuppressWarnings("deprecation") + private static final Set HTTP_TO_HTTP2_HEADER_BLACKLIST = new HashSet() { + private static final long serialVersionUID = -5678614530214167043L; + { + add(HttpHeaders.Names.CONNECTION); + add(HttpHeaders.Names.KEEP_ALIVE); + add(HttpHeaders.Names.PROXY_CONNECTION); + add(HttpHeaders.Names.TRANSFER_ENCODING); + add(HttpHeaders.Names.HOST); + add(ExtensionHeaderNames.STREAM_ID.text()); + add(ExtensionHeaderNames.AUTHORITY.text()); + add(ExtensionHeaderNames.SCHEME.text()); + add(ExtensionHeaderNames.PATH.text()); + } + }; /** - * This will be the method used for {@link HttpRequest} objects generated - * out of the HTTP message flow defined in - * HTTP/2 Spec Message Flow + * This will be the method used for {@link HttpRequest} objects generated out of the HTTP message flow defined in HTTP/2 Spec Message Flow */ public static final HttpMethod OUT_OF_MESSAGE_SEQUENCE_METHOD = HttpMethod.OPTIONS; /** - * This will be the path used for {@link HttpRequest} objects generated - * out of the HTTP message flow defined in - * HTTP/2 Spec Message Flow + * This will be the path used for {@link HttpRequest} objects generated out of the HTTP message flow defined in HTTP/2 Spec Message Flow */ public static final String OUT_OF_MESSAGE_SEQUENCE_PATH = ""; /** - * This will be the status code used for {@link HttpResponse} objects generated - * out of the HTTP message flow defined in - * HTTP/2 Spec Message Flow + * This will be the status code used for {@link HttpResponse} objects generated out of the HTTP message flow defined + * in HTTP/2 Spec Message Flow */ public static final HttpResponseStatus OUT_OF_MESSAGE_SEQUENCE_RETURN_CODE = HttpResponseStatus.OK; - private HttpUtil() { } + private HttpUtil() { + } /** * Provides the HTTP header extensions used to carry HTTP/2 information in HTTP objects */ public enum ExtensionHeaderNames { /** - * HTTP extension header which will identify the stream id from the HTTP/2 event(s) - * responsible for generating a {@code HttpObject} + * HTTP extension header which will identify the stream id from the HTTP/2 event(s) responsible for generating a + * {@code HttpObject} *

* {@code "x-http2-stream-id"} */ STREAM_ID("x-http2-stream-id"), /** - * HTTP extension header which will identify the authority pseudo header from the HTTP/2 - * event(s) responsible for generating a {@code HttpObject} + * HTTP extension header which will identify the authority pseudo header from the HTTP/2 event(s) responsible + * for generating a {@code HttpObject} *

* {@code "x-http2-authority"} */ AUTHORITY("x-http2-authority"), /** - * HTTP extension header which will identify the scheme pseudo header from the HTTP/2 - * event(s) responsible for generating a {@code HttpObject} + * HTTP extension header which will identify the scheme pseudo header from the HTTP/2 event(s) responsible for + * generating a {@code HttpObject} *

* {@code "x-http2-scheme"} */ SCHEME("x-http2-scheme"), /** - * HTTP extension header which will identify the path pseudo header from the HTTP/2 event(s) - * responsible for generating a {@code HttpObject} + * HTTP extension header which will identify the path pseudo header from the HTTP/2 event(s) responsible for + * generating a {@code HttpObject} *

* {@code "x-http2-path"} */ PATH("x-http2-path"), /** - * HTTP extension header which will identify the stream id used to create this stream in a - * HTTP/2 push promise frame + * HTTP extension header which will identify the stream id used to create this stream in a HTTP/2 push promise + * frame *

* {@code "x-http2-stream-promise-id"} */ STREAM_PROMISE_ID("x-http2-stream-promise-id"), /** - * HTTP extension header which will identify the stream id which this stream is dependent - * on. This stream will be a child node of the stream id associated with this header value. + * HTTP extension header which will identify the stream id which this stream is dependent on. This stream will + * be a child node of the stream id associated with this header value. *

* {@code "x-http2-stream-dependency-id"} */ STREAM_DEPENDENCY_ID("x-http2-stream-dependency-id"), /** - * HTTP extension header which will identify the weight (if non-default and the priority is - * not on the default stream) of the associated HTTP/2 stream responsible responsible for - * generating a {@code HttpObject} + * HTTP extension header which will identify the weight (if non-default and the priority is not on the default + * stream) of the associated HTTP/2 stream responsible responsible for generating a {@code HttpObject} *

* {@code "x-http2-stream-weight"} */ @@ -176,21 +176,19 @@ public final class HttpUtil { * * @param streamId The stream associated with the response * @param http2Headers The initial set of HTTP/2 headers to create the response with - * @param validateHttpHeaders - *

    - *
  • {@code true} to validate HTTP headers in the http-codec
  • - *
  • {@code false} not to validate HTTP headers in the http-codec
  • - *
+ * @param validateHttpHeaders
    + *
  • {@code true} to validate HTTP headers in the http-codec
  • + *
  • {@code false} not to validate HTTP headers in the http-codec
  • + *
* @return A new response object which represents headers/data * @throws Http2Exception see {@link #addHttp2ToHttpHeaders(int, Http2Headers, FullHttpMessage, Map)} */ - public static FullHttpResponse toHttpResponse(int streamId, Http2Headers http2Headers, - boolean validateHttpHeaders) throws Http2Exception { + public static FullHttpResponse toHttpResponse(int streamId, Http2Headers http2Headers, boolean validateHttpHeaders) + throws Http2Exception { HttpResponseStatus status = parseStatus(http2Headers.status()); // HTTP/2 does not define a way to carry the version or reason phrase that is included in an // HTTP/1.1 status line. - FullHttpResponse msg = - new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, validateHttpHeaders); + FullHttpResponse msg = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, validateHttpHeaders); addHttp2ToHttpHeaders(streamId, http2Headers, msg, false); return msg; } @@ -200,11 +198,10 @@ public final class HttpUtil { * * @param streamId The stream associated with the request * @param http2Headers The initial set of HTTP/2 headers to create the request with - * @param validateHttpHeaders - *
    - *
  • {@code true} to validate HTTP headers in the http-codec
  • - *
  • {@code false} not to validate HTTP headers in the http-codec
  • - *
+ * @param validateHttpHeaders
    + *
  • {@code true} to validate HTTP headers in the http-codec
  • + *
  • {@code false} not to validate HTTP headers in the http-codec
  • + *
* @return A new request object which represents headers/data * @throws Http2Exception see {@link #addHttp2ToHttpHeaders(int, Http2Headers, FullHttpMessage, Map)} */ @@ -212,9 +209,8 @@ public final class HttpUtil { throws Http2Exception { // HTTP/2 does not define a way to carry the version identifier that is // included in the HTTP/1.1 request line. - FullHttpRequest msg = - new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(http2Headers - .method().toString()), http2Headers.path().toString(), validateHttpHeaders); + FullHttpRequest msg = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(http2Headers.method() + .toString()), http2Headers.path().toString(), validateHttpHeaders); addHttp2ToHttpHeaders(streamId, http2Headers, msg, false); return msg; } @@ -225,25 +221,26 @@ public final class HttpUtil { * @param streamId The stream associated with {@code sourceHeaders} * @param sourceHeaders The HTTP/2 headers to convert * @param destinationMessage The object which will contain the resulting HTTP/1.x headers - * @param addToTrailer {@code true} to add to trailing headers. {@code false} to add to initial - * headers. + * @param addToTrailer {@code true} to add to trailing headers. {@code false} to add to initial headers. * @throws Http2Exception If not all HTTP/2 headers can be translated to HTTP/1.x */ public static void addHttp2ToHttpHeaders(int streamId, Http2Headers sourceHeaders, - FullHttpMessage destinationMessage, boolean addToTrailer) - throws Http2Exception { + FullHttpMessage destinationMessage, boolean addToTrailer) throws Http2Exception { HttpHeaders headers = addToTrailer ? destinationMessage.trailingHeaders() : destinationMessage.headers(); boolean request = destinationMessage instanceof HttpRequest; Http2ToHttpHeaderTranslator visitor = new Http2ToHttpHeaderTranslator(headers, request); - sourceHeaders.forEachEntry(visitor); - if (visitor.cause() != null) { - throw visitor.cause(); + try { + sourceHeaders.forEachEntry(visitor); + } catch (Http2Exception ex) { + throw ex; + } catch (Exception ex) { + PlatformDependent.throwException(ex); } headers.remove(HttpHeaders.Names.TRANSFER_ENCODING); headers.remove(HttpHeaders.Names.TRAILER); if (!addToTrailer) { - headers.set(ExtensionHeaderNames.STREAM_ID.text(), streamId); + headers.setInt(ExtensionHeaderNames.STREAM_ID.text(), streamId); HttpHeaderUtil.setKeepAlive(destinationMessage, true); } } @@ -259,7 +256,7 @@ public final class HttpUtil { out.path(new AsciiString(request.uri())); out.method(new AsciiString(request.method().toString())); - String value = inHeaders.get(HttpHeaders.Names.HOST); + String value = inHeaders.getAndConvert(HttpHeaders.Names.HOST); if (value != null) { URI hostUri = URI.create(value); // The authority MUST NOT include the deprecated "userinfo" subcomponent @@ -274,15 +271,15 @@ public final class HttpUtil { } // Consume the Authority extension header if present - value = inHeaders.get(ExtensionHeaderNames.AUTHORITY.text()); - if (value != null) { - out.authority(new AsciiString(value)); + CharSequence cValue = inHeaders.get(ExtensionHeaderNames.AUTHORITY.text()); + if (cValue != null) { + out.authority(AsciiString.of(cValue)); } // Consume the Scheme extension header if present - value = inHeaders.get(ExtensionHeaderNames.SCHEME.text()); - if (value != null) { - out.scheme(new AsciiString(value)); + cValue = inHeaders.get(ExtensionHeaderNames.SCHEME.text()); + if (cValue != null) { + out.scheme(AsciiString.of(cValue)); } } else if (in instanceof HttpResponse) { HttpResponse response = (HttpResponse) in; @@ -290,90 +287,81 @@ public final class HttpUtil { } // Add the HTTP headers which have not been consumed above - inHeaders.forEachEntry(new TextHeaderProcessor() { - @Override - public boolean process(CharSequence name, CharSequence value) throws Exception { - AsciiString aName = AsciiString.of(name); - if (!HTTP_TO_HTTP2_HEADER_BLACKLIST.contains(aName.toLowerCase())) { - AsciiString aValue = AsciiString.of(value); - out.add(aName, aValue); + try { + inHeaders.forEachEntry(new EntryVisitor() { + @Override + public boolean visit(Entry entry) throws Exception { + final AsciiString aName = AsciiString.of(entry.getKey()).toLowerCase(); + if (!HTTP_TO_HTTP2_HEADER_BLACKLIST.contains(aName)) { + AsciiString aValue = AsciiString.of(entry.getValue()); + out.add(aName, aValue); + } + return true; } - return true; - } - }); + }); + } catch (Exception ex) { + PlatformDependent.throwException(ex); + } return out; } /** * A visitor which translates HTTP/2 headers to HTTP/1 headers */ - private static final class Http2ToHttpHeaderTranslator implements BinaryHeaders.BinaryHeaderVisitor { + private static final class Http2ToHttpHeaderTranslator implements BinaryHeaders.EntryVisitor { /** * 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().toString()); + ExtensionHeaderNames.AUTHORITY.text()); RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.SCHEME.value(), - ExtensionHeaderNames.SCHEME.text().toString()); + ExtensionHeaderNames.SCHEME.text()); REQUEST_HEADER_TRANSLATIONS.putAll(RESPONSE_HEADER_TRANSLATIONS); RESPONSE_HEADER_TRANSLATIONS.put(Http2Headers.PseudoHeaderName.PATH.value(), - ExtensionHeaderNames.PATH.text().toString()); + ExtensionHeaderNames.PATH.text()); } private final HttpHeaders output; - private final Map translations; - private Http2Exception e; + private final Map translations; /** * Create a new instance * * @param output The HTTP/1.x headers object to store the results of the translation - * @param request if {@code true}, translates headers using the request translation map. - * Otherwise uses the response translation map. + * @param request if {@code true}, translates headers using the request translation map. Otherwise uses the + * response translation map. */ public Http2ToHttpHeaderTranslator(HttpHeaders output, boolean request) { this.output = output; - translations = request? REQUEST_HEADER_TRANSLATIONS : RESPONSE_HEADER_TRANSLATIONS; + translations = request ? REQUEST_HEADER_TRANSLATIONS : RESPONSE_HEADER_TRANSLATIONS; } @Override - public boolean visit(AsciiString name, AsciiString value) { - String translatedName = translations.get(name); + public boolean visit(Entry entry) throws Http2Exception { + final AsciiString name = entry.getKey(); + final AsciiString value = entry.getValue(); + AsciiString translatedName = translations.get(name); if (translatedName != null || !Http2Headers.PseudoHeaderName.isPseudoHeader(name)) { if (translatedName == null) { - translatedName = name.toString(); + translatedName = name; } // http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-8.1.2.3 // All headers that start with ':' are only valid in HTTP/2 context if (translatedName.isEmpty() || translatedName.charAt(0) == ':') { - e = Http2Exception - .protocolError("Unknown HTTP/2 header '%s' encountered in translation to HTTP/1.x", - translatedName); - return false; + throw Http2Exception + .protocolError("Unknown HTTP/2 header '%s' encountered in translation to HTTP/1.x", + translatedName); } else { - output.add(translatedName, value.toString()); + output.add(translatedName, value); } } return true; } - - /** - * Get any exceptions encountered while translating HTTP/2 headers to HTTP/1.x headers - * - * @return - *
    - *
  • {@code null} if no exceptions where encountered
  • - *
  • Otherwise an exception describing what went wrong
  • - *
- */ - public Http2Exception cause() { - return e; - } } } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapter.java index 51048943fd..293435240b 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapter.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapter.java @@ -308,7 +308,7 @@ public class InboundHttp2ToHttpAdapter extends Http2EventAdapter { promisedStreamId); } - msg.headers().set(HttpUtil.ExtensionHeaderNames.STREAM_PROMISE_ID.text(), streamId); + msg.headers().setInt(HttpUtil.ExtensionHeaderNames.STREAM_PROMISE_ID.text(), streamId); processHeadersEnd(ctx, promisedStreamId, msg, false); } 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 1b1bc3e651..57505e3ac7 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 @@ -14,15 +14,18 @@ */ package io.netty.handler.codec.http2; +import java.util.Map.Entry; + import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.AsciiString; -import io.netty.handler.codec.TextHeaderProcessor; +import io.netty.handler.codec.TextHeaders.EntryVisitor; import io.netty.handler.codec.TooLongFrameException; 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.collection.IntObjectHashMap; import io.netty.util.collection.IntObjectMap; +import io.netty.util.internal.PlatformDependent; /** * Translate header/data/priority HTTP/2 frame events into HTTP events. Just as {@link InboundHttp2ToHttpAdapter} @@ -179,13 +182,17 @@ public final class InboundHttp2ToHttpPriorityAdapter extends InboundHttp2ToHttpA * @param http2Headers The target HTTP/2 headers */ private void addHttpHeadersToHttp2Headers(HttpHeaders httpHeaders, final Http2Headers http2Headers) { - httpHeaders.forEachEntry(new TextHeaderProcessor() { - @Override - public boolean process(CharSequence name, CharSequence value) throws Exception { - http2Headers.add(new AsciiString(name), new AsciiString(value)); - return true; - } - }); + try { + httpHeaders.forEachEntry(new EntryVisitor() { + @Override + public boolean visit(Entry entry) throws Exception { + http2Headers.add(AsciiString.of(entry.getKey()), AsciiString.of(entry.getValue())); + return true; + } + }); + } catch (Exception ex) { + PlatformDependent.throwException(ex); + } } @Override @@ -215,7 +222,7 @@ public final class InboundHttp2ToHttpPriorityAdapter extends InboundHttp2ToHttpA // and the HTTP message flow exists in OPEN. if (parent != null && !parent.equals(connection.connectionStream())) { HttpHeaders headers = new DefaultHttpHeaders(); - headers.set(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), parent.id()); + headers.setInt(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), parent.id()); importOutOfMessageFlowHeaders(stream.id(), headers); } } else { @@ -224,7 +231,7 @@ public final class InboundHttp2ToHttpPriorityAdapter extends InboundHttp2ToHttpA removePriorityRelatedHeaders(msg.trailingHeaders()); } else if (!parent.equals(connection.connectionStream())) { HttpHeaders headers = getActiveHeaders(msg); - headers.set(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), parent.id()); + headers.setInt(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), parent.id()); } } } @@ -242,7 +249,7 @@ public final class InboundHttp2ToHttpPriorityAdapter extends InboundHttp2ToHttpA } else { headers = getActiveHeaders(msg); } - headers.set(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), stream.weight()); + headers.setShort(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), stream.weight()); } @Override diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ToHttpConnectionHandlerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ToHttpConnectionHandlerTest.java index a63f57c9f1..ffa7bb2220 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ToHttpConnectionHandlerTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ToHttpConnectionHandlerTest.java @@ -106,7 +106,7 @@ public class DefaultHttp2ToHttpConnectionHandlerTest { final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, "/example"); try { final HttpHeaders httpHeaders = request.headers(); - httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); + httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); httpHeaders.set(HttpHeaders.Names.HOST, "http://my-user_name@www.example.org:5555/example"); httpHeaders.set(HttpUtil.ExtensionHeaderNames.AUTHORITY.text(), "www.example.org:5555"); 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 af505517f6..e03e1966f0 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 @@ -169,8 +169,8 @@ public class InboundHttp2ToHttpAdapterTest { HttpHeaders httpHeaders = request.headers(); httpHeaders.set(HttpUtil.ExtensionHeaderNames.SCHEME.text(), "https"); httpHeaders.set(HttpUtil.ExtensionHeaderNames.AUTHORITY.text(), "example.org"); - httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, 0); + httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); + httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0); final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")).scheme(as("https")) .authority(as("example.org")) @@ -200,8 +200,8 @@ public class InboundHttp2ToHttpAdapterTest { "/some/path/resource2", content, true); try { HttpHeaders httpHeaders = request.headers(); - httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); + httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); + httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length()); final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")) .path(as("/some/path/resource2")); runInChannel(clientChannel, new Http2Runnable() { @@ -230,8 +230,8 @@ public class InboundHttp2ToHttpAdapterTest { "/some/path/resource2", content, true); try { HttpHeaders httpHeaders = request.headers(); - httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); + httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); + httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length()); final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")) .path(as("/some/path/resource2")); final int midPoint = text.length() / 2; @@ -264,8 +264,8 @@ public class InboundHttp2ToHttpAdapterTest { "/some/path/resource2", content, true); try { HttpHeaders httpHeaders = request.headers(); - httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); + httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); + httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length()); final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("GET")) .path(as("/some/path/resource2")); runInChannel(clientChannel, new Http2Runnable() { @@ -298,8 +298,8 @@ public class InboundHttp2ToHttpAdapterTest { "/some/path/resource2", content, true); try { HttpHeaders httpHeaders = request.headers(); - httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); + httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); + httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length()); HttpHeaders trailingHeaders = request.trailingHeaders(); trailingHeaders.set("FoO", "goo"); trailingHeaders.set("foO2", "goo2"); @@ -338,8 +338,8 @@ public class InboundHttp2ToHttpAdapterTest { "/some/path/resource2", content, true); try { HttpHeaders httpHeaders = request.headers(); - httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); + httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); + httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length()); HttpHeaders trailingHeaders = request.trailingHeaders(); trailingHeaders.set("Foo", "goo"); trailingHeaders.set("fOo2", "goo2"); @@ -383,13 +383,13 @@ public class InboundHttp2ToHttpAdapterTest { "/some/path/resource2", content2, true); try { HttpHeaders httpHeaders = request.headers(); - httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); + httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); + httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length()); HttpHeaders httpHeaders2 = request2.headers(); - httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3); - httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), 123); - httpHeaders2.set(HttpHeaders.Names.CONTENT_LENGTH, text2.length()); + httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); + httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3); + httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), 123); + httpHeaders2.setInt(HttpHeaders.Names.CONTENT_LENGTH, text2.length()); final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("PUT")) .path(as("/some/path/resource")); final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(as("PUT")) @@ -432,20 +432,20 @@ public class InboundHttp2ToHttpAdapterTest { HttpUtil.OUT_OF_MESSAGE_SEQUENCE_METHOD, HttpUtil.OUT_OF_MESSAGE_SEQUENCE_PATH, true); try { HttpHeaders httpHeaders = request.headers(); - httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); + httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); + httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length()); HttpHeaders httpHeaders2 = request2.headers(); - httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders2.set(HttpHeaders.Names.CONTENT_LENGTH, text2.length()); + httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); + httpHeaders2.setInt(HttpHeaders.Names.CONTENT_LENGTH, text2.length()); final Http2Headers http2Headers = new DefaultHttp2Headers().method(as("PUT")) .path(as("/some/path/resource")); final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(as("PUT")) .path(as("/some/path/resource2")); HttpHeaders httpHeaders3 = request3.headers(); - httpHeaders3.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders3.set(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3); - httpHeaders3.set(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), 222); - httpHeaders3.set(HttpHeaders.Names.CONTENT_LENGTH, 0); + httpHeaders3.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); + httpHeaders3.setInt(HttpUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 3); + httpHeaders3.setInt(HttpUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), 222); + httpHeaders3.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0); runInChannel(clientChannel, new Http2Runnable() { @Override public void run() { @@ -486,18 +486,18 @@ public class InboundHttp2ToHttpAdapterTest { HttpMethod.GET, "/push/test", true); try { HttpHeaders httpHeaders = response.headers(); - httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); + httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); + httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length()); HttpHeaders httpHeaders2 = response2.headers(); httpHeaders2.set(HttpUtil.ExtensionHeaderNames.SCHEME.text(), "https"); httpHeaders2.set(HttpUtil.ExtensionHeaderNames.AUTHORITY.text(), "example.org"); - httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); - httpHeaders2.set(HttpUtil.ExtensionHeaderNames.STREAM_PROMISE_ID.text(), 3); - httpHeaders2.set(HttpHeaders.Names.CONTENT_LENGTH, text2.length()); + httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); + httpHeaders2.setInt(HttpUtil.ExtensionHeaderNames.STREAM_PROMISE_ID.text(), 3); + httpHeaders2.setInt(HttpHeaders.Names.CONTENT_LENGTH, text2.length()); httpHeaders = request.headers(); - httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, 0); + httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); + httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0); final Http2Headers http2Headers3 = new DefaultHttp2Headers().method(as("GET")) .path(as("/push/test")); runInChannel(clientChannel, new Http2Runnable() { @@ -545,9 +545,9 @@ public class InboundHttp2ToHttpAdapterTest { final FullHttpMessage request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "/info/test", true); HttpHeaders httpHeaders = request.headers(); - httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); + httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); httpHeaders.set(HttpHeaders.Names.EXPECT, HttpHeaders.Values.CONTINUE); - httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, 0); + httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0); final Http2Headers http2Headers = new DefaultHttp2Headers() .method(as("PUT")) @@ -577,8 +577,8 @@ public class InboundHttp2ToHttpAdapterTest { reset(serverListener); httpHeaders = response.headers(); - httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, 0); + httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); + httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0); final Http2Headers http2HeadersResponse = new DefaultHttp2Headers().status(as("100")); runInChannel(serverConnectedChannel, new Http2Runnable() { @Override @@ -597,7 +597,7 @@ public class InboundHttp2ToHttpAdapterTest { setServerLatch(1); httpHeaders = request2.headers(); - httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, text.length()); + httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, text.length()); httpHeaders.remove(HttpHeaders.Names.EXPECT); runInChannel(clientChannel, new Http2Runnable() { @Override @@ -614,8 +614,8 @@ public class InboundHttp2ToHttpAdapterTest { setClientLatch(1); httpHeaders = response2.headers(); - httpHeaders.set(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); - httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, 0); + httpHeaders.setInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), 3); + httpHeaders.setInt(HttpHeaders.Names.CONTENT_LENGTH, 0); final Http2Headers http2HeadersResponse2 = new DefaultHttp2Headers().status(as("200")); runInChannel(serverConnectedChannel, new Http2Runnable() { @Override diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompHeaders.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompHeaders.java index 7df231a82f..dcf26bb290 100644 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompHeaders.java +++ b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/DefaultStompHeaders.java @@ -17,29 +17,94 @@ package io.netty.handler.codec.stomp; import io.netty.handler.codec.DefaultTextHeaders; -import io.netty.handler.codec.TextHeaderProcessor; import io.netty.handler.codec.TextHeaders; public class DefaultStompHeaders extends DefaultTextHeaders implements StompHeaders { @Override - public StompHeaders add(CharSequence name, Object value) { + public StompHeaders add(CharSequence name, CharSequence value) { super.add(name, value); return this; } @Override - public StompHeaders add(CharSequence name, Iterable values) { + public StompHeaders add(CharSequence name, Iterable values) { super.add(name, values); return this; } @Override - public StompHeaders add(CharSequence name, Object... values) { + public StompHeaders add(CharSequence name, CharSequence... values) { super.add(name, values); return this; } + @Override + public StompHeaders addObject(CharSequence name, Object value) { + super.addObject(name, value); + return this; + } + + @Override + public StompHeaders addObject(CharSequence name, Iterable values) { + super.addObject(name, values); + return this; + } + + @Override + public StompHeaders addObject(CharSequence name, Object... values) { + super.addObject(name, values); + return this; + } + + @Override + public StompHeaders addBoolean(CharSequence name, boolean value) { + super.addBoolean(name, value); + return this; + } + + @Override + public StompHeaders addChar(CharSequence name, char value) { + super.addChar(name, value); + return this; + } + + @Override + public StompHeaders addByte(CharSequence name, byte value) { + super.addByte(name, value); + return this; + } + + @Override + public StompHeaders addShort(CharSequence name, short value) { + super.addShort(name, value); + return this; + } + + @Override + public StompHeaders addInt(CharSequence name, int value) { + super.addInt(name, value); + return this; + } + + @Override + public StompHeaders addLong(CharSequence name, long value) { + super.addLong(name, value); + return this; + } + + @Override + public StompHeaders addFloat(CharSequence name, float value) { + super.addFloat(name, value); + return this; + } + + @Override + public StompHeaders addDouble(CharSequence name, double value) { + super.addDouble(name, value); + return this; + } + @Override public StompHeaders add(TextHeaders headers) { super.add(headers); @@ -47,23 +112,89 @@ public class DefaultStompHeaders extends DefaultTextHeaders implements StompHead } @Override - public StompHeaders set(CharSequence name, Object value) { + public StompHeaders set(CharSequence name, CharSequence value) { super.set(name, value); return this; } @Override - public StompHeaders set(CharSequence name, Object... values) { + public StompHeaders set(CharSequence name, Iterable values) { super.set(name, values); return this; } @Override - public StompHeaders set(CharSequence name, Iterable values) { + public StompHeaders set(CharSequence name, CharSequence... values) { super.set(name, values); return this; } + @Override + public StompHeaders setObject(CharSequence name, Object value) { + super.setObject(name, value); + return this; + } + + @Override + public StompHeaders setObject(CharSequence name, Iterable values) { + super.setObject(name, values); + return this; + } + + @Override + public StompHeaders setObject(CharSequence name, Object... values) { + super.setObject(name, values); + return this; + } + + @Override + public StompHeaders setBoolean(CharSequence name, boolean value) { + super.setBoolean(name, value); + return this; + } + + @Override + public StompHeaders setChar(CharSequence name, char value) { + super.setChar(name, value); + return this; + } + + @Override + public StompHeaders setByte(CharSequence name, byte value) { + super.setByte(name, value); + return this; + } + + @Override + public StompHeaders setShort(CharSequence name, short value) { + super.setShort(name, value); + return this; + } + + @Override + public StompHeaders setInt(CharSequence name, int value) { + super.setInt(name, value); + return this; + } + + @Override + public StompHeaders setLong(CharSequence name, long value) { + super.setLong(name, value); + return this; + } + + @Override + public StompHeaders setFloat(CharSequence name, float value) { + super.setFloat(name, value); + return this; + } + + @Override + public StompHeaders setDouble(CharSequence name, double value) { + super.setDouble(name, value); + return this; + } + @Override public StompHeaders set(TextHeaders headers) { super.set(headers); @@ -71,14 +202,14 @@ public class DefaultStompHeaders extends DefaultTextHeaders implements StompHead } @Override - public StompHeaders clear() { - super.clear(); + public StompHeaders setAll(TextHeaders headers) { + super.setAll(headers); return this; } @Override - public StompHeaders forEachEntry(TextHeaderProcessor processor) { - super.forEachEntry(processor); + public StompHeaders clear() { + super.clear(); return this; } } 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 b929bb67dc..839103e3a5 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 @@ -16,7 +16,6 @@ package io.netty.handler.codec.stomp; import io.netty.handler.codec.AsciiString; -import io.netty.handler.codec.TextHeaderProcessor; import io.netty.handler.codec.TextHeaders; /** @@ -46,32 +45,98 @@ public interface StompHeaders extends TextHeaders { AsciiString CONTENT_TYPE = new AsciiString("content-type"); @Override - StompHeaders add(CharSequence name, Object value); + StompHeaders add(CharSequence name, CharSequence value); @Override - StompHeaders add(CharSequence name, Iterable values); + StompHeaders add(CharSequence name, Iterable values); @Override - StompHeaders add(CharSequence name, Object... values); + StompHeaders add(CharSequence name, CharSequence... values); + + @Override + StompHeaders addObject(CharSequence name, Object value); + + @Override + StompHeaders addObject(CharSequence name, Iterable values); + + @Override + StompHeaders addObject(CharSequence name, Object... values); + + @Override + StompHeaders addBoolean(CharSequence name, boolean value); + + @Override + StompHeaders addByte(CharSequence name, byte value); + + @Override + StompHeaders addChar(CharSequence name, char value); + + @Override + StompHeaders addShort(CharSequence name, short value); + + @Override + StompHeaders addInt(CharSequence name, int value); + + @Override + StompHeaders addLong(CharSequence name, long value); + + @Override + StompHeaders addFloat(CharSequence name, float value); + + @Override + StompHeaders addDouble(CharSequence name, double value); @Override StompHeaders add(TextHeaders headers); @Override - StompHeaders set(CharSequence name, Object value); + StompHeaders set(CharSequence name, CharSequence value); @Override - StompHeaders set(CharSequence name, Iterable values); + StompHeaders set(CharSequence name, Iterable values); @Override - StompHeaders set(CharSequence name, Object... values); + StompHeaders set(CharSequence name, CharSequence... values); + + @Override + StompHeaders setObject(CharSequence name, Object value); + + @Override + StompHeaders setObject(CharSequence name, Iterable values); + + @Override + StompHeaders setObject(CharSequence name, Object... values); + + @Override + StompHeaders setBoolean(CharSequence name, boolean value); + + @Override + StompHeaders setByte(CharSequence name, byte value); + + @Override + StompHeaders setChar(CharSequence name, char value); + + @Override + StompHeaders setShort(CharSequence name, short value); + + @Override + StompHeaders setInt(CharSequence name, int value); + + @Override + StompHeaders setLong(CharSequence name, long value); + + @Override + StompHeaders setFloat(CharSequence name, float value); + + @Override + StompHeaders setDouble(CharSequence name, double value); @Override StompHeaders set(TextHeaders headers); @Override - StompHeaders clear(); + StompHeaders setAll(TextHeaders headers); @Override - StompHeaders forEachEntry(TextHeaderProcessor processor); + StompHeaders clear(); } diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeAggregator.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeAggregator.java index 217e883037..99f728efb9 100644 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeAggregator.java +++ b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeAggregator.java @@ -69,8 +69,7 @@ public class StompSubframeAggregator @Override protected long contentLength(StompHeadersSubframe start) throws Exception { - String value = start.headers().get(StompHeaders.CONTENT_LENGTH); - return Long.parseLong(value); + return start.headers().getLong(StompHeaders.CONTENT_LENGTH, 0); } @Override diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeDecoder.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeDecoder.java index e24de2d60c..101ea6aaca 100644 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeDecoder.java +++ b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeDecoder.java @@ -219,15 +219,7 @@ public class StompSubframeDecoder extends ReplayingDecoder { } private static long getContentLength(StompHeaders headers, long defaultValue) { - String contentLength = headers.get(StompHeaders.CONTENT_LENGTH); - if (contentLength != null) { - try { - return Long.parseLong(contentLength); - } catch (NumberFormatException ignored) { - return defaultValue; - } - } - return defaultValue; + return headers.getLong(StompHeaders.CONTENT_LENGTH, defaultValue); } private static void skipNullCharacter(ByteBuf buffer) { diff --git a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeEncoder.java b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeEncoder.java index 4da3739967..668bd48f37 100644 --- a/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeEncoder.java +++ b/codec-stomp/src/main/java/io/netty/handler/codec/stomp/StompSubframeEncoder.java @@ -22,6 +22,7 @@ import io.netty.handler.codec.AsciiHeadersEncoder.NewlineType; import io.netty.handler.codec.AsciiHeadersEncoder.SeparatorType; import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.util.CharsetUtil; +import io.netty.util.internal.PlatformDependent; import java.util.List; @@ -65,7 +66,12 @@ public class StompSubframeEncoder extends MessageToMessageEncoder buf.writeBytes(frame.command().toString().getBytes(CharsetUtil.US_ASCII)); buf.writeByte(StompConstants.LF); - frame.headers().forEachEntry(new AsciiHeadersEncoder(buf, SeparatorType.COLON, NewlineType.LF)); + try { + frame.headers().forEachEntry(new AsciiHeadersEncoder(buf, SeparatorType.COLON, NewlineType.LF)); + } catch (Exception ex) { + buf.release(); + PlatformDependent.throwException(ex); + } buf.writeByte(StompConstants.LF); return buf; } 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 d1ae85cdbc..805603ffb3 100644 --- a/codec/src/main/java/io/netty/handler/codec/AsciiHeadersEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/AsciiHeadersEncoder.java @@ -17,9 +17,12 @@ package io.netty.handler.codec; -import io.netty.buffer.ByteBuf; +import java.util.Map.Entry; -public final class AsciiHeadersEncoder implements TextHeaderProcessor { +import io.netty.buffer.ByteBuf; +import io.netty.handler.codec.TextHeaders.EntryVisitor; + +public final class AsciiHeadersEncoder implements EntryVisitor { /** * The separator characters to insert between a header name and a header value. @@ -74,7 +77,9 @@ public final class AsciiHeadersEncoder implements TextHeaderProcessor { } @Override - public boolean process(CharSequence name, CharSequence value) throws Exception { + public boolean visit(Entry entry) throws Exception { + final CharSequence name = entry.getKey(); + final CharSequence value = entry.getValue(); final ByteBuf buf = this.buf; final int nameLen = name.length(); final int valueLen = value.length(); diff --git a/codec/src/main/java/io/netty/handler/codec/AsciiString.java b/codec/src/main/java/io/netty/handler/codec/AsciiString.java index 1ea7309176..7568c5e8c8 100644 --- a/codec/src/main/java/io/netty/handler/codec/AsciiString.java +++ b/codec/src/main/java/io/netty/handler/codec/AsciiString.java @@ -16,9 +16,11 @@ package io.netty.handler.codec; import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.netty.util.internal.EmptyArrays; import java.nio.ByteBuffer; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -27,10 +29,10 @@ import java.util.regex.Pattern; 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}. + * 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}. */ public final class AsciiString implements CharSequence, Comparable { @@ -41,75 +43,140 @@ public final class AsciiString implements CharSequence, Comparable return CHARSEQUENCE_CASE_INSENSITIVE_ORDER.compare(o1, o2); } }; + public static final Comparator CASE_SENSITIVE_ORDER = new Comparator() { + @Override + public int compare(AsciiString o1, AsciiString o2) { + return CHARSEQUENCE_CASE_SENSITIVE_ORDER.compare(o1, o2); + } + }; - public static final Comparator CHARSEQUENCE_CASE_INSENSITIVE_ORDER = - new Comparator() { - @Override - public int compare(CharSequence o1, CharSequence o2) { - if (o1 == o2) { - return 0; + public static final Comparator CHARSEQUENCE_CASE_INSENSITIVE_ORDER = new Comparator() { + @Override + public int compare(CharSequence o1, CharSequence o2) { + if (o1 == o2) { + return 0; + } + + AsciiString a1 = o1 instanceof AsciiString ? (AsciiString) o1 : null; + AsciiString a2 = o2 instanceof AsciiString ? (AsciiString) o2 : null; + + int result; + int length1 = o1.length(); + int length2 = o2.length(); + int minLength = Math.min(length1, length2); + if (a1 != null && a2 != null) { + byte[] thisValue = a1.value; + byte[] thatValue = a2.value; + for (int i = 0; i < minLength; i++) { + byte v1 = thisValue[i]; + byte v2 = thatValue[i]; + if (v1 == v2) { + continue; } - - AsciiString a1 = o1 instanceof AsciiString ? (AsciiString) o1 : null; - AsciiString a2 = o2 instanceof AsciiString ? (AsciiString) o2 : null; - - int result; - int length1 = o1.length(); - int length2 = o2.length(); - int minLength = Math.min(length1, length2); - if (a1 != null && a2 != null) { - byte[] thisValue = a1.value; - byte[] thatValue = a2.value; - for (int i = 0; i < minLength; i++) { - byte v1 = thisValue[i]; - byte v2 = thatValue[i]; - if (v1 == v2) { - continue; - } - int c1 = toLowerCase(v1) & 0xFF; - int c2 = toLowerCase(v2) & 0xFF; - result = c1 - c2; - if (result != 0) { - return result; - } - } - } else if (a1 != null) { - byte[] thisValue = a1.value; - for (int i = 0; i < minLength; i++) { - int c1 = toLowerCase(thisValue[i]) & 0xFF; - int c2 = toLowerCase(o2.charAt(i)); - result = c1 - c2; - if (result != 0) { - return result; - } - } - } else if (a2 != null) { - byte[] thatValue = a2.value; - for (int i = 0; i < minLength; i++) { - int c1 = toLowerCase(o1.charAt(i)); - int c2 = toLowerCase(thatValue[i]) & 0xFF; - result = c1 - c2; - if (result != 0) { - return result; - } - } - } else { - for (int i = 0; i < minLength; i++) { - int c1 = toLowerCase(o1.charAt(i)); - int c2 = toLowerCase(o2.charAt(i)); - result = c1 - c2; - if (result != 0) { - return result; - } - } + int c1 = toLowerCase(v1) & 0xFF; + int c2 = toLowerCase(v2) & 0xFF; + result = c1 - c2; + if (result != 0) { + return result; } - - return length1 - length2; } - }; + } else if (a1 != null) { + byte[] thisValue = a1.value; + for (int i = 0; i < minLength; i++) { + int c1 = toLowerCase(thisValue[i]) & 0xFF; + int c2 = toLowerCase(o2.charAt(i)); + result = c1 - c2; + if (result != 0) { + return result; + } + } + } else if (a2 != null) { + byte[] thatValue = a2.value; + for (int i = 0; i < minLength; i++) { + int c1 = toLowerCase(o1.charAt(i)); + int c2 = toLowerCase(thatValue[i]) & 0xFF; + result = c1 - c2; + if (result != 0) { + return result; + } + } + } else { + for (int i = 0; i < minLength; i++) { + int c1 = toLowerCase(o1.charAt(i)); + int c2 = toLowerCase(o2.charAt(i)); + result = c1 - c2; + if (result != 0) { + return result; + } + } + } + + return length1 - length2; + } + }; + + public static final Comparator CHARSEQUENCE_CASE_SENSITIVE_ORDER = new Comparator() { + @Override + public int compare(CharSequence o1, CharSequence o2) { + if (o1 == o2) { + return 0; + } + + AsciiString a1 = o1 instanceof AsciiString ? (AsciiString) o1 : null; + AsciiString a2 = o2 instanceof AsciiString ? (AsciiString) o2 : null; + + int result; + int length1 = o1.length(); + int length2 = o2.length(); + int minLength = Math.min(length1, length2); + if (a1 != null && a2 != null) { + byte[] thisValue = a1.value; + byte[] thatValue = a2.value; + for (int i = 0; i < minLength; i++) { + byte v1 = thisValue[i]; + byte v2 = thatValue[i]; + result = v1 - v2; + if (result != 0) { + return result; + } + } + } else if (a1 != null) { + byte[] thisValue = a1.value; + for (int i = 0; i < minLength; i++) { + int c1 = thisValue[i]; + int c2 = o2.charAt(i); + result = c1 - c2; + if (result != 0) { + return result; + } + } + } else if (a2 != null) { + byte[] thatValue = a2.value; + for (int i = 0; i < minLength; i++) { + int c1 = o1.charAt(i); + int c2 = thatValue[i]; + result = c1 - c2; + if (result != 0) { + return result; + } + } + } else { + for (int i = 0; i < minLength; i++) { + int c1 = o1.charAt(i); + int c2 = o2.charAt(i); + result = c1 - c2; + if (result != 0) { + return result; + } + } + } + + return length1 - length2; + } + }; /** - * Returns the case-insensitive hash code of the specified string. Note that this method uses the same hashing + * Returns the case-insensitive hash code of the specified string. Note that this method uses the same hashing * algorithm with {@link #hashCode()} so that you can put both {@link AsciiString}s and arbitrary * {@link CharSequence}s into the same {@link TextHeaders}. */ @@ -120,7 +187,7 @@ public final class AsciiString implements CharSequence, Comparable int hash = 0; final int end = value.length(); - for (int i = 0; i < end; i ++) { + for (int i = 0; i < end; i++) { hash = hash * 31 ^ value.charAt(i) & 31; } @@ -128,8 +195,8 @@ public final class AsciiString implements CharSequence, Comparable } /** - * Returns {@code true} if both {@link CharSequence}'s are equals when ignore the case. - * This only supports 8-bit ASCII. + * Returns {@code true} if both {@link CharSequence}'s are equals when ignore the case. This only supports 8-bit + * ASCII. */ public static boolean equalsIgnoreCase(CharSequence a, CharSequence b) { if (a == b) { @@ -178,9 +245,31 @@ 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; + } + /** - * Returns an {@link AsciiString} containing the given character sequence. If the given string - * is already a {@link AsciiString}, just returns the same instance. + * Returns an {@link AsciiString} containing the given character sequence. If the given string is already a + * {@link AsciiString}, just returns the same instance. */ public static AsciiString of(CharSequence string) { return string instanceof AsciiString ? (AsciiString) string : new AsciiString(string); @@ -210,9 +299,8 @@ public final class AsciiString implements CharSequence, Comparable 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 + ')'); + throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + + ") <= " + "value.length(" + value.length + ')'); } if (copy || start != 0 || length != value.length) { @@ -229,13 +317,12 @@ public final class AsciiString implements CharSequence, Comparable public AsciiString(char[] value, int start, int length) { checkNull(value); if (start < 0 || start > value.length - length) { - throw new IndexOutOfBoundsException("expected: " + - "0 <= start(" + start + ") <= start + length(" + length + ") <= " + - "value.length(" + value.length + ')'); + throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + + ") <= " + "value.length(" + value.length + ')'); } this.value = new byte[length]; - for (int i = 0, j = start; i < length; i ++, j ++) { + for (int i = 0, j = start; i < length; i++, j++) { this.value[i] = c2b(value[j]); } } @@ -250,9 +337,8 @@ public final class AsciiString implements CharSequence, Comparable } if (start < 0 || length < 0 || length > value.length() - start) { - throw new IndexOutOfBoundsException("expected: " + - "0 <= start(" + start + ") <= start + length(" + length + ") <= " + - "value.length(" + value.length() + ')'); + throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + + ") <= " + "value.length(" + value.length() + ')'); } this.value = new byte[length]; @@ -271,9 +357,8 @@ public final class AsciiString implements CharSequence, Comparable } if (start < 0 || length > value.capacity() - start) { - throw new IndexOutOfBoundsException("expected: " + - "0 <= start(" + start + ") <= start + length(" + length + ") <= " + - "value.capacity(" + value.capacity() + ')'); + throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length + + ") <= " + "value.capacity(" + value.capacity() + ')'); } if (value.hasArray()) { @@ -347,12 +432,9 @@ public final class AsciiString implements CharSequence, Comparable /** * 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()}. + * @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()); @@ -361,8 +443,8 @@ public final class AsciiString implements CharSequence, Comparable @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() + ')'); + throw new IndexOutOfBoundsException("expected: 0 <= start(" + start + ") <= end (" + end + ") <= length(" + + length() + ')'); } final byte[] value = this.value; @@ -385,8 +467,8 @@ public final class AsciiString implements CharSequence, Comparable return hash; } - for (byte b: value) { - hash = hash * 31 ^ b & 31; + for (int i = 0; i < value.length; ++i) { + hash = hash * 31 ^ value[i] & 31; } return this.hash = hash; @@ -412,7 +494,7 @@ public final class AsciiString implements CharSequence, Comparable byte[] thisValue = value; byte[] thatValue = that.value; int end = thisValue.length; - for (int i = 0, j = 0; i < end; i ++, j ++) { + for (int i = 0, j = 0; i < end; i++, j++) { if (thisValue[i] != thatValue[j]) { return false; } @@ -449,24 +531,17 @@ public final class AsciiString implements CharSequence, Comparable } /** - * Compares the specified string to this string using the ASCII values of - * the characters. Returns 0 if the strings contain the same characters in - * the same order. Returns a negative integer if the first non-equal - * character in this string has an ASCII value which is less than the - * ASCII value of the character at the same position in the specified - * string, or if this string is a prefix of the specified string. Returns a - * positive integer if the first non-equal character in this string has a - * ASCII value which is greater than the ASCII value of the character at - * the same position in the specified string, or if the specified string is - * a prefix of this string. + * Compares the specified string to this string using the ASCII values of the characters. Returns 0 if the strings + * contain the same characters in the same order. Returns a negative integer if the first non-equal character in + * this string has an ASCII value which is less than the ASCII value of the character at the same position in the + * specified string, or if this string is a prefix of the specified string. Returns a positive integer if the first + * non-equal character in this string has a ASCII value which is greater than the ASCII value of the character at + * the same position in the specified string, or if the specified string is a prefix of this string. * - * @param string - * the string to compare. - * @return 0 if the strings are equal, a negative integer if this string is - * before the specified string, or a positive integer if this string - * is after the specified string. - * @throws NullPointerException - * if {@code string} is {@code null}. + * @param string the string to compare. + * @return 0 if the strings are equal, a negative integer if this string is before the specified string, or a + * positive integer if this string is after the specified string. + * @throws NullPointerException if {@code string} is {@code null}. */ @Override public int compareTo(CharSequence string) { @@ -479,7 +554,7 @@ public final class AsciiString implements CharSequence, Comparable int length2 = string.length(); int minLength = Math.min(length1, length2); byte[] value = this.value; - for (int i = 0, j = 0; j < minLength; i ++, j ++) { + for (int i = 0, j = 0; j < minLength; i++, j++) { result = (value[i] & 0xFF) - string.charAt(j); if (result != 0) { return result; @@ -490,36 +565,28 @@ public final class AsciiString implements CharSequence, Comparable } /** - * Compares the specified string to this string using the ASCII values of - * the characters, ignoring case differences. Returns 0 if the strings - * contain the same characters in the same order. Returns a negative integer - * if the first non-equal character in this string has an ASCII value which - * is less than the ASCII value of the character at the same position in - * the specified string, or if this string is a prefix of the specified - * string. Returns a positive integer if the first non-equal character in - * this string has an ASCII value which is greater than the ASCII value - * of the character at the same position in the specified string, or if the - * specified string is a prefix of this string. + * Compares the specified string to this string using the ASCII values of the characters, ignoring case differences. + * Returns 0 if the strings contain the same characters in the same order. Returns a negative integer if the first + * non-equal character in this string has an ASCII value which is less than the ASCII value of the character at the + * same position in the specified string, or if this string is a prefix of the specified string. Returns a positive + * integer if the first non-equal character in this string has an ASCII value which is greater than the ASCII value + * of the character at the same position in the specified string, or if the specified string is a prefix of this + * string. * - * @param string - * the string to compare. - * @return 0 if the strings are equal, a negative integer if this string is - * before the specified string, or a positive integer if this string - * is after the specified string. - * @throws NullPointerException - * if {@code string} is {@code null}. + * @param string the string to compare. + * @return 0 if the strings are equal, a negative integer if this string is before the specified string, or a + * positive integer if this string is after the specified string. + * @throws NullPointerException if {@code string} is {@code null}. */ public int compareToIgnoreCase(CharSequence string) { - return CHARSEQUENCE_CASE_INSENSITIVE_ORDER.compare(this, string); + return CHARSEQUENCE_CASE_INSENSITIVE_ORDER.compare(this, string); } /** * Concatenates this string and the specified string. * - * @param string - * the string to concatenate - * @return a new string which is the concatenation of this string and the - * specified string. + * @param string the string to concatenate + * @return a new string which is the concatenation of this string and the specified string. */ public AsciiString concat(CharSequence string) { int thisLen = length(); @@ -546,7 +613,7 @@ public final class AsciiString implements CharSequence, Comparable int newLen = thisLen + thatLen; byte[] newValue = Arrays.copyOf(value, newLen); - for (int i = thisLen, j = 0; i < newLen; i ++, j ++) { + for (int i = thisLen, j = 0; i < newLen; i++, j++) { newValue[i] = c2b(string.charAt(j)); } @@ -554,15 +621,11 @@ public final class AsciiString implements CharSequence, Comparable } /** - * Compares the specified string to this string to determine if the - * specified string is a suffix. + * Compares the specified string to this string to determine if the specified string is a suffix. * - * @param suffix - * the suffix to look for. - * @return {@code true} if the specified string is a suffix of this string, - * {@code false} otherwise. - * @throws NullPointerException - * if {@code suffix} is {@code null}. + * @param suffix the suffix to look for. + * @return {@code true} if the specified string is a suffix of this string, {@code false} otherwise. + * @throws NullPointerException if {@code suffix} is {@code null}. */ public boolean endsWith(CharSequence suffix) { int suffixLen = suffix.length(); @@ -570,13 +633,11 @@ public final class AsciiString implements CharSequence, Comparable } /** - * Compares the specified string to this string ignoring the case of the - * characters and returns true if they are equal. + * Compares the specified string to this string ignoring the case of the characters and returns true if they are + * equal. * - * @param string - * the string to compare. - * @return {@code true} if the specified string is equal to this string, - * {@code false} otherwise. + * @param string the string to compare. + * @return {@code true} if the specified string is equal to this string, {@code false} otherwise. */ public boolean equalsIgnoreCase(CharSequence string) { if (string == this) { @@ -594,7 +655,7 @@ public final class AsciiString implements CharSequence, Comparable return false; } - for (int i = 0; i < thisLen; i ++) { + for (int i = 0; i < thisLen; i++) { char c1 = (char) (value[i] & 0xFF); char c2 = string.charAt(i); if (c1 != c2 && toLowerCase(c1) != toLowerCase(c2)) { @@ -644,7 +705,7 @@ public final class AsciiString implements CharSequence, Comparable final byte[] value = this.value; final char[] buffer = new char[length]; - for (int i = 0, j = start; i < length; i ++, j ++) { + for (int i = 0, j = start; i < length; i++, j++) { buffer[i] = (char) (value[j] & 0xFF); } return buffer; @@ -653,14 +714,10 @@ public final class AsciiString implements CharSequence, Comparable /** * 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. + * @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) { @@ -671,8 +728,8 @@ public final class AsciiString implements CharSequence, Comparable final int thisLen = value.length; if (srcIdx < 0 || length > thisLen - srcIdx) { - throw new IndexOutOfBoundsException("expected: " + - "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + length + ") <= srcLen(" + thisLen + ')'); + throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + + length + ") <= srcLen(" + thisLen + ')'); } dst.setBytes(dstIdx, value, srcIdx, length); @@ -681,12 +738,9 @@ public final class AsciiString implements CharSequence, Comparable /** * 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. + * @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) { @@ -697,8 +751,8 @@ public final class AsciiString implements CharSequence, Comparable final int thisLen = value.length; if (srcIdx < 0 || length > thisLen - srcIdx) { - throw new IndexOutOfBoundsException("expected: " + - "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + length + ") <= srcLen(" + thisLen + ')'); + throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + + length + ") <= srcLen(" + thisLen + ')'); } dst.writeBytes(value, srcIdx, length); @@ -707,14 +761,10 @@ public final class AsciiString implements CharSequence, Comparable /** * 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. + * @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) { @@ -725,8 +775,8 @@ public final class AsciiString implements CharSequence, Comparable final int thisLen = value.length; if (srcIdx < 0 || length > thisLen - srcIdx) { - throw new IndexOutOfBoundsException("expected: " + - "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + length + ") <= srcLen(" + thisLen + ')'); + throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + + length + ") <= srcLen(" + thisLen + ')'); } System.arraycopy(value, srcIdx, dst, dstIdx, length); @@ -735,14 +785,10 @@ public final class AsciiString implements CharSequence, Comparable /** * Copied the content of this string to a character array. * - * @param srcIdx - * the starting offset of characters to copy. - * @param dst - * the destination character array. - * @param dstIdx - * the starting offset in the destination byte array. - * @param length - * the number of characters to copy. + * @param srcIdx the starting offset of characters to copy. + * @param dst the destination character array. + * @param dstIdx the starting offset in the destination byte array. + * @param length the number of characters to copy. */ public void copy(int srcIdx, char[] dst, int dstIdx, int length) { if (dst == null) { @@ -753,41 +799,34 @@ public final class AsciiString implements CharSequence, Comparable final int thisLen = value.length; if (srcIdx < 0 || length > thisLen - srcIdx) { - throw new IndexOutOfBoundsException("expected: " + - "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + length + ") <= srcLen(" + thisLen + ')'); + throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length(" + + length + ") <= srcLen(" + thisLen + ')'); } final int dstEnd = dstIdx + length; - for (int i = srcIdx, j = dstIdx; j < dstEnd; i ++, j ++) { + for (int i = srcIdx, j = dstIdx; j < dstEnd; i++, j++) { dst[j] = (char) (value[i] & 0xFF); } } /** - * 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. + * 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. + * @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. + * 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. + * @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; @@ -797,7 +836,7 @@ public final class AsciiString implements CharSequence, Comparable start = 0; } - for (int i = start; i < length; i ++) { + for (int i = start; i < length; i++) { if ((value[i] & 0xFF) == c) { return i; } @@ -807,34 +846,27 @@ public final class AsciiString implements CharSequence, Comparable } /** - * Searches in this string for the first index of the specified string. The - * search for the string starts at the beginning and moves towards the end - * of this string. + * Searches in this string for the first index of the specified string. The search for the string starts at the + * beginning and moves towards the end of this string. * - * @param string - * the string to find. - * @return the index of the first character of the specified string in this - * string, -1 if the specified string is not a substring. - * @throws NullPointerException - * if {@code string} is {@code null}. + * @param string the string to find. + * @return the index of the first character of the specified string in this string, -1 if the specified string is + * not a substring. + * @throws NullPointerException if {@code string} is {@code null}. */ public int indexOf(CharSequence string) { return indexOf(string, 0); } /** - * Searches in this string for the index of the specified string. The search - * for the string starts at the specified offset and moves towards the end - * of this string. + * Searches in this string for the index of the specified string. The search for the string starts at the specified + * offset and moves towards the end of this string. * - * @param subString - * the string to find. - * @param start - * the starting offset. - * @return the index of the first character of the specified string in this - * string, -1 if the specified string is not a substring. - * @throws NullPointerException - * if {@code subString} is {@code null}. + * @param subString the string to find. + * @param start the starting offset. + * @return the index of the first character of the specified string in this string, -1 if the specified string is + * not a substring. + * @throws NullPointerException if {@code subString} is {@code null}. */ public int indexOf(CharSequence subString, int start) { if (start < 0) { @@ -870,30 +902,23 @@ public final class AsciiString implements CharSequence, Comparable } /** - * 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. + * 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. + * @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. + * 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. + * @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) { @@ -902,7 +927,7 @@ public final class AsciiString implements CharSequence, Comparable if (start >= length) { start = length - 1; } - for (int i = start; i >= 0; -- i) { + for (int i = start; i >= 0; --i) { if ((value[i] & 0xFF) == c) { return i; } @@ -912,16 +937,13 @@ public final class AsciiString implements CharSequence, Comparable } /** - * Searches in this string for the last index of the specified string. The - * search for the string starts at the end and moves towards the beginning - * of this string. + * Searches in this string for the last index of the specified string. The search for the string starts at the end + * and moves towards the beginning of this string. * - * @param string - * the string to find. - * @return the index of the first character of the specified string in this - * string, -1 if the specified string is not a substring. - * @throws NullPointerException - * if {@code string} is {@code null}. + * @param string the string to find. + * @return the index of the first character of the specified string in this string, -1 if the specified string is + * not a substring. + * @throws NullPointerException if {@code string} is {@code null}. */ public int lastIndexOf(CharSequence string) { // Use count instead of count - 1 so lastIndexOf("") answers count @@ -929,18 +951,14 @@ public final class AsciiString implements CharSequence, Comparable } /** - * Searches in this string for the index of the specified string. The search - * for the string starts at the specified offset and moves towards the - * beginning of this string. + * Searches in this string for the index of the specified string. The search for the string starts at the specified + * offset and moves towards the beginning of this string. * - * @param subString - * the string to find. - * @param start - * the starting offset. - * @return the index of the first character of the specified string in this - * string , -1 if the specified string is not a substring. - * @throws NullPointerException - * if {@code subString} is {@code null}. + * @param subString the string to find. + * @param start the starting offset. + * @return the index of the first character of the specified string in this string , -1 if the specified string is + * not a substring. + * @throws NullPointerException if {@code subString} is {@code null}. */ public int lastIndexOf(CharSequence subString, int start) { final byte[] value = this.value; @@ -985,21 +1003,15 @@ public final class AsciiString implements CharSequence, Comparable } /** - * Compares the specified string to this string and compares the specified - * range of characters to determine if they are the same. + * Compares the specified string to this string and compares the specified range of characters to determine if they + * are the same. * - * @param thisStart - * the starting offset in this string. - * @param string - * the string to compare. - * @param start - * the starting offset in the specified string. - * @param length - * the number of characters to compare. - * @return {@code true} if the ranges of characters are equal, {@code false} - * otherwise - * @throws NullPointerException - * if {@code string} is {@code null}. + * @param thisStart the starting offset in this string. + * @param string the string to compare. + * @param start the starting offset in the specified string. + * @param length the number of characters to compare. + * @return {@code true} if the ranges of characters are equal, {@code false} otherwise + * @throws NullPointerException if {@code string} is {@code null}. */ public boolean regionMatches(int thisStart, CharSequence string, int start, int length) { if (string == null) { @@ -1021,7 +1033,7 @@ public final class AsciiString implements CharSequence, Comparable } final int thisEnd = thisStart + length; - for (int i = thisStart, j = start; i < thisEnd; i ++, j ++) { + for (int i = thisStart, j = start; i < thisEnd; i++, j++) { if ((value[i] & 0xFF) != string.charAt(j)) { return false; } @@ -1030,27 +1042,18 @@ public final class AsciiString implements CharSequence, Comparable } /** - * Compares the specified string to this string and compares the specified - * range of characters to determine if they are the same. When ignoreCase is - * true, the case of the characters is ignored during the comparison. + * Compares the specified string to this string and compares the specified range of characters to determine if they + * are the same. When ignoreCase is true, the case of the characters is ignored during the comparison. * - * @param ignoreCase - * specifies if case should be ignored. - * @param thisStart - * the starting offset in this string. - * @param string - * the string to compare. - * @param start - * the starting offset in the specified string. - * @param length - * the number of characters to compare. - * @return {@code true} if the ranges of characters are equal, {@code false} - * otherwise. - * @throws NullPointerException - * if {@code string} is {@code null}. + * @param ignoreCase specifies if case should be ignored. + * @param thisStart the starting offset in this string. + * @param string the string to compare. + * @param start the starting offset in the specified string. + * @param length the number of characters to compare. + * @return {@code true} if the ranges of characters are equal, {@code false} otherwise. + * @throws NullPointerException if {@code string} is {@code null}. */ - public boolean regionMatches(boolean ignoreCase, int thisStart, - CharSequence string, int start, int length) { + public boolean regionMatches(boolean ignoreCase, int thisStart, CharSequence string, int start, int length) { if (!ignoreCase) { return regionMatches(thisStart, string, start, length); } @@ -1080,13 +1083,10 @@ public final class AsciiString implements CharSequence, Comparable } /** - * Copies this string replacing occurrences of the specified character with - * another character. + * Copies this string replacing occurrences of the specified character with another character. * - * @param oldChar - * the character to replace. - * @param newChar - * the replacement character. + * @param oldChar the character to replace. + * @param newChar the replacement character. * @return a new string with occurrences of oldChar replaced by newChar. */ public AsciiString replace(char oldChar, char newChar) { @@ -1098,7 +1098,7 @@ public final class AsciiString implements CharSequence, Comparable final byte[] value = this.value; final int count = value.length; byte[] buffer = new byte[count]; - for (int i = 0, j = 0; i < value.length; i ++, j ++) { + for (int i = 0, j = 0; i < value.length; i++, j++) { byte b = value[i]; if ((char) (b & 0xFF) == oldChar) { b = (byte) newChar; @@ -1110,49 +1110,41 @@ public final class AsciiString implements CharSequence, Comparable } /** - * Compares the specified string to this string to determine if the - * specified string is a prefix. + * Compares the specified string to this string to determine if the specified string is a prefix. * - * @param prefix - * the string to look for. - * @return {@code true} if the specified string is a prefix of this string, - * {@code false} otherwise - * @throws NullPointerException - * if {@code prefix} is {@code null}. + * @param prefix the string to look for. + * @return {@code true} if the specified string is a prefix of this string, {@code false} otherwise + * @throws NullPointerException if {@code prefix} is {@code null}. */ public boolean startsWith(CharSequence prefix) { return startsWith(prefix, 0); } /** - * Compares the specified string to this string, starting at the specified - * offset, to determine if the specified string is a prefix. + * Compares the specified string to this string, starting at the specified offset, to determine if the specified + * string is a prefix. * - * @param prefix - * the string to look for. - * @param start - * the starting offset. - * @return {@code true} if the specified string occurs in this string at the - * specified offset, {@code false} otherwise. - * @throws NullPointerException - * if {@code prefix} is {@code null}. + * @param prefix the string to look for. + * @param start the starting offset. + * @return {@code true} if the specified string occurs in this string at the specified offset, {@code false} + * otherwise. + * @throws NullPointerException if {@code prefix} is {@code null}. */ public boolean startsWith(CharSequence prefix, int start) { return regionMatches(start, prefix, 0, prefix.length()); } /** - * Converts the characters in this string to lowercase, using the default - * Locale. + * Converts the characters in this string to lowercase, using the default Locale. * - * @return a new string containing the lowercase characters equivalent to - * the characters in this string. + * @return a new string containing the lowercase characters equivalent to the characters in this string. */ public AsciiString toLowerCase() { boolean lowercased = true; final byte[] value = this.value; - - for (byte b: value) { + int i, j; + for (i = 0; i < value.length; ++i) { + byte b = value[i]; if (b >= 'A' && b <= 'Z') { lowercased = false; break; @@ -1166,7 +1158,7 @@ public final class AsciiString implements CharSequence, Comparable final int length = value.length; final byte[] newValue = new byte[length]; - for (int i = 0, j = 0; i < length; i ++, j ++) { + for (i = 0, j = 0; i < length; ++i, ++j) { newValue[i] = toLowerCase(value[j]); } @@ -1174,16 +1166,16 @@ public final class AsciiString implements CharSequence, Comparable } /** - * Converts the characters in this string to uppercase, using the default - * Locale. + * Converts the characters in this string to uppercase, using the default Locale. * - * @return a new string containing the uppercase characters equivalent to - * the characters in this string. + * @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; - for (byte b: value) { + int i, j; + for (i = 0; i < value.length; ++i) { + byte b = value[i]; if (b >= 'a' && b <= 'z') { uppercased = false; break; @@ -1197,7 +1189,7 @@ public final class AsciiString implements CharSequence, Comparable final int length = value.length; final byte[] newValue = new byte[length]; - for (int i = 0, j = 0; i < length; i ++, j ++) { + for (i = 0, j = 0; i < length; ++i, ++j) { newValue[i] = toUpperCase(value[j]); } @@ -1205,21 +1197,19 @@ public final class AsciiString implements CharSequence, Comparable } /** - * Copies this string removing white space characters from the beginning and - * end of the string. + * Copies this string removing white space characters from the beginning and end of the string. * - * @return a new string with characters {@code <= \\u0020} removed from - * the beginning and the end. + * @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] <= ' ') { - start ++; + start++; } while (end >= start && value[end] <= ' ') { - end --; + end--; } if (start == 0 && end == last) { return this; @@ -1228,11 +1218,9 @@ public final class AsciiString implements CharSequence, Comparable } /** - * Compares a {@code CharSequence} to this {@code String} to determine if - * their contents are equal. + * Compares a {@code CharSequence} to this {@code String} to determine if their contents are equal. * - * @param cs - * the character sequence to compare to. + * @param cs the character sequence to compare to. * @return {@code true} if equal, otherwise {@code false} */ public boolean contentEquals(CharSequence cs) { @@ -1256,35 +1244,24 @@ public final class AsciiString implements CharSequence, Comparable /** * Determines whether this string matches a given regular expression. * - * @param expr - * the regular expression to be matched. + * @param expr the regular expression to be matched. * @return {@code true} if the expression matches, otherwise {@code false}. - * @throws PatternSyntaxException - * if the syntax of the supplied regular expression is not - * valid. - * @throws NullPointerException - * if {@code expr} is {@code null}. + * @throws PatternSyntaxException if the syntax of the supplied regular expression is not valid. + * @throws NullPointerException if {@code expr} is {@code null}. */ public boolean matches(String expr) { return Pattern.matches(expr, this); } /** - * Splits this string using the supplied regular expression {@code expr}. - * The parameter {@code max} controls the behavior how many times the - * pattern is applied to the string. + * Splits this string using the supplied regular expression {@code expr}. The parameter {@code max} controls the + * behavior how many times the pattern is applied to the string. * - * @param expr - * the regular expression used to divide the string. - * @param max - * the number of entries in the resulting array. - * @return an array of Strings created by separating the string along - * matches of the regular expression. - * @throws NullPointerException - * if {@code expr} is {@code null}. - * @throws PatternSyntaxException - * if the syntax of the supplied regular expression is not - * valid. + * @param expr the regular expression used to divide the string. + * @param max the number of entries in the resulting array. + * @return an array of Strings created by separating the string along matches of the regular expression. + * @throws NullPointerException if {@code expr} is {@code null}. + * @throws PatternSyntaxException if the syntax of the supplied regular expression is not valid. * @see Pattern#split(CharSequence, int) */ public AsciiString[] split(String expr, int max) { @@ -1293,7 +1270,7 @@ public final class AsciiString implements CharSequence, Comparable private static AsciiString[] toAsciiStringArray(String[] jdkResult) { AsciiString[] res = new AsciiString[jdkResult.length]; - for (int i = 0; i < jdkResult.length; i ++) { + for (int i = 0; i < jdkResult.length; i++) { res[i] = new AsciiString(jdkResult[i]); } return res; @@ -1308,7 +1285,7 @@ public final class AsciiString implements CharSequence, Comparable int start = 0; final byte[] value = this.value; final int length = value.length; - for (int i = start; i < length; i ++) { + for (int i = start; i < length; i++) { if (charAt(i) == delim) { if (start == i) { res.add(EMPTY_STRING); @@ -1327,7 +1304,7 @@ public final class AsciiString implements CharSequence, Comparable res.add(new AsciiString(value, start, length - start, false)); } else { // Truncate trailing empty elements. - for (int i = res.size() - 1; i >= 0; i --) { + for (int i = res.size() - 1; i >= 0; i--) { if (res.get(i).isEmpty()) { res.remove(i); } else { @@ -1341,13 +1318,10 @@ public final class AsciiString implements CharSequence, Comparable } /** - * Determines if this {@code String} contains the sequence of characters in - * the {@code CharSequence} passed. + * Determines if this {@code String} contains the sequence of characters in the {@code CharSequence} passed. * - * @param cs - * the character sequence to search for. - * @return {@code true} if the sequence of characters are contained in this - * string, otherwise {@code false}. + * @param cs the character sequence to search for. + * @return {@code true} if the sequence of characters are contained in this string, otherwise {@code false}. */ public boolean contains(CharSequence cs) { if (cs == null) { @@ -1379,7 +1353,7 @@ public final class AsciiString implements CharSequence, Comparable int i = start; boolean negative = charAt(i) == '-'; - if (negative && ++ i == end) { + if (negative && ++i == end) { throw new NumberFormatException(subSequence(start, end).toString()); } @@ -1392,7 +1366,7 @@ public final class AsciiString implements CharSequence, Comparable int result = 0; int offset = start; while (offset < end) { - int digit = Character.digit((char) (value[offset ++] & 0xFF), radix); + int digit = Character.digit((char) (value[offset++] & 0xFF), radix); if (digit == -1) { throw new NumberFormatException(subSequence(start, end).toString()); } @@ -1437,7 +1411,7 @@ public final class AsciiString implements CharSequence, Comparable int i = start; boolean negative = charAt(i) == '-'; - if (negative && ++ i == end) { + if (negative && ++i == end) { throw new NumberFormatException(subSequence(start, end).toString()); } @@ -1450,7 +1424,7 @@ public final class AsciiString implements CharSequence, Comparable long result = 0; int offset = start; while (offset < end) { - int digit = Character.digit((char) (value[offset ++] & 0xFF), radix); + int digit = Character.digit((char) (value[offset++] & 0xFF), radix); if (digit == -1) { throw new NumberFormatException(subSequence(start, end).toString()); } 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 465ad962ec..b64a857e9a 100644 --- a/codec/src/main/java/io/netty/handler/codec/BinaryHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/BinaryHeaders.java @@ -16,354 +16,123 @@ package io.netty.handler.codec; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.TreeSet; - /** - * 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 some additional utility when handling text data. + * 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 + * some additional utility when handling text data. */ -public interface BinaryHeaders extends Iterable> { - +public interface BinaryHeaders extends Headers { /** - * A visitor that helps reduce GC pressure while iterating over a collection of {@link BinaryHeaders}. + * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}. */ - public interface BinaryHeaderVisitor { - /** - * @return - *
    - *
  • {@code true} if the processor wants to continue the loop and handle the entry.
  • - *
  • {@code false} if the processor wants to stop handling headers and abort the loop.
  • - *
- */ - boolean visit(AsciiString name, AsciiString value) throws Exception; + public interface EntryVisitor extends Headers.EntryVisitor { } /** - * Returns the value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name the name of the header to search - * @return the first header value if the header is found. - * {@code null} if there's no such header. + * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}. */ - AsciiString get(AsciiString name); + public interface NameVisitor extends Headers.NameVisitor { + } - /** - * Returns the value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name the name of the header to search - * @param defaultValue the default value - * @return the first header value if the header is found. - * {@code defaultValue} if there's no such header. - */ - AsciiString get(AsciiString name, AsciiString defaultValue); - - /** - * Returns and removes the value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name the name of the header to search - * @return the first header value or {@code null} if there is no such header - */ - AsciiString getAndRemove(AsciiString name); - - /** - * Returns and removes the value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name the name of the header to search - * @param defaultValue the default value - * @return the first header value or {@code defaultValue} if there is no such header - */ - AsciiString getAndRemove(AsciiString name, AsciiString defaultValue); - - /** - * Returns the values of headers with the specified name - * - * @param name The name of the headers to search - * @return A {@link List} of header values which will be empty if no values are found - */ - List getAll(AsciiString name); - - /** - * Returns and Removes the values of headers with the specified name - * - * @param name The name of the headers to search - * @return A {@link List} of header values which will be empty if no values are found - */ - List getAllAndRemove(AsciiString name); - - /** - * Returns a new {@link List} that contains all headers in this object. Note that modifying the - * returned {@link List} will not affect the state of this object. If you intend to enumerate over the header - * entries only, use {@link #iterator()} instead, which has much less overhead. - */ - List> entries(); - - /** - * Returns {@code true} if and only if this collection contains the header with the specified name. - * - * @param name The name of the header to search for - * @return {@code true} if at least one header is found - */ - boolean contains(AsciiString name); - - /** - * Returns the number of header entries in this collection. - */ - int size(); - - /** - * Returns {@code true} if and only if this collection contains no header entries. - */ - boolean isEmpty(); - - /** - * Returns a new {@link Set} that contains the names of all headers in this object. Note that modifying the - * returned {@link Set} will not affect the state of this object. If you intend to enumerate over the header - * entries only, use {@link #iterator()} instead, which has much less overhead. - */ - Set names(); - - /** - * Adds a new header with the specified name and value. - * - * If the specified value is not a {@link String}, it is converted - * into a {@link String} by {@link Object#toString()}, except in the cases - * of {@link java.util.Date} and {@link java.util.Calendar}, which are formatted to the date - * format defined in RFC2616. - * - * @param name the name of the header being added - * @param value the value of the header being added - * - * @return {@code this} - */ + @Override BinaryHeaders add(AsciiString name, AsciiString value); - /** - * Adds a new header with the specified name and values. - * - * This getMethod can be represented approximately as the following code: - *
-     * for (Object v: values) {
-     *     if (v == null) {
-     *         break;
-     *     }
-     *     headers.add(name, v);
-     * }
-     * 
- * - * @param name the name of the headers being set - * @param values the values of the headers being set - * @return {@code this} - */ - BinaryHeaders add(AsciiString name, Iterable values); + @Override + BinaryHeaders add(AsciiString name, Iterable values); - /** - * Adds a new header with the specified name and values. - * - * This getMethod can be represented approximately as the following code: - *
-     * for (Object v: values) {
-     *     if (v == null) {
-     *         break;
-     *     }
-     *     headers.add(name, v);
-     * }
-     * 
- * - * @param name the name of the headers being set - * @param values the values of the headers being set - * @return {@code this} - */ + @Override BinaryHeaders add(AsciiString name, AsciiString... values); + @Override + BinaryHeaders addObject(AsciiString name, Object value); + + @Override + BinaryHeaders addObject(AsciiString name, Iterable values); + + @Override + BinaryHeaders addObject(AsciiString name, Object... values); + + @Override + BinaryHeaders addBoolean(AsciiString name, boolean value); + + @Override + BinaryHeaders addByte(AsciiString name, byte value); + + @Override + BinaryHeaders addChar(AsciiString name, char value); + + @Override + BinaryHeaders addShort(AsciiString name, short value); + + @Override + BinaryHeaders addInt(AsciiString name, int value); + + @Override + BinaryHeaders addLong(AsciiString name, long value); + + @Override + BinaryHeaders addFloat(AsciiString name, float value); + + @Override + BinaryHeaders addDouble(AsciiString name, double value); + /** - * Adds all header entries of the specified {@code headers}. - * - * @return {@code this} + * See {@link Headers#add(Headers)} */ BinaryHeaders add(BinaryHeaders headers); - /** - * Sets a header with the specified name and value. - * - * If there is an existing header with the same name, it is removed. - * If the specified value is not a {@link String}, it is converted into a - * {@link String} by {@link Object#toString()}, except for {@link java.util.Date} - * and {@link java.util.Calendar}, which are formatted to the date format defined in - * RFC2616. - * - * @param name The name of the header being set - * @param value The value of the header being set - * @return {@code this} - */ + @Override BinaryHeaders set(AsciiString name, AsciiString value); - /** - * Sets a header with the specified name and values. - * - * If there is an existing header with the same name, it is removed. - * This getMethod can be represented approximately as the following code: - *
-     * headers.remove(name);
-     * for (Object v: values) {
-     *     if (v == null) {
-     *         break;
-     *     }
-     *     headers.add(name, v);
-     * }
-     * 
- * - * @param name the name of the headers being set - * @param values the values of the headers being set - * @return {@code this} - */ - BinaryHeaders set(AsciiString name, Iterable values); + @Override + BinaryHeaders set(AsciiString name, Iterable values); - /** - * Sets a header with the specified name and values. - * - * If there is an existing header with the same name, it is removed. - * This getMethod can be represented approximately as the following code: - *
-     * headers.remove(name);
-     * for (Object v: values) {
-     *     if (v == null) {
-     *         break;
-     *     }
-     *     headers.add(name, v);
-     * }
-     * 
- * - * @param name the name of the headers being set - * @param values the values of the headers being set - * @return {@code this} - */ + @Override BinaryHeaders set(AsciiString name, AsciiString... values); + @Override + BinaryHeaders setObject(AsciiString name, Object value); + + @Override + BinaryHeaders setObject(AsciiString name, Iterable values); + + @Override + BinaryHeaders setObject(AsciiString name, Object... values); + + @Override + BinaryHeaders setBoolean(AsciiString name, boolean value); + + @Override + BinaryHeaders setByte(AsciiString name, byte value); + + @Override + BinaryHeaders setChar(AsciiString name, char value); + + @Override + BinaryHeaders setShort(AsciiString name, short value); + + @Override + BinaryHeaders setInt(AsciiString name, int value); + + @Override + BinaryHeaders setLong(AsciiString name, long value); + + @Override + BinaryHeaders setFloat(AsciiString name, float value); + + @Override + BinaryHeaders setDouble(AsciiString name, double value); + /** - * Cleans the current header entries and copies all header entries of the specified {@code headers}. - * - * @return {@code this} + * See {@link Headers#set(Headers)} */ BinaryHeaders set(BinaryHeaders headers); /** - * Retains all current headers but calls {@link #set(AsciiString, Object)} for each entry in {@code headers} - * @param headers The headers used to {@link #set(AsciiString, Object)} values in this instance - * @return {@code this} + * See {@link Headers#setAll(Headers)} */ BinaryHeaders setAll(BinaryHeaders headers); - /** - * Removes the header with the specified name. - * - * @param name The name of the header to remove - * @return {@code true} if and only if at least one entry has been removed - */ - boolean remove(AsciiString name); - - /** - * Removes all headers. - * - * @return {@code this} - */ - BinaryHeaders clear(); - - /** - * Returns {@code true} if a header with the name and value exists. - * - * @param name the header name - * @param value the header value - * @return {@code true} if it contains it {@code false} otherwise - */ - boolean contains(AsciiString name, AsciiString value); - @Override - Iterator> iterator(); - - BinaryHeaders forEachEntry(BinaryHeaderVisitor visitor); - - /** - * Common utilities for {@link BinaryHeaders}. - */ - public static final class Utils { - private static final int HASH_CODE_PRIME = 31; - - /** - * Generates a hash code for a {@link BinaryHeaders} object. - */ - public static int hashCode(BinaryHeaders headers) { - int result = 1; - for (AsciiString name : headers.names()) { - result = HASH_CODE_PRIME * result + name.hashCode(); - Set values = new TreeSet(headers.getAll(name)); - for (AsciiString value : values) { - result = HASH_CODE_PRIME * result + value.hashCode(); - } - } - return result; - } - - /** - * Compares the contents of two {@link BinaryHeaders} objects. - */ - public static boolean equals(BinaryHeaders h1, BinaryHeaders h2) { - // First, check that the set of names match. - Set names = h1.names(); - if (!names.equals(h2.names())) { - return false; - } - - // Compare the values for each name. - for (AsciiString name : names) { - List values = h1.getAll(name); - List otherValues = h2.getAll(name); - if (values.size() != otherValues.size()) { - return false; - } - - // Convert the values to a set and remove values from the other object to see if - // they match. - Set valueSet = new HashSet(values); - valueSet.removeAll(otherValues); - if (!valueSet.isEmpty()) { - return false; - } - } - - return true; - } - - /** - * Generates a {@link String} representation of the {@link BinaryHeaders}, assuming all of - * the names and values are {@code UTF-8} strings. - */ - public static String toStringUtf8(BinaryHeaders headers) { - StringBuilder builder = - new StringBuilder(headers.getClass().getSimpleName()).append('['); - boolean first = true; - Set names = new TreeSet(headers.names()); - for (AsciiString name : names) { - Set valueSet = new TreeSet(headers.getAll(name)); - for (AsciiString value : valueSet) { - if (!first) { - builder.append(", "); - } - first = false; - builder.append(name).append(": ").append(value); - } - } - return builder.append("]").toString(); - } - } + BinaryHeaders clear(); } diff --git a/codec/src/main/java/io/netty/handler/codec/ConvertibleHeaders.java b/codec/src/main/java/io/netty/handler/codec/ConvertibleHeaders.java new file mode 100644 index 0000000000..fa335300be --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/ConvertibleHeaders.java @@ -0,0 +1,112 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package io.netty.handler.codec; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * Extension to the {@link Headers} interface to provide methods which convert the + * native {@code UnconvertedType} to the not-native {@code ConvertedType} + */ +public interface ConvertibleHeaders extends Headers { + + /** + * Interface to do conversions to and from the two generic type parameters + */ + public interface TypeConverter { + /** + * Convert a native value + * @param value The value to be converted + * @return The conversion results + */ + ConvertedType toConvertedType(UnconvertedType value); + + /** + * Undo a conversion and restore the original native type + * @param value The converted value + * @return The original native type + */ + UnconvertedType toUnconvertedType(ConvertedType value); + } + + /** + * Invokes {@link Headers#get(Object)} and does a conversion on the results if not {@code null} + * @param name The name of entry to get + * @return The value corresponding to {@code name} and then converted + */ + ConvertedType getAndConvert(UnconvertedType name); + + /** + * Invokes {@link Headers#get(Object, Object)} and does a conversion on the results if not {@code null} + * @param name The name of entry to get + * @return The value corresponding to {@code name} and then converted + */ + ConvertedType getAndConvert(UnconvertedType name, ConvertedType defaultValue); + + /** + * Invokes {@link Headers#getAndRemove(Object)} and does a conversion on the results if not {@code null} + * @param name The name of entry to get + * @return The value corresponding to {@code name} and then converted + */ + ConvertedType getAndRemoveAndConvert(UnconvertedType name); + + /** + * Invokes {@link Headers#getAndRemove(Object, Object)} and does + * a conversion on the results if not {@code null} + * @param name The name of entry to get + * @return The value corresponding to {@code name} and then converted + */ + ConvertedType getAndRemoveAndConvert(UnconvertedType name, ConvertedType defaultValue); + + /** + * Invokes {@link Headers#getAll(Object)} and does a conversion on the results if not {@code null} + * @param name The name of entry to get + * @return The values corresponding to {@code name} and then converted + */ + List getAllAndConvert(UnconvertedType name); + + /** + * Invokes {@link Headers#getAllAndRemove(Object)} and does a conversion on the results if not {@code null} + * @param name The name of entry to get + * @return The values corresponding to {@code name} and then converted + */ + List getAllAndRemoveAndConvert(UnconvertedType name); + + /** + * Invokes {@link Headers#entries()} and lazily does a conversion on the results as they are accessed + * @param name The name of entry to get + * @return The values corresponding to {@code name} and then lazily converted + */ + List> entriesConverted(); + + /** + * Invokes {@link Headers#iterator()} and lazily does a conversion on the results as they are accessed + * @param name The name of entry to get + * @return Iterator which will provide converted values corresponding to {@code name} + */ + Iterator> iteratorConverted(); + + /** + * Invokes {@link Headers#names()} and does a conversion on the results + * @param name The name of entry to get + * @return The values corresponding to {@code name} and then converted + */ + Set namesAndConvert(Comparator comparator); +} 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 4f9a1be054..541b3045c7 100644 --- a/codec/src/main/java/io/netty/handler/codec/DefaultBinaryHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/DefaultBinaryHeaders.java @@ -14,329 +14,334 @@ */ package io.netty.handler.codec; -import static io.netty.util.internal.ObjectUtil.checkNotNull; +import static io.netty.handler.codec.AsciiString.CASE_INSENSITIVE_ORDER; import io.netty.util.internal.PlatformDependent; -import java.util.Arrays; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.TreeSet; +import java.text.ParseException; -public class DefaultBinaryHeaders implements BinaryHeaders { - private final HeaderMap.ValueUnmarshaller VALUE_UNMARSHALLER = - new HeaderMap.ValueUnmarshaller() { - @Override - public AsciiString unmarshal(CharSequence value) { - return (AsciiString) value; - } - }; - - private final BinaryHeaderVisitor addAll = new BinaryHeaderVisitor() { +public class DefaultBinaryHeaders extends DefaultHeaders implements BinaryHeaders { + private static final HashCodeGenerator ASCII_HASH_CODE_GENERATOR = + new HashCodeGenerator() { @Override - public boolean visit(AsciiString name, AsciiString value) throws Exception { - add(name, value); - return true; - } - }; - private final BinaryHeaderVisitor setAll = new BinaryHeaderVisitor() { - @Override - public boolean visit(AsciiString name, AsciiString value) throws Exception { - set(name, value); - return true; + public int generateHashCode(AsciiString name) { + return AsciiString.caseInsensitiveHashCode(name); } }; - private final HeaderMap headers; + private static final ValueConverter OBJECT_TO_ASCII = new ValueConverter() { + @Override + public AsciiString convert(Object value) { + if (value instanceof AsciiString) { + return (AsciiString) value; + } else if (value instanceof CharSequence) { + return new AsciiString((CharSequence) value); + } + return new AsciiString(value.toString()); + } + + @Override + public AsciiString convert(int value) { + return new AsciiString(String.valueOf(value)); + } + + @Override + public AsciiString convert(long value) { + return new AsciiString(String.valueOf(value)); + } + + @Override + public AsciiString convert(double value) { + return new AsciiString(String.valueOf(value)); + } + + @Override + public AsciiString convert(char value) { + return new AsciiString(String.valueOf(value)); + } + + @Override + public AsciiString convert(boolean value) { + return new AsciiString(String.valueOf(value)); + } + + @Override + public AsciiString convert(float value) { + return new AsciiString(String.valueOf(value)); + } + + @Override + public int convertToInt(AsciiString value) { + return value.parseInt(); + } + + @Override + public long convertToLong(AsciiString value) { + return value.parseLong(); + } + + @Override + public long convertToTimeMillis(AsciiString value) { + try { + return HeaderDateFormat.get().parse(value.toString()); + } catch (ParseException e) { + PlatformDependent.throwException(e); + } + return 0; + } + + @Override + public double convertToDouble(AsciiString value) { + return value.parseDouble(); + } + + @Override + public char convertToChar(AsciiString value) { + return value.charAt(0); + } + + @Override + public boolean convertToBoolean(AsciiString value) { + return value.byteAt(0) != 0; + } + + @Override + public float convertToFloat(AsciiString value) { + return value.parseFloat(); + } + + @Override + public AsciiString convert(short value) { + return new AsciiString(String.valueOf(value)); + } + + @Override + public short convertToShort(AsciiString value) { + return value.parseShort(); + } + + @Override + public AsciiString convert(byte value) { + return new AsciiString(String.valueOf(value)); + } + + @Override + public byte convertToByte(AsciiString 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; + } + }; public DefaultBinaryHeaders() { - // Binary headers are case-sensitive. It's up the HTTP/1 translation layer to convert headers to - // lowercase. - headers = new HeaderMap(false); + this(false); + } + + 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); } @Override public BinaryHeaders add(AsciiString name, AsciiString value) { - headers.add(name, value); + super.add(name, value); return this; } @Override - public BinaryHeaders add(AsciiString name, Iterable values) { - headers.add(name, values); + public BinaryHeaders add(AsciiString name, Iterable values) { + super.add(name, values); return this; } @Override public BinaryHeaders add(AsciiString name, AsciiString... values) { - headers.add(name, values); + super.add(name, values); + return this; + } + + @Override + public BinaryHeaders addObject(AsciiString name, Object value) { + super.addObject(name, value); + return this; + } + + @Override + public BinaryHeaders addObject(AsciiString name, Iterable values) { + super.addObject(name, values); + return this; + } + + @Override + public BinaryHeaders addObject(AsciiString name, Object... values) { + super.addObject(name, values); + return this; + } + + @Override + public BinaryHeaders addBoolean(AsciiString name, boolean value) { + super.addBoolean(name, value); + return this; + } + + @Override + public BinaryHeaders addChar(AsciiString name, char value) { + super.addChar(name, value); + return this; + } + + @Override + public BinaryHeaders addByte(AsciiString name, byte value) { + super.addByte(name, value); + return this; + } + + @Override + public BinaryHeaders addShort(AsciiString name, short value) { + super.addShort(name, value); + return this; + } + + @Override + public BinaryHeaders addInt(AsciiString name, int value) { + super.addInt(name, value); + return this; + } + + @Override + public BinaryHeaders addLong(AsciiString name, long value) { + super.addLong(name, value); + return this; + } + + @Override + public BinaryHeaders addFloat(AsciiString name, float value) { + super.addFloat(name, value); + return this; + } + + @Override + public BinaryHeaders addDouble(AsciiString name, double value) { + super.addDouble(name, value); return this; } @Override public BinaryHeaders add(BinaryHeaders headers) { - checkNotNull(headers, "headers"); - - add0(headers); + super.add(headers); return this; } - private void add0(BinaryHeaders headers) { - if (headers.isEmpty()) { - return; - } - - if (headers instanceof DefaultBinaryHeaders) { - this.headers.add(((DefaultBinaryHeaders) headers).headers); - } else { - forEachEntry(addAll); - } - } - - @Override - public boolean remove(AsciiString name) { - return headers.remove(name); - } - @Override public BinaryHeaders set(AsciiString name, AsciiString value) { - headers.set(name, value); + super.set(name, value); return this; } @Override - public BinaryHeaders set(AsciiString name, Iterable values) { - headers.set(name, values); + public BinaryHeaders set(AsciiString name, Iterable values) { + super.set(name, values); return this; } @Override public BinaryHeaders set(AsciiString name, AsciiString... values) { - headers.set(name, values); + super.set(name, values); + return this; + } + + @Override + public BinaryHeaders setObject(AsciiString name, Object value) { + super.setObject(name, value); + return this; + } + + @Override + public BinaryHeaders setObject(AsciiString name, Iterable values) { + super.setObject(name, values); + return this; + } + + @Override + public BinaryHeaders setObject(AsciiString name, Object... values) { + super.setObject(name, values); + return this; + } + + @Override + public BinaryHeaders setBoolean(AsciiString name, boolean value) { + super.setBoolean(name, value); + return this; + } + + @Override + public BinaryHeaders setChar(AsciiString name, char value) { + super.setChar(name, value); + return this; + } + + @Override + public BinaryHeaders setByte(AsciiString name, byte value) { + super.setByte(name, value); + return this; + } + + @Override + public BinaryHeaders setShort(AsciiString name, short value) { + super.setShort(name, value); + return this; + } + + @Override + public BinaryHeaders setInt(AsciiString name, int value) { + super.setInt(name, value); + return this; + } + + @Override + public BinaryHeaders setLong(AsciiString name, long value) { + super.setLong(name, value); + return this; + } + + @Override + public BinaryHeaders setFloat(AsciiString name, float value) { + super.setFloat(name, value); + return this; + } + + @Override + public BinaryHeaders setDouble(AsciiString name, double value) { + super.setDouble(name, value); return this; } @Override public BinaryHeaders set(BinaryHeaders headers) { - checkNotNull(headers, "headers"); - clear(); - add0(headers); + super.set(headers); return this; } @Override public BinaryHeaders setAll(BinaryHeaders headers) { - checkNotNull(headers, "headers"); - - if (headers instanceof DefaultBinaryHeaders) { - this.headers.setAll(((DefaultBinaryHeaders) headers).headers); - } else { - forEachEntry(setAll); - } - + super.setAll(headers); return this; } @Override public BinaryHeaders clear() { - headers.clear(); + super.clear(); return this; } - - @Override - public AsciiString get(AsciiString name) { - return (AsciiString) headers.get(name); - } - - @Override - public AsciiString get(AsciiString name, AsciiString defaultValue) { - AsciiString v = get(name); - if (v == null) { - return defaultValue; - } - return v; - } - - @Override - public AsciiString getAndRemove(AsciiString name) { - return (AsciiString) headers.getAndRemove(name); - } - - @Override - public AsciiString getAndRemove(AsciiString name, AsciiString defaultValue) { - AsciiString v = getAndRemove(name); - if (v == null) { - return defaultValue; - } - return v; - } - - @Override - public List getAll(AsciiString name) { - return headers.getAll(name, VALUE_UNMARSHALLER); - } - - @Override - public List getAllAndRemove(AsciiString name) { - return headers.getAllAndRemove(name, VALUE_UNMARSHALLER); - } - - @Override - public List> entries() { - int size = size(); - @SuppressWarnings("unchecked") - final Map.Entry[] all = new Map.Entry[size]; - - headers.forEachEntry(new HeaderMap.EntryVisitor() { - int cnt; - @Override - public boolean visit(Entry entry) { - all[cnt++] = new AsciiStringHeaderEntry(entry); - return true; - } - }); - - return Arrays.asList(all); - } - - @Override - public Iterator> iterator() { - return new AsciiStringHeaderIterator(); - } - - @Override - public boolean contains(AsciiString name) { - return get(name) != null; - } - - @Override - public int size() { - return headers.size(); - } - - @Override - public boolean isEmpty() { - return headers.isEmpty(); - } - - @Override - public boolean contains(AsciiString name, AsciiString value) { - return contains(name, value, false); - } - - public boolean contains(AsciiString name, AsciiString value, boolean ignoreCase) { - return headers.contains(name, value); - } - - @Override - public Set names() { - return names(headers.isIgnoreCase()); - } - - /** - * Get the set of names for all text headers - * @param caseInsensitive {@code true} if names should be added in a case insensitive - * @return The set of names for all text headers - */ - public Set names(boolean caseInsensitive) { - final Set names = caseInsensitive ? new TreeSet(AsciiString.CASE_INSENSITIVE_ORDER) - : new LinkedHashSet(size()); - headers.forEachName(new HeaderMap.NameVisitor() { - @Override - public boolean visit(CharSequence name) { - names.add((AsciiString) name); - return true; - } - }); - return names; - } - - @Override - public BinaryHeaders forEachEntry(final BinaryHeaders.BinaryHeaderVisitor visitor) { - headers.forEachEntry(new HeaderMap.EntryVisitor() { - @Override - public boolean visit(Entry entry) { - try { - return visitor.visit((AsciiString) entry.getKey(), - (AsciiString) entry.getValue()); - } catch (Exception e) { - PlatformDependent.throwException(e); - return false; - } - } - }); - return this; - } - - @Override - public int hashCode() { - return Utils.hashCode(this); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof BinaryHeaders)) { - return false; - } - - return Utils.equals(this, (BinaryHeaders) o); - } - - @Override - public String toString() { - return Utils.toStringUtf8(this); - } - - private static final class AsciiStringHeaderEntry implements Map.Entry { - private final Entry entry; - - AsciiStringHeaderEntry(Entry entry) { - this.entry = entry; - } - - @Override - public AsciiString getKey() { - return (AsciiString) entry.getKey(); - } - - @Override - public AsciiString getValue() { - return (AsciiString) entry.getValue(); - } - - @Override - public AsciiString setValue(AsciiString value) { - checkNotNull(value, "value"); - return (AsciiString) entry.setValue(value); - } - - @Override - public String toString() { - return entry.toString(); - } - } - - private final class AsciiStringHeaderIterator implements Iterator> { - - private Iterator> iter = headers.iterator(); - - @Override - public boolean hasNext() { - return iter.hasNext(); - } - - @Override - public Entry next() { - Entry entry = iter.next(); - return new AsciiStringHeaderEntry(entry); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - } } diff --git a/codec/src/main/java/io/netty/handler/codec/DefaultConvertibleHeaders.java b/codec/src/main/java/io/netty/handler/codec/DefaultConvertibleHeaders.java new file mode 100644 index 0000000000..8b13b78a00 --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/DefaultConvertibleHeaders.java @@ -0,0 +1,181 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package io.netty.handler.codec; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeSet; + +public class DefaultConvertibleHeaders extends DefaultHeaders + implements ConvertibleHeaders { + + private final TypeConverter typeConverter; + + public DefaultConvertibleHeaders(Comparator keyComparator, + Comparator valueComparator, + HashCodeGenerator hashCodeGenerator, + ValueConverter valueConverter, + TypeConverter typeConverter) { + super(keyComparator, valueComparator, hashCodeGenerator, valueConverter); + this.typeConverter = typeConverter; + } + + public DefaultConvertibleHeaders(Comparator keyComparator, + Comparator valueComparator, + HashCodeGenerator hashCodeGenerator, + ValueConverter valueConverter, + TypeConverter typeConverter, + NameConverter nameConverter) { + super(keyComparator, valueComparator, hashCodeGenerator, valueConverter, nameConverter); + this.typeConverter = typeConverter; + } + + @Override + public ConvertedType getAndConvert(UnconvertedType name) { + return getAndConvert(name, null); + } + + @Override + public ConvertedType getAndConvert(UnconvertedType name, ConvertedType defaultValue) { + UnconvertedType v = get(name); + if (v == null) { + return defaultValue; + } + return typeConverter.toConvertedType(v); + } + + @Override + public ConvertedType getAndRemoveAndConvert(UnconvertedType name) { + return getAndRemoveAndConvert(name, null); + } + + @Override + public ConvertedType getAndRemoveAndConvert(UnconvertedType name, ConvertedType defaultValue) { + UnconvertedType v = getAndRemove(name); + if (v == null) { + return defaultValue; + } + return typeConverter.toConvertedType(v); + } + + @Override + public List getAllAndConvert(UnconvertedType name) { + List all = getAll(name); + List allConverted = new ArrayList(all.size()); + for (int i = 0; i < all.size(); ++i) { + allConverted.add(typeConverter.toConvertedType(all.get(i))); + } + return allConverted; + } + + @Override + public List getAllAndRemoveAndConvert(UnconvertedType name) { + List all = getAllAndRemove(name); + List allConverted = new ArrayList(all.size()); + for (int i = 0; i < all.size(); ++i) { + allConverted.add(typeConverter.toConvertedType(all.get(i))); + } + return allConverted; + } + + @Override + public List> entriesConverted() { + List> entries = entries(); + List> entriesConverted = new ArrayList>( + entries.size()); + for (int i = 0; i < entries.size(); ++i) { + entriesConverted.add(new ConvertedEntry(entries.get(i))); + } + return entriesConverted; + } + + @Override + public Iterator> iteratorConverted() { + return new ConvertedIterator(); + } + + @Override + public Set namesAndConvert(Comparator comparator) { + Set names = names(); + Set namesConverted = new TreeSet(comparator); + for (UnconvertedType unconverted : names) { + namesConverted.add(typeConverter.toConvertedType(unconverted)); + } + return namesConverted; + } + + private final class ConvertedIterator implements Iterator> { + private Iterator> iter = iterator(); + + @Override + public boolean hasNext() { + return iter.hasNext(); + } + + @Override + public Entry next() { + Entry next = iter.next(); + + return new ConvertedEntry(next); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + private final class ConvertedEntry implements Entry { + private final Entry entry; + private ConvertedType name; + private ConvertedType value; + + public ConvertedEntry(Entry entry) { + this.entry = entry; + } + + @Override + public ConvertedType getKey() { + if (name == null) { + name = typeConverter.toConvertedType(entry.getKey()); + } + return name; + } + + @Override + public ConvertedType getValue() { + if (value == null) { + value = typeConverter.toConvertedType(entry.getValue()); + } + return value; + } + + @Override + public ConvertedType setValue(ConvertedType value) { + ConvertedType old = getValue(); + entry.setValue(typeConverter.toUnconvertedType(value)); + return old; + } + + @Override + public String toString() { + return entry.toString(); + } + } +} diff --git a/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java b/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java new file mode 100644 index 0000000000..c474a84658 --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/DefaultHeaders.java @@ -0,0 +1,1459 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package io.netty.handler.codec; + +import static io.netty.util.internal.ObjectUtil.checkNotNull; +import io.netty.util.collection.CollectionUtils; +import io.netty.util.collection.IntObjectHashMap; +import io.netty.util.collection.IntObjectMap; +import io.netty.util.concurrent.FastThreadLocal; +import io.netty.util.internal.PlatformDependent; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.TimeZone; +import java.util.TreeSet; + +public class DefaultHeaders implements Headers { + /** + * Allows users of this interface to specify a hash code other than the default {@link Object#hashCode()} + */ + public interface HashCodeGenerator { + /** + * Obtain the hash code for the given {@code name} + * + * @param name The name to generate the hash code for + * @return The hash code for {@code name} + */ + int generateHashCode(T name); + } + + /** + * Allows users to convert the {@code name} elements before being processed by this map + */ + public interface NameConverter { + /** + * Convert the {@code name} to some other form of the same object type + * + * @param name The object to convert + * @return The results of the conversion + */ + T convertName(T name); + } + + /** + * A name converted which does not covert but instead just returns this {@code name} unchanged + */ + public static final class IdentityNameConverter implements NameConverter { + @Override + public T convertName(T name) { + return name; + } + } + + private static final int HASH_CODE_PRIME = 31; + private static final int DEFAULT_BUCKET_SIZE = 17; + private static final int DEFAULT_MAP_SIZE = 4; + + private static final NameConverter DEFAULT_NAME_CONVERTER = new IdentityNameConverter(); + + private final EntryVisitor setAllVisitor = new EntryVisitor() { + @Override + public boolean visit(Entry entry) { + DefaultHeaders.this.set(entry.getKey(), entry.getValue()); + return true; + } + }; + + private final EntryVisitor addAllVisitor = new EntryVisitor() { + @Override + public boolean visit(Entry entry) { + DefaultHeaders.this.add(entry.getKey(), entry.getValue()); + return true; + } + }; + + private final IntObjectMap entries; + private final IntObjectMap tailEntries; + private final HeaderEntry head; + private final Comparator keyComparator; + private final Comparator valueComparator; + private final HashCodeGenerator hashCodeGenerator; + private final ValueConverter valueConverter; + private final NameConverter nameConverter; + private final int bucketSize; + int size; + + @SuppressWarnings("unchecked") + public DefaultHeaders(Comparator keyComparator, Comparator valueComparator, + HashCodeGenerator hashCodeGenerator, ValueConverter typeConverter) { + this(keyComparator, valueComparator, hashCodeGenerator, typeConverter, + (NameConverter) DEFAULT_NAME_CONVERTER); + } + + public DefaultHeaders(Comparator keyComparator, Comparator valueComparator, + HashCodeGenerator hashCodeGenerator, ValueConverter typeConverter, NameConverter nameConverter) { + this(keyComparator, valueComparator, hashCodeGenerator, typeConverter, nameConverter, DEFAULT_BUCKET_SIZE, + DEFAULT_MAP_SIZE); + } + + public DefaultHeaders(Comparator keyComparator, Comparator valueComparator, + HashCodeGenerator hashCodeGenerator, ValueConverter valueConverter, NameConverter nameConverter, + int bucketSize, int initialMapSize) { + if (keyComparator == null) { + throw new NullPointerException("keyComparator"); + } + if (valueComparator == null) { + throw new NullPointerException("valueComparator"); + } + if (hashCodeGenerator == null) { + throw new NullPointerException("hashCodeGenerator"); + } + if (valueConverter == null) { + throw new NullPointerException("valueConverter"); + } + if (nameConverter == null) { + throw new NullPointerException("nameConverter"); + } + if (bucketSize < 1) { + throw new IllegalArgumentException("bucketSize must be a positive integer"); + } + head = new HeaderEntry(); + head.before = head.after = head; + this.keyComparator = keyComparator; + this.valueComparator = valueComparator; + this.hashCodeGenerator = hashCodeGenerator; + this.valueConverter = valueConverter; + this.nameConverter = nameConverter; + this.bucketSize = bucketSize; + entries = new IntObjectHashMap(initialMapSize); + tailEntries = new IntObjectHashMap(initialMapSize); + } + + @Override + public T get(T name) { + checkNotNull(name, "name"); + + int h = hashCodeGenerator.generateHashCode(name); + int i = index(h); + HeaderEntry e = entries.get(i); + while (e != null) { + if (e.hash == h && keyComparator.compare(e.name, name) == 0) { + return e.value; + } + e = e.next; + } + return null; + } + + @Override + public T get(T name, T defaultValue) { + T value = get(name); + if (value == null) { + return defaultValue; + } + return value; + } + + @Override + public T getAndRemove(T name) { + checkNotNull(name, "name"); + int h = hashCodeGenerator.generateHashCode(name); + int i = index(h); + HeaderEntry e = entries.get(i); + if (e == null) { + return null; + } + + T value = null; + for (;;) { + if (e.hash == h && keyComparator.compare(e.name, name) == 0) { + if (value == null) { + value = e.value; + } + e.remove(); + HeaderEntry next = e.next; + if (next != null) { + entries.put(i, next); + e = next; + } else { + entries.remove(i); + tailEntries.remove(i); + return value; + } + } else { + break; + } + } + + for (;;) { + HeaderEntry next = e.next; + if (next == null) { + break; + } + if (next.hash == h && keyComparator.compare(e.name, name) == 0) { + if (value == null) { + value = next.value; + } + e.next = next.next; + if (e.next == null) { + tailEntries.put(i, e); + } + next.remove(); + } else { + e = next; + } + } + + return value; + } + + @Override + public T getAndRemove(T name, T defaultValue) { + T value = getAndRemove(name); + if (value == null) { + return defaultValue; + } + return value; + } + + @Override + public List getAll(T name) { + checkNotNull(name, "name"); + List values = new ArrayList(4); + int h = hashCodeGenerator.generateHashCode(name); + int i = index(h); + HeaderEntry e = entries.get(i); + while (e != null) { + if (e.hash == h && keyComparator.compare(e.name, name) == 0) { + values.add(e.value); + } + e = e.next; + } + + return values; + } + + @Override + public List getAllAndRemove(T name) { + checkNotNull(name, "name"); + int h = hashCodeGenerator.generateHashCode(name); + int i = index(h); + HeaderEntry e = entries.get(i); + if (e == null) { + return null; + } + + List values = new ArrayList(4); + for (;;) { + if (e.hash == h && keyComparator.compare(e.name, name) == 0) { + values.add(e.value); + e.remove(); + HeaderEntry next = e.next; + if (next != null) { + entries.put(i, next); + e = next; + } else { + entries.remove(i); + tailEntries.remove(i); + return values; + } + } else { + break; + } + } + + for (;;) { + HeaderEntry next = e.next; + if (next == null) { + break; + } + if (next.hash == h && keyComparator.compare(next.name, name) == 0) { + values.add(next.value); + e.next = next.next; + if (e.next == null) { + tailEntries.put(i, e); + } + next.remove(); + } else { + e = next; + } + } + + return values; + } + + @Override + public List> entries() { + final int size = size(); + List> localEntries = new ArrayList>(size); + + HeaderEntry e = head.after; + while (e != head) { + localEntries.add(e); + e = e.after; + } + + assert size == localEntries.size(); + return localEntries; + } + + @Override + public boolean contains(T name) { + return get(name) != null; + } + + @Override + public boolean contains(T name, T value) { + return contains(name, value, keyComparator, valueComparator); + } + + @Override + public boolean containsObject(T name, Object value) { + return contains(name, valueConverter.convert(checkNotNull(value, "value"))); + } + + @Override + public boolean containsBoolean(T name, int value) { + return contains(name, valueConverter.convert(checkNotNull(value, "value"))); + } + + @Override + public boolean containsByte(T name, byte value) { + return contains(name, valueConverter.convert(checkNotNull(value, "value"))); + } + + @Override + public boolean containsChar(T name, char value) { + return contains(name, valueConverter.convert(checkNotNull(value, "value"))); + } + + @Override + public boolean containsShort(T name, byte value) { + return contains(name, valueConverter.convert(checkNotNull(value, "value"))); + } + + @Override + public boolean containsInt(T name, int value) { + return contains(name, valueConverter.convert(checkNotNull(value, "value"))); + } + + @Override + public boolean containsLong(T name, long value) { + return contains(name, valueConverter.convert(checkNotNull(value, "value"))); + } + + @Override + public boolean containsFloat(T name, float value) { + return contains(name, valueConverter.convert(checkNotNull(value, "value"))); + } + + @Override + public boolean containsDouble(T name, double value) { + return contains(name, valueConverter.convert(checkNotNull(value, "value"))); + } + + @Override + public boolean contains(T name, T value, Comparator comparator) { + return contains(name, value, comparator, comparator); + } + + @Override + public boolean contains(T name, T value, + Comparator keyComparator, Comparator valueComparator) { + checkNotNull(name, "name"); + checkNotNull(value, "value"); + checkNotNull(keyComparator, "keyComparator"); + checkNotNull(valueComparator, "valueComparator"); + int h = hashCodeGenerator.generateHashCode(name); + int i = index(h); + HeaderEntry e = entries.get(i); + while (e != null) { + if (e.hash == h && + keyComparator.compare(e.name, name) == 0 && + valueComparator.compare(e.value, value) == 0) { + return true; + } + e = e.next; + } + return false; + } + + @Override + public boolean containsObject(T name, Object value, Comparator comparator) { + return containsObject(name, value, comparator, comparator); + } + + @Override + public boolean containsObject(T name, Object value, Comparator keyComparator, + Comparator valueComparator) { + return contains(name, valueConverter.convert(checkNotNull(value, "value")), keyComparator, valueComparator); + } + + @Override + public int size() { + return size; + } + + @Override + public boolean isEmpty() { + return head == head.after; + } + + @Override + public Set names() { + final Set names = new TreeSet(keyComparator); + + HeaderEntry e = head.after; + while (e != head) { + names.add(e.name); + e = e.after; + } + + return names; + } + + @Override + public List namesList() { + final List names = new ArrayList(size()); + + HeaderEntry e = head.after; + while (e != head) { + names.add(e.name); + e = e.after; + } + + return names; + } + + @Override + public Headers add(T name, T value) { + name = convertName(name); + checkNotNull(value, "value"); + int h = hashCodeGenerator.generateHashCode(name); + int i = index(h); + add0(h, i, name, value); + return this; + } + + @Override + public Headers add(T name, Iterable values) { + name = convertName(name); + checkNotNull(values, "values"); + + int h = hashCodeGenerator.generateHashCode(name); + int i = index(h); + for (T v : values) { + if (v == null) { + break; + } + add0(h, i, name, v); + } + return this; + } + + @Override + public Headers add(T name, T... values) { + name = convertName(name); + checkNotNull(values, "values"); + + int h = hashCodeGenerator.generateHashCode(name); + int i = index(h); + for (T v : values) { + if (v == null) { + break; + } + add0(h, i, name, v); + } + return this; + } + + @Override + public Headers addObject(T name, Object value) { + return add(name, valueConverter.convert(checkNotNull(value, "value"))); + } + + @Override + public Headers addObject(T name, Iterable values) { + name = convertName(name); + checkNotNull(values, "values"); + + int h = hashCodeGenerator.generateHashCode(name); + int i = index(h); + for (Object o : values) { + if (o == null) { + break; + } + T converted = valueConverter.convert(o); + checkNotNull(converted, "converted"); + add0(h, i, name, converted); + } + return this; + } + + @Override + public Headers addObject(T name, Object... values) { + name = convertName(name); + checkNotNull(values, "values"); + + int h = hashCodeGenerator.generateHashCode(name); + int i = index(h); + for (Object o : values) { + if (o == null) { + break; + } + T converted = valueConverter.convert(o); + checkNotNull(converted, "converted"); + add0(h, i, name, converted); + } + return this; + } + + @Override + public Headers addInt(T name, int value) { + return add(name, valueConverter.convert(value)); + } + + @Override + public Headers addLong(T name, long value) { + return add(name, valueConverter.convert(value)); + } + + @Override + public Headers addDouble(T name, double value) { + return add(name, valueConverter.convert(value)); + } + + @Override + public Headers addChar(T name, char value) { + return add(name, valueConverter.convert(value)); + } + + @Override + public Headers addBoolean(T name, boolean value) { + return add(name, valueConverter.convert(value)); + } + + @Override + public Headers addFloat(T name, float value) { + return add(name, valueConverter.convert(value)); + } + + @Override + public Headers addByte(T name, byte value) { + return add(name, valueConverter.convert(value)); + } + + @Override + public Headers addShort(T name, short value) { + return add(name, valueConverter.convert(value)); + } + + @Override + public Headers add(Headers headers) { + checkNotNull(headers, "headers"); + + add0(headers); + return this; + } + + @Override + public Headers set(T name, T value) { + name = convertName(name); + checkNotNull(value, "value"); + int h = hashCodeGenerator.generateHashCode(name); + int i = index(h); + remove0(h, i, name); + add0(h, i, name, value); + return this; + } + + @Override + public Headers set(T name, Iterable values) { + name = convertName(name); + checkNotNull(values, "values"); + + int h = hashCodeGenerator.generateHashCode(name); + int i = index(h); + remove0(h, i, name); + for (T v : values) { + if (v == null) { + break; + } + add0(h, i, name, v); + } + + return this; + } + + @Override + public Headers set(T name, T... values) { + name = convertName(name); + checkNotNull(values, "values"); + + int h = hashCodeGenerator.generateHashCode(name); + int i = index(h); + remove0(h, i, name); + for (T v : values) { + if (v == null) { + break; + } + add0(h, i, name, v); + } + + return this; + } + + @Override + public Headers setObject(T name, Object value) { + return set(name, valueConverter.convert(checkNotNull(value, "value"))); + } + + @Override + public Headers setObject(T name, Iterable values) { + name = convertName(name); + checkNotNull(values, "values"); + + int h = hashCodeGenerator.generateHashCode(name); + int i = index(h); + remove0(h, i, name); + for (Object o : values) { + if (o == null) { + break; + } + T converted = valueConverter.convert(o); + checkNotNull(converted, "converted"); + add0(h, i, name, converted); + } + + return this; + } + + @Override + public Headers setObject(T name, Object... values) { + name = convertName(name); + checkNotNull(values, "values"); + + int h = hashCodeGenerator.generateHashCode(name); + int i = index(h); + remove0(h, i, name); + for (Object o : values) { + if (o == null) { + break; + } + T converted = valueConverter.convert(o); + checkNotNull(converted, "converted"); + add0(h, i, name, converted); + } + + return this; + } + + @Override + public Headers setInt(T name, int value) { + return set(name, valueConverter.convert(value)); + } + + @Override + public Headers setLong(T name, long value) { + return set(name, valueConverter.convert(value)); + } + + @Override + public Headers setDouble(T name, double value) { + return set(name, valueConverter.convert(value)); + } + + @Override + public Headers setFloat(T name, float value) { + return set(name, valueConverter.convert(value)); + } + + @Override + public Headers setChar(T name, char value) { + return set(name, valueConverter.convert(value)); + } + + @Override + public Headers setBoolean(T name, boolean value) { + return set(name, valueConverter.convert(value)); + } + + @Override + public Headers setByte(T name, byte value) { + return set(name, valueConverter.convert(value)); + } + + @Override + public Headers setShort(T name, short value) { + return set(name, valueConverter.convert(value)); + } + + @Override + public Headers set(Headers headers) { + checkNotNull(headers, "headers"); + + clear(); + add0(headers); + return this; + } + + @Override + public Headers setAll(Headers headers) { + checkNotNull(headers, "headers"); + + if (headers instanceof DefaultHeaders) { + DefaultHeaders m = (DefaultHeaders) headers; + HeaderEntry e = m.head.after; + while (e != m.head) { + set(e.name, e.value); + e = e.after; + } + } else { + try { + headers.forEachEntry(setAllVisitor); + } catch (Exception ex) { + PlatformDependent.throwException(ex); + } + } + + return this; + } + + @Override + public boolean remove(T name) { + checkNotNull(name, "name"); + int h = hashCodeGenerator.generateHashCode(name); + int i = index(h); + return remove0(h, i, name); + } + + @Override + public Headers clear() { + entries.clear(); + tailEntries.clear(); + head.before = head.after = head; + size = 0; + return this; + } + + @Override + public Iterator> iterator() { + return new KeyValueHeaderIterator(); + } + + @Override + public Map.Entry forEachEntry(EntryVisitor visitor) throws Exception { + HeaderEntry e = head.after; + while (e != head) { + if (!visitor.visit(e)) { + return e; + } + e = e.after; + } + return null; + } + + @Override + public T forEachName(NameVisitor visitor) throws Exception { + HeaderEntry e = head.after; + while (e != head) { + if (!visitor.visit(e.name)) { + return e.name; + } + e = e.after; + } + return null; + } + + @Override + public Boolean getBoolean(T name) { + T v = get(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToBoolean(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public boolean getBoolean(T name, boolean defaultValue) { + Boolean v = getBoolean(name); + return v == null ? defaultValue : v; + } + + @Override + public Byte getByte(T name) { + T v = get(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToByte(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public byte getByte(T name, byte defaultValue) { + Byte v = getByte(name); + return v == null ? defaultValue : v; + } + + @Override + public Character getChar(T name) { + T v = get(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToChar(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public char getChar(T name, char defaultValue) { + Character v = getChar(name); + return v == null ? defaultValue : v; + } + + @Override + public Short getShort(T name) { + T v = get(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToShort(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public short getInt(T name, short defaultValue) { + Short v = getShort(name); + return v == null ? defaultValue : v; + } + + @Override + public Integer getInt(T name) { + T v = get(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToInt(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public int getInt(T name, int defaultValue) { + Integer v = getInt(name); + return v == null ? defaultValue : v; + } + + @Override + public Long getLong(T name) { + T v = get(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToLong(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public long getLong(T name, long defaultValue) { + Long v = getLong(name); + return v == null ? defaultValue : v; + } + + @Override + public Float getFloat(T name) { + T v = get(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToFloat(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public float getFloat(T name, float defaultValue) { + Float v = getFloat(name); + return v == null ? defaultValue : v; + } + + @Override + public Double getDouble(T name) { + T v = get(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToDouble(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public double getDouble(T name, double defaultValue) { + Double v = getDouble(name); + return v == null ? defaultValue : v; + } + + @Override + public Long getTimeMillis(T name) { + T v = get(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToTimeMillis(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public long getTimeMillis(T name, long defaultValue) { + Long v = getTimeMillis(name); + return v == null ? defaultValue : v; + } + + @Override + public Boolean getBooleanAndRemove(T name) { + T v = getAndRemove(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToBoolean(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public boolean getBooleanAndRemove(T name, boolean defaultValue) { + Boolean v = getBooleanAndRemove(name); + return v == null ? defaultValue : v; + } + + @Override + public Byte getByteAndRemove(T name) { + T v = getAndRemove(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToByte(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public byte getByteAndRemove(T name, byte defaultValue) { + Byte v = getByteAndRemove(name); + return v == null ? defaultValue : v; + } + + @Override + public Character getCharAndRemove(T name) { + T v = getAndRemove(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToChar(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public char getCharAndRemove(T name, char defaultValue) { + Character v = getCharAndRemove(name); + return v == null ? defaultValue : v; + } + + @Override + public Short getShortAndRemove(T name) { + T v = getAndRemove(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToShort(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public short getShortAndRemove(T name, short defaultValue) { + Short v = getShortAndRemove(name); + return v == null ? defaultValue : v; + } + + @Override + public Integer getIntAndRemove(T name) { + T v = getAndRemove(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToInt(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public int getIntAndRemove(T name, int defaultValue) { + Integer v = getIntAndRemove(name); + return v == null ? defaultValue : v; + } + + @Override + public Long getLongAndRemove(T name) { + T v = getAndRemove(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToLong(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public long getLongAndRemove(T name, long defaultValue) { + Long v = getLongAndRemove(name); + return v == null ? defaultValue : v; + } + + @Override + public Float getFloatAndRemove(T name) { + T v = getAndRemove(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToFloat(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public float getFloatAndRemove(T name, float defaultValue) { + Float v = getFloatAndRemove(name); + return v == null ? defaultValue : v; + } + + @Override + public Double getDoubleAndRemove(T name) { + T v = getAndRemove(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToDouble(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public double getDoubleAndRemove(T name, double defaultValue) { + Double v = getDoubleAndRemove(name); + return v == null ? defaultValue : v; + } + + @Override + public Long getTimeMillisAndRemove(T name) { + T v = getAndRemove(name); + if (v == null) { + return null; + } + try { + return valueConverter.convertToTimeMillis(v); + } catch (Throwable t) { + return null; + } + } + + @Override + public long getTimeMillisAndRemove(T name, long defaultValue) { + Long v = getTimeMillisAndRemove(name); + return v == null ? defaultValue : v; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof DefaultHeaders)) { + return false; + } + + @SuppressWarnings("unchecked") + DefaultHeaders h2 = (DefaultHeaders) o; + // First, check that the set of names match. Don't use a TreeSet for comparison + // because we want to force the keyComparator to be used for all comparisons + List namesList = namesList(); + List otherNamesList = h2.namesList(); + if (!CollectionUtils.equals(namesList, otherNamesList, keyComparator)) { + return false; + } + + // Compare the values for each name. Don't use a TreeSet for comparison + // because we want to force the valueComparator to be used for all comparisons + Set names = new TreeSet(keyComparator); + names.addAll(namesList); + for (T name : names) { + if (!CollectionUtils.equals(getAll(name), h2.getAll(name), valueComparator)) { + return false; + } + } + + return true; + } + + @Override + public int hashCode() { + int result = 1; + for (T name : names()) { + result = HASH_CODE_PRIME * result + name.hashCode(); + List values = getAll(name); + Collections.sort(values, valueComparator); + for (int i = 0; i < values.size(); ++i) { + result = HASH_CODE_PRIME * result + hashCodeGenerator.generateHashCode(values.get(i)); + } + } + return result; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(getClass().getSimpleName()).append('['); + for (T name : names()) { + List values = getAll(name); + Collections.sort(values, valueComparator); + for (int i = 0; i < values.size(); ++i) { + builder.append(name).append(": ").append(values.get(i)).append(", "); + } + } + if (builder.length() > 2) { // remove the trailing ", " + builder.setLength(builder.length() - 2); + } + return builder.append(']').toString(); + } + + private T convertName(T name) { + return nameConverter.convertName(checkNotNull(name, "name")); + } + + private int index(int hash) { + return Math.abs(hash % bucketSize); + } + + private void add0(Headers headers) { + if (headers.isEmpty()) { + return; + } + + if (headers instanceof DefaultHeaders) { + DefaultHeaders m = (DefaultHeaders) headers; + HeaderEntry e = m.head.after; + while (e != m.head) { + add(e.name, e.value); + e = e.after; + } + } else { + try { + headers.forEachEntry(addAllVisitor); + } catch (Exception ex) { + PlatformDependent.throwException(ex); + } + } + } + + private void add0(int h, int i, T name, T value) { + // Update the per-bucket hash table linked list + HeaderEntry newEntry = new HeaderEntry(h, name, value); + HeaderEntry oldTail = tailEntries.get(i); + if (oldTail == null) { + entries.put(i, newEntry); + } else { + oldTail.next = newEntry; + } + tailEntries.put(i, newEntry); + + // Update the overall insertion order linked list + newEntry.addBefore(head); + } + + private boolean remove0(int h, int i, T name) { + HeaderEntry e = entries.get(i); + if (e == null) { + return false; + } + + boolean removed = false; + for (;;) { + if (e.hash == h && keyComparator.compare(e.name, name) == 0) { + e.remove(); + HeaderEntry next = e.next; + if (next != null) { + entries.put(i, next); + e = next; + } else { + entries.remove(i); + tailEntries.remove(i); + return true; + } + removed = true; + } else { + break; + } + } + + for (;;) { + HeaderEntry next = e.next; + if (next == null) { + break; + } + if (next.hash == h && keyComparator.compare(next.name, name) == 0) { + e.next = next.next; + if (e.next == null) { + tailEntries.put(i, e); + } + next.remove(); + removed = true; + } else { + e = next; + } + } + + return removed; + } + + private final class HeaderEntry implements Map.Entry { + final int hash; + final T name; + T value; + /** + * In bucket linked list + */ + HeaderEntry next; + /** + * Overall insertion order linked list + */ + HeaderEntry before, after; + + HeaderEntry(int hash, T name, T value) { + this.hash = hash; + this.name = name; + this.value = value; + } + + HeaderEntry() { + hash = -1; + name = null; + value = null; + } + + void remove() { + before.after = after; + after.before = before; + --size; + } + + void addBefore(HeaderEntry e) { + after = e; + before = e.before; + before.after = this; + after.before = this; + ++size; + } + + @Override + public T getKey() { + return name; + } + + @Override + public T getValue() { + return value; + } + + @Override + public T setValue(T value) { + checkNotNull(value, "value"); + T oldValue = this.value; + this.value = value; + return oldValue; + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + b.append(name); + b.append('='); + b.append(value); + return b.toString(); + } + } + + protected final class KeyValueHeaderIterator implements Iterator> { + + private HeaderEntry current = head; + + @Override + public boolean hasNext() { + return current.after != head; + } + + @Override + public Entry next() { + current = current.after; + + if (current == head) { + throw new NoSuchElementException(); + } + + return current; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + /** + * This DateFormat decodes 3 formats of {@link java.util.Date}, but only encodes the one, the first: + *
    + *
  • Sun, 06 Nov 1994 08:49:37 GMT: standard specification, the only one with valid generation
  • + *
  • Sun, 06 Nov 1994 08:49:37 GMT: obsolete specification
  • + *
  • Sun Nov 6 08:49:37 1994: obsolete specification
  • + *
+ */ + static final class HeaderDateFormat { + private static final ParsePosition parsePos = new ParsePosition(0); + private static final FastThreadLocal dateFormatThreadLocal = + new FastThreadLocal() { + @Override + protected HeaderDateFormat initialValue() { + return new HeaderDateFormat(); + } + }; + + static HeaderDateFormat get() { + return dateFormatThreadLocal.get(); + } + + /** + * Standard date format: + * + *
+         * Sun, 06 Nov 1994 08:49:37 GMT -> E, d MMM yyyy HH:mm:ss z
+         * 
+ */ + private final DateFormat dateFormat1 = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH); + /** + * First obsolete format: + * + *
+         * Sunday, 06-Nov-94 08:49:37 GMT -> E, d-MMM-y HH:mm:ss z
+         * 
+ */ + private final DateFormat dateFormat2 = new SimpleDateFormat("E, dd-MMM-yy HH:mm:ss z", Locale.ENGLISH); + /** + * Second obsolete format + * + *
+         * Sun Nov 6 08:49:37 1994 -> EEE, MMM d HH:mm:ss yyyy
+         * 
+ */ + private final DateFormat dateFormat3 = new SimpleDateFormat("E MMM d HH:mm:ss yyyy", Locale.ENGLISH); + + private HeaderDateFormat() { + TimeZone tz = TimeZone.getTimeZone("GMT"); + dateFormat1.setTimeZone(tz); + dateFormat2.setTimeZone(tz); + dateFormat3.setTimeZone(tz); + } + + long parse(String text) throws ParseException { + Date date = dateFormat1.parse(text, parsePos); + if (date == null) { + date = dateFormat2.parse(text, parsePos); + } + if (date == null) { + date = dateFormat3.parse(text, parsePos); + } + if (date == null) { + throw new ParseException(text, 0); + } + return date.getTime(); + } + + long parse(String text, long defaultValue) { + Date date = dateFormat1.parse(text, parsePos); + if (date == null) { + date = dateFormat2.parse(text, parsePos); + } + if (date == null) { + date = dateFormat3.parse(text, parsePos); + } + if (date == null) { + return defaultValue; + } + return date.getTime(); + } + } +} 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 be2a95bbfc..f9268de2bb 100644 --- a/codec/src/main/java/io/netty/handler/codec/DefaultTextHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/DefaultTextHeaders.java @@ -16,719 +16,367 @@ package io.netty.handler.codec; -import static io.netty.util.internal.ObjectUtil.checkNotNull; -import io.netty.util.concurrent.FastThreadLocal; +import static io.netty.handler.codec.AsciiString.CHARSEQUENCE_CASE_INSENSITIVE_ORDER; +import static io.netty.handler.codec.AsciiString.CHARSEQUENCE_CASE_SENSITIVE_ORDER; import io.netty.util.internal.PlatformDependent; -import java.text.DateFormat; import java.text.ParseException; -import java.text.ParsePosition; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Date; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.TimeZone; -import java.util.TreeSet; +import java.util.Comparator; -public class DefaultTextHeaders implements TextHeaders { - private final HeaderMap.NameConverter NAME_CONVERTER = new HeaderMap.NameConverter() { +public class DefaultTextHeaders extends DefaultConvertibleHeaders implements TextHeaders { + private static final HashCodeGenerator CHARSEQUECE_CASE_INSENSITIVE_HASH_CODE_GENERATOR = + new HashCodeGenerator() { @Override - public CharSequence convertName(CharSequence name) { - return DefaultTextHeaders.this.convertName(name); - } - }; - private final HeaderMap.ValueMarshaller VALUE_MARSHALLER = new HeaderMap.ValueMarshaller() { - @Override - public CharSequence marshal(Object value) { - return convertValue(value); - } - }; - private final HeaderMap.ValueUnmarshaller VALUE_UNMARSHALLER = - new HeaderMap.ValueUnmarshaller() { - @Override - public String unmarshal(CharSequence value) { - return value.toString(); - } - }; - - private final TextHeaderProcessor addAll = new TextHeaderProcessor() { - @Override - public boolean process(CharSequence name, CharSequence value) throws Exception { - headers.add(name, value); - return true; + public int generateHashCode(CharSequence name) { + return AsciiString.caseInsensitiveHashCode(name); } }; - private final TextHeaderProcessor setAll = new TextHeaderProcessor() { + private static final HashCodeGenerator CHARSEQUECE_CASE_SENSITIVE_HASH_CODE_GENERATOR = + new HashCodeGenerator() { @Override - public boolean process(CharSequence name, CharSequence value) throws Exception { - headers.set(name, value); - return true; + public int generateHashCode(CharSequence name) { + return name.hashCode(); } }; - private final HeaderMap headers; + public static class DefaultTextValueTypeConverter implements ValueConverter { + @Override + public CharSequence convert(Object value) { + if (value instanceof CharSequence) { + return (CharSequence) value; + } + return value.toString(); + } + + @Override + public CharSequence convert(int value) { + return String.valueOf(value); + } + + @Override + public CharSequence convert(long value) { + return String.valueOf(value); + } + + @Override + public CharSequence convert(double value) { + return String.valueOf(value); + } + + @Override + public CharSequence convert(char value) { + return String.valueOf(value); + } + + @Override + public CharSequence convert(boolean value) { + return String.valueOf(value); + } + + @Override + public CharSequence convert(float value) { + return String.valueOf(value); + } + + @Override + public boolean convertToBoolean(CharSequence value) { + return Boolean.parseBoolean(value.toString()); + } + + @Override + public CharSequence convert(byte value) { + return String.valueOf(value); + } + + @Override + public byte convertToByte(CharSequence value) { + return Byte.valueOf(value.toString()); + } + + @Override + public char convertToChar(CharSequence value) { + return value.charAt(0); + } + + @Override + public CharSequence convert(short value) { + return String.valueOf(value); + } + + @Override + public short convertToShort(CharSequence value) { + return Short.valueOf(value.toString()); + } + + @Override + public int convertToInt(CharSequence value) { + return Integer.valueOf(value.toString()); + } + + @Override + public long convertToLong(CharSequence value) { + return Long.valueOf(value.toString()); + } + + @Override + public long convertToTimeMillis(CharSequence value) { + try { + return HeaderDateFormat.get().parse(value.toString()); + } catch (ParseException e) { + PlatformDependent.throwException(e); + } + return 0; + } + + @Override + public float convertToFloat(CharSequence value) { + return Float.valueOf(value.toString()); + } + + @Override + public double convertToDouble(CharSequence value) { + return Double.valueOf(value.toString()); + } + } + + private static final Headers.ValueConverter CHARSEQUENCE_FROM_OBJECT_CONVERTER = + new DefaultTextValueTypeConverter(); + private static final ConvertibleHeaders.TypeConverter CHARSEQUENCE_TO_STRING_CONVERTER = + new ConvertibleHeaders.TypeConverter() { + @Override + public String toConvertedType(CharSequence value) { + return value.toString(); + } + + @Override + public CharSequence toUnconvertedType(String value) { + return value; + } + }; + + private static final NameConverter CHARSEQUENCE_IDENTITY_CONVERTER = + new IdentityNameConverter(); public DefaultTextHeaders() { this(true); } public DefaultTextHeaders(boolean ignoreCase) { - headers = new HeaderMap(ignoreCase, NAME_CONVERTER); + this(ignoreCase, CHARSEQUENCE_FROM_OBJECT_CONVERTER, CHARSEQUENCE_IDENTITY_CONVERTER); } - protected CharSequence convertName(CharSequence name) { - if (name == null) { - throw new NullPointerException("name"); - } - return name; - } - - protected CharSequence convertValue(Object value) { - if (value == null) { - throw new NullPointerException("value"); - } - if (value instanceof CharSequence) { - return (CharSequence) value; - } - return value.toString(); + protected DefaultTextHeaders(boolean ignoreCase, Headers.ValueConverter valueConverter, + NameConverter nameConverter) { + super(comparator(ignoreCase), comparator(ignoreCase), + ignoreCase ? CHARSEQUECE_CASE_INSENSITIVE_HASH_CODE_GENERATOR + : CHARSEQUECE_CASE_SENSITIVE_HASH_CODE_GENERATOR, valueConverter, + CHARSEQUENCE_TO_STRING_CONVERTER, nameConverter); } @Override - public TextHeaders add(CharSequence name, Object value) { - CharSequence convertedVal = convertValue(value); - headers.add(name, convertedVal); + public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) { + return contains(name, value, comparator(ignoreCase)); + } + + @Override + public boolean containsObject(CharSequence name, Object value, boolean ignoreCase) { + return containsObject(name, value, comparator(ignoreCase)); + } + + @Override + public TextHeaders add(CharSequence name, CharSequence value) { + super.add(name, value); return this; } @Override - public TextHeaders add(CharSequence name, Iterable values) { - headers.addConvertedValues(name, VALUE_MARSHALLER, values); + public TextHeaders add(CharSequence name, Iterable values) { + super.add(name, values); return this; } @Override - public TextHeaders add(CharSequence name, Object... values) { - headers.addConvertedValues(name, VALUE_MARSHALLER, values); + public TextHeaders add(CharSequence name, CharSequence... values) { + super.add(name, values); + return this; + } + + @Override + public TextHeaders addObject(CharSequence name, Object value) { + super.addObject(name, value); + return this; + } + + @Override + public TextHeaders addObject(CharSequence name, Iterable values) { + super.addObject(name, values); + return this; + } + + @Override + public TextHeaders addObject(CharSequence name, Object... values) { + super.addObject(name, values); + return this; + } + + @Override + public TextHeaders addBoolean(CharSequence name, boolean value) { + super.addBoolean(name, value); + return this; + } + + @Override + public TextHeaders addChar(CharSequence name, char value) { + super.addChar(name, value); + return this; + } + + @Override + public TextHeaders addByte(CharSequence name, byte value) { + super.addByte(name, value); + return this; + } + + @Override + public TextHeaders addShort(CharSequence name, short value) { + super.addShort(name, value); + return this; + } + + @Override + public TextHeaders addInt(CharSequence name, int value) { + super.addInt(name, value); + return this; + } + + @Override + public TextHeaders addLong(CharSequence name, long value) { + super.addLong(name, value); + return this; + } + + @Override + public TextHeaders addFloat(CharSequence name, float value) { + super.addFloat(name, value); + return this; + } + + @Override + public TextHeaders addDouble(CharSequence name, double value) { + super.addDouble(name, value); return this; } @Override public TextHeaders add(TextHeaders headers) { - checkNotNull(headers, "headers"); - - add0(headers); - return this; - } - - private void add0(TextHeaders headers) { - if (headers.isEmpty()) { - return; - } - - if (headers instanceof DefaultTextHeaders) { - this.headers.add(((DefaultTextHeaders) headers).headers); - } else { - headers.forEachEntry(addAll); - } - } - - @Override - public boolean remove(CharSequence name) { - return headers.remove(name); - } - - @Override - public TextHeaders set(CharSequence name, Object value) { - CharSequence convertedVal = convertValue(value); - headers.set(name, convertedVal); + super.add(headers); return this; } @Override - public TextHeaders set(CharSequence name, Iterable values) { - headers.set(name, VALUE_MARSHALLER, values); + public TextHeaders set(CharSequence name, CharSequence value) { + super.set(name, value); return this; } @Override - public TextHeaders set(CharSequence name, Object... values) { - headers.set(name, VALUE_MARSHALLER, values); + public TextHeaders set(CharSequence name, Iterable values) { + super.set(name, values); + return this; + } + + @Override + public TextHeaders set(CharSequence name, CharSequence... values) { + super.set(name, values); + return this; + } + + @Override + public TextHeaders setObject(CharSequence name, Object value) { + super.setObject(name, value); + return this; + } + + @Override + public TextHeaders setObject(CharSequence name, Iterable values) { + super.setObject(name, values); + return this; + } + + @Override + public TextHeaders setObject(CharSequence name, Object... values) { + super.setObject(name, values); + return this; + } + + @Override + public TextHeaders setBoolean(CharSequence name, boolean value) { + super.setBoolean(name, value); + return this; + } + + @Override + public TextHeaders setChar(CharSequence name, char value) { + super.setChar(name, value); + return this; + } + + @Override + public TextHeaders setByte(CharSequence name, byte value) { + super.setByte(name, value); + return this; + } + + @Override + public TextHeaders setShort(CharSequence name, short value) { + super.setShort(name, value); + return this; + } + + @Override + public TextHeaders setInt(CharSequence name, int value) { + super.setInt(name, value); + return this; + } + + @Override + public TextHeaders setLong(CharSequence name, long value) { + super.setLong(name, value); + return this; + } + + @Override + public TextHeaders setFloat(CharSequence name, float value) { + super.setFloat(name, value); + return this; + } + + @Override + public TextHeaders setDouble(CharSequence name, double value) { + super.setDouble(name, value); return this; } @Override public TextHeaders set(TextHeaders headers) { - if (headers == null) { - throw new NullPointerException("headers"); - } - - clear(); - add0(headers); + super.set(headers); return this; } @Override public TextHeaders setAll(TextHeaders headers) { - if (headers == null) { - throw new NullPointerException("headers"); - } - - if (headers instanceof DefaultTextHeaders) { - this.headers.setAll(((DefaultTextHeaders) headers).headers); - } else { - headers.forEachEntry(setAll); - } - + super.setAll(headers); return this; } @Override public TextHeaders clear() { - headers.clear(); + super.clear(); return this; } - @Override - public CharSequence getUnconverted(CharSequence name) { - return headers.get(name); - } - - @Override - public String get(CharSequence name) { - CharSequence v = getUnconverted(name); - if (v == null) { - return null; - } - return v.toString(); - } - - @Override - public String get(CharSequence name, String defaultValue) { - CharSequence v = getUnconverted(name); - if (v == null) { - return defaultValue; - } - return v.toString(); - } - - @Override - public Integer getInt(CharSequence name) { - CharSequence v = getUnconverted(name); - if (v == null) { - return null; - } - - try { - if (v instanceof AsciiString) { - return ((AsciiString) v).parseInt(); - } else { - return Integer.parseInt(v.toString()); - } - } catch (NumberFormatException ignored) { - return null; - } - } - - @Override - public int getInt(CharSequence name, int defaultValue) { - CharSequence v = getUnconverted(name); - if (v == null) { - return defaultValue; - } - - try { - if (v instanceof AsciiString) { - return ((AsciiString) v).parseInt(); - } else { - return Integer.parseInt(v.toString()); - } - } catch (NumberFormatException ignored) { - return defaultValue; - } - } - - @Override - public Long getLong(CharSequence name) { - CharSequence v = getUnconverted(name); - if (v == null) { - return null; - } - - try { - if (v instanceof AsciiString) { - return ((AsciiString) v).parseLong(); - } else { - return Long.parseLong(v.toString()); - } - } catch (NumberFormatException ignored) { - return null; - } - } - - @Override - public long getLong(CharSequence name, long defaultValue) { - CharSequence v = getUnconverted(name); - if (v == null) { - return defaultValue; - } - - try { - if (v instanceof AsciiString) { - return ((AsciiString) v).parseLong(); - } else { - return Long.parseLong(v.toString()); - } - } catch (NumberFormatException ignored) { - return defaultValue; - } - } - - @Override - public Long getTimeMillis(CharSequence name) { - CharSequence v = getUnconverted(name); - if (v == null) { - return null; - } - - try { - return HttpHeaderDateFormat.get().parse(v.toString()); - } catch (ParseException ignored) { - return null; - } - } - - @Override - public long getTimeMillis(CharSequence name, long defaultValue) { - CharSequence v = getUnconverted(name); - if (v == null) { - return defaultValue; - } - - return HttpHeaderDateFormat.get().parse(v.toString(), defaultValue); - } - - @Override - public CharSequence getUnconvertedAndRemove(CharSequence name) { - return headers.getAndRemove(name); - } - - @Override - public String getAndRemove(CharSequence name) { - CharSequence v = getUnconvertedAndRemove(name); - if (v == null) { - return null; - } - return v.toString(); - } - - @Override - public String getAndRemove(CharSequence name, String defaultValue) { - CharSequence v = getUnconvertedAndRemove(name); - if (v == null) { - return defaultValue; - } - return v.toString(); - } - - @Override - public Integer getIntAndRemove(CharSequence name) { - CharSequence v = getUnconvertedAndRemove(name); - if (v == null) { - return null; - } - - try { - if (v instanceof AsciiString) { - return ((AsciiString) v).parseInt(); - } else { - return Integer.parseInt(v.toString()); - } - } catch (NumberFormatException ignored) { - return null; - } - } - - @Override - public int getIntAndRemove(CharSequence name, int defaultValue) { - CharSequence v = getUnconvertedAndRemove(name); - if (v == null) { - return defaultValue; - } - - try { - if (v instanceof AsciiString) { - return ((AsciiString) v).parseInt(); - } else { - return Integer.parseInt(v.toString()); - } - } catch (NumberFormatException ignored) { - return defaultValue; - } - } - - @Override - public Long getLongAndRemove(CharSequence name) { - CharSequence v = getUnconvertedAndRemove(name); - if (v == null) { - return null; - } - - try { - if (v instanceof AsciiString) { - return ((AsciiString) v).parseLong(); - } else { - return Long.parseLong(v.toString()); - } - } catch (NumberFormatException ignored) { - return null; - } - } - - @Override - public long getLongAndRemove(CharSequence name, long defaultValue) { - CharSequence v = getUnconvertedAndRemove(name); - if (v == null) { - return defaultValue; - } - - try { - if (v instanceof AsciiString) { - return ((AsciiString) v).parseLong(); - } else { - return Long.parseLong(v.toString()); - } - } catch (NumberFormatException ignored) { - return defaultValue; - } - } - - @Override - public Long getTimeMillisAndRemove(CharSequence name) { - CharSequence v = getUnconvertedAndRemove(name); - if (v == null) { - return null; - } - - try { - return HttpHeaderDateFormat.get().parse(v.toString()); - } catch (ParseException ignored) { - return null; - } - } - - @Override - public long getTimeMillisAndRemove(CharSequence name, long defaultValue) { - CharSequence v = getUnconvertedAndRemove(name); - if (v == null) { - return defaultValue; - } - - return HttpHeaderDateFormat.get().parse(v.toString(), defaultValue); - } - - @Override - public List getAllUnconverted(CharSequence name) { - return headers.getAll(name); - } - - @Override - public List getAll(CharSequence name) { - return headers.getAll(name, VALUE_UNMARSHALLER); - } - - @Override - public List getAllAndRemove(CharSequence name) { - return headers.getAll(name, VALUE_UNMARSHALLER); - } - - @Override - public List getAllUnconvertedAndRemove(CharSequence name) { - return headers.getAllAndRemove(name); - } - - @Override - public List> entries() { - int size = size(); - @SuppressWarnings("unchecked") - final Map.Entry[] all = new Map.Entry[size]; - - headers.forEachEntry(new HeaderMap.EntryVisitor() { - int cnt; - @Override - public boolean visit(Entry entry) { - all[cnt++] = new StringHeaderEntry(entry); - return true; - } - }); - - return Arrays.asList(all); - } - - @Override - public List> unconvertedEntries() { - return headers.entries(); - } - - @Override - public Iterator> iterator() { - return new StringHeaderIterator(); - } - - @Override - public Iterator> unconvertedIterator() { - return headers.iterator(); - } - - @Override - public boolean contains(CharSequence name) { - return getUnconverted(name) != null; - } - - @Override - public int size() { - return headers.size(); - } - - @Override - public boolean isEmpty() { - return headers.isEmpty(); - } - - @Override - public boolean contains(CharSequence name, Object value) { - return contains(name, value, false); - } - - @Override - public boolean contains(CharSequence name, Object value, boolean ignoreCase) { - CharSequence convertedVal = convertValue(value); - return headers.contains(name, convertedVal, ignoreCase); - } - - @Override - public Set unconvertedNames() { - return headers.names(); - } - - @Override - public Set names() { - return names(false); - } - - /** - * Get the set of names for all text headers - * @param caseInsensitive {@code true} if names should be added in a case insensitive - * @return The set of names for all text headers - */ - public Set names(boolean caseInsensitive) { - final Set names = - caseInsensitive ? new TreeSet(String.CASE_INSENSITIVE_ORDER) - : new LinkedHashSet(size()); - headers.forEachName(new HeaderMap.NameVisitor() { - @Override - public boolean visit(CharSequence name) { - names.add(name.toString()); - return true; - } - }); - return names; - } - - @Override - public TextHeaders forEachEntry(final TextHeaderProcessor processor) { - headers.forEachEntry(new HeaderMap.EntryVisitor() { - @Override - public boolean visit(Entry entry) { - try { - return processor.process(entry.getKey(), entry.getValue()); - } catch (Exception ex) { - PlatformDependent.throwException(ex); - return false; - } - } - }); - return this; - } - - @Override - public int hashCode() { - return headers.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof DefaultTextHeaders)) { - return false; - } - - DefaultTextHeaders other = (DefaultTextHeaders) o; - - // First, check that the set of names match. - Set names = names(true); - if (!names.equals(other.names(true))) { - return false; - } - - // Compare the values for each name. - for (String name : names) { - List values = getAll(name); - List otherValues = other.getAll(name); - if (values.size() != otherValues.size()) { - return false; - } - - // Convert the values to a set and remove values from the other object to see if - // they match. - Set valueSet = new HashSet(values); - valueSet.removeAll(otherValues); - if (!valueSet.isEmpty()) { - return false; - } - } - - return true; - } - - private static final class StringHeaderEntry implements Entry { - private final Entry entry; - private String name; - private String value; - - StringHeaderEntry(Entry entry) { - this.entry = entry; - } - - @Override - public String getKey() { - if (name == null) { - name = entry.getKey().toString(); - } - return name; - } - - @Override - public String getValue() { - if (value == null) { - value = entry.getValue().toString(); - } - return value; - } - - @Override - public String setValue(String value) { - return entry.setValue(value).toString(); - } - - @Override - public String toString() { - return entry.toString(); - } - } - - private final class StringHeaderIterator implements Iterator> { - - private Iterator> iter = headers.iterator(); - - @Override - public boolean hasNext() { - return iter.hasNext(); - } - - @Override - public Entry next() { - Entry next = iter.next(); - - return new StringHeaderEntry(next); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - } - - /** - * This DateFormat decodes 3 formats of {@link java.util.Date}, but only encodes the one, - * the first: - *
    - *
  • Sun, 06 Nov 1994 08:49:37 GMT: standard specification, the only one with - * valid generation
  • - *
  • Sun, 06 Nov 1994 08:49:37 GMT: obsolete specification
  • - *
  • Sun Nov 6 08:49:37 1994: obsolete specification
  • - *
- */ - static final class HttpHeaderDateFormat { - - private static final ParsePosition parsePos = new ParsePosition(0); - private static final FastThreadLocal dateFormatThreadLocal = - new FastThreadLocal() { - @Override - protected HttpHeaderDateFormat initialValue() { - return new HttpHeaderDateFormat(); - } - }; - - static HttpHeaderDateFormat get() { - return dateFormatThreadLocal.get(); - } - - /** - * Standard date format: - *
Sun, 06 Nov 1994 08:49:37 GMT -> E, d MMM yyyy HH:mm:ss z
- */ - private final DateFormat dateFormat1 = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH); - /** - * First obsolete format: - *
Sunday, 06-Nov-94 08:49:37 GMT -> E, d-MMM-y HH:mm:ss z
- */ - private final DateFormat dateFormat2 = new SimpleDateFormat("E, dd-MMM-yy HH:mm:ss z", Locale.ENGLISH); - /** - * Second obsolete format - *
Sun Nov 6 08:49:37 1994 -> EEE, MMM d HH:mm:ss yyyy
- */ - private final DateFormat dateFormat3 = new SimpleDateFormat("E MMM d HH:mm:ss yyyy", Locale.ENGLISH); - - private HttpHeaderDateFormat() { - TimeZone tz = TimeZone.getTimeZone("GMT"); - dateFormat1.setTimeZone(tz); - dateFormat2.setTimeZone(tz); - dateFormat3.setTimeZone(tz); - } - - long parse(String text) throws ParseException { - Date date = dateFormat1.parse(text, parsePos); - if (date == null) { - date = dateFormat2.parse(text, parsePos); - } - if (date == null) { - date = dateFormat3.parse(text, parsePos); - } - if (date == null) { - throw new ParseException(text, 0); - } - return date.getTime(); - } - - long parse(String text, long defaultValue) { - Date date = dateFormat1.parse(text, parsePos); - if (date == null) { - date = dateFormat2.parse(text, parsePos); - } - if (date == null) { - date = dateFormat3.parse(text, parsePos); - } - if (date == null) { - return defaultValue; - } - return date.getTime(); - } + private static Comparator comparator(boolean ignoreCase) { + return ignoreCase ? CHARSEQUENCE_CASE_INSENSITIVE_ORDER : CHARSEQUENCE_CASE_SENSITIVE_ORDER; } } 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 15058a4ded..a6ff1e7a4c 100644 --- a/codec/src/main/java/io/netty/handler/codec/EmptyBinaryHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/EmptyBinaryHeaders.java @@ -16,154 +16,199 @@ package io.netty.handler.codec; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; - -public class EmptyBinaryHeaders implements BinaryHeaders { - - @Override - public AsciiString get(AsciiString name) { - return null; - } - - @Override - public AsciiString get(AsciiString name, AsciiString defaultValue) { - return defaultValue; - } - - @Override - public AsciiString getAndRemove(AsciiString name) { - return null; - } - - @Override - public AsciiString getAndRemove(AsciiString name, AsciiString defaultValue) { - return defaultValue; - } - - @Override - public List getAll(AsciiString name) { - return Collections.emptyList(); - } - - @Override - public List getAllAndRemove(AsciiString name) { - return Collections.emptyList(); - } - - @Override - public List> entries() { - return Collections.emptyList(); - } - - @Override - public boolean contains(AsciiString name) { - return false; - } - - @Override - public int size() { - return 0; - } - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public Set names() { - return Collections.emptySet(); +public class EmptyBinaryHeaders extends EmptyHeaders implements BinaryHeaders { + protected EmptyBinaryHeaders() { } @Override public BinaryHeaders add(AsciiString name, AsciiString value) { - throw new UnsupportedOperationException("read only"); + super.add(name, value); + return this; } @Override - public BinaryHeaders add(AsciiString name, Iterable values) { - throw new UnsupportedOperationException("read only"); + public BinaryHeaders add(AsciiString name, Iterable values) { + super.add(name, values); + return this; } @Override public BinaryHeaders add(AsciiString name, AsciiString... values) { - throw new UnsupportedOperationException("read only"); + super.add(name, values); + return this; + } + + @Override + public BinaryHeaders addObject(AsciiString name, Object value) { + super.addObject(name, value); + return this; + } + + @Override + public BinaryHeaders addObject(AsciiString name, Iterable values) { + super.addObject(name, values); + return this; + } + + @Override + public BinaryHeaders addObject(AsciiString name, Object... values) { + super.addObject(name, values); + return this; + } + + @Override + public BinaryHeaders addBoolean(AsciiString name, boolean value) { + super.addBoolean(name, value); + return this; + } + + @Override + public BinaryHeaders addChar(AsciiString name, char value) { + super.addChar(name, value); + return this; + } + + @Override + public BinaryHeaders addByte(AsciiString name, byte value) { + super.addByte(name, value); + return this; + } + + @Override + public BinaryHeaders addShort(AsciiString name, short value) { + super.addShort(name, value); + return this; + } + + @Override + public BinaryHeaders addInt(AsciiString name, int value) { + super.addInt(name, value); + return this; + } + + @Override + public BinaryHeaders addLong(AsciiString name, long value) { + super.addLong(name, value); + return this; + } + + @Override + public BinaryHeaders addFloat(AsciiString name, float value) { + super.addFloat(name, value); + return this; + } + + @Override + public BinaryHeaders addDouble(AsciiString name, double value) { + super.addDouble(name, value); + return this; } @Override public BinaryHeaders add(BinaryHeaders headers) { - throw new UnsupportedOperationException("read only"); + super.add(headers); + return this; } @Override public BinaryHeaders set(AsciiString name, AsciiString value) { - throw new UnsupportedOperationException("read only"); + super.set(name, value); + return this; } @Override - public BinaryHeaders set(AsciiString name, Iterable values) { - throw new UnsupportedOperationException("read only"); + public BinaryHeaders set(AsciiString name, Iterable values) { + super.set(name, values); + return this; } @Override public BinaryHeaders set(AsciiString name, AsciiString... values) { - throw new UnsupportedOperationException("read only"); + super.set(name, values); + return this; + } + + @Override + public BinaryHeaders setObject(AsciiString name, Object value) { + super.setObject(name, value); + return this; + } + + @Override + public BinaryHeaders setObject(AsciiString name, Iterable values) { + super.setObject(name, values); + return this; + } + + @Override + public BinaryHeaders setObject(AsciiString name, Object... values) { + super.setObject(name, values); + return this; + } + + @Override + public BinaryHeaders setBoolean(AsciiString name, boolean value) { + super.setBoolean(name, value); + return this; + } + + @Override + public BinaryHeaders setChar(AsciiString name, char value) { + super.setChar(name, value); + return this; + } + + @Override + public BinaryHeaders setByte(AsciiString name, byte value) { + super.setByte(name, value); + return this; + } + + @Override + public BinaryHeaders setShort(AsciiString name, short value) { + super.setShort(name, value); + return this; + } + + @Override + public BinaryHeaders setInt(AsciiString name, int value) { + super.setInt(name, value); + return this; + } + + @Override + public BinaryHeaders setLong(AsciiString name, long value) { + super.setLong(name, value); + return this; + } + + @Override + public BinaryHeaders setFloat(AsciiString name, float value) { + super.setFloat(name, value); + return this; + } + + @Override + public BinaryHeaders setDouble(AsciiString name, double value) { + super.setDouble(name, value); + return this; } @Override public BinaryHeaders set(BinaryHeaders headers) { - throw new UnsupportedOperationException("read only"); + super.set(headers); + return this; } @Override public BinaryHeaders setAll(BinaryHeaders headers) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public boolean remove(AsciiString name) { - return false; + super.setAll(headers); + return this; } @Override public BinaryHeaders clear() { + super.clear(); return this; } - - @Override - public boolean contains(AsciiString name, AsciiString value) { - return false; - } - - @Override - public Iterator> iterator() { - return entries().iterator(); - } - - @Override - public BinaryHeaders forEachEntry(BinaryHeaderVisitor processor) { - return this; - } - - @Override - public int hashCode() { - return BinaryHeaders.Utils.hashCode(this); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof BinaryHeaders)) { - return false; - } - return ((BinaryHeaders) obj).isEmpty(); - } - - @Override - public String toString() { - return BinaryHeaders.Utils.toStringUtf8(this); - } } diff --git a/codec/src/main/java/io/netty/handler/codec/EmptyConvertibleHeaders.java b/codec/src/main/java/io/netty/handler/codec/EmptyConvertibleHeaders.java new file mode 100644 index 0000000000..9a4ce83876 --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/EmptyConvertibleHeaders.java @@ -0,0 +1,71 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package io.netty.handler.codec; + +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; + +public class EmptyConvertibleHeaders extends + EmptyHeaders implements ConvertibleHeaders { + + @Override + public ConvertedType getAndConvert(UnconvertedType name) { + return null; + } + + @Override + public ConvertedType getAndConvert(UnconvertedType name, ConvertedType defaultValue) { + return defaultValue; + } + + @Override + public ConvertedType getAndRemoveAndConvert(UnconvertedType name) { + return null; + } + + @Override + public ConvertedType getAndRemoveAndConvert(UnconvertedType name, ConvertedType defaultValue) { + return defaultValue; + } + + @Override + public List getAllAndConvert(UnconvertedType name) { + return Collections.emptyList(); + } + + @Override + public List getAllAndRemoveAndConvert(UnconvertedType name) { + return Collections.emptyList(); + } + + @Override + public List> entriesConverted() { + return Collections.emptyList(); + } + + @Override + public Iterator> iteratorConverted() { + return entriesConverted().iterator(); + } + + @Override + public Set namesAndConvert(Comparator comparator) { + return Collections.emptySet(); + } +} diff --git a/codec/src/main/java/io/netty/handler/codec/EmptyHeaders.java b/codec/src/main/java/io/netty/handler/codec/EmptyHeaders.java new file mode 100644 index 0000000000..1ab29f2726 --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/EmptyHeaders.java @@ -0,0 +1,536 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package io.netty.handler.codec; + +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; + +public class EmptyHeaders implements Headers { + @Override + public T get(T name) { + return null; + } + + @Override + public T get(T name, T defaultValue) { + return null; + } + + @Override + public T getAndRemove(T name) { + return null; + } + + @Override + public T getAndRemove(T name, T defaultValue) { + return null; + } + + @Override + public List getAll(T name) { + return Collections.emptyList(); + } + + @Override + public List getAllAndRemove(T name) { + return Collections.emptyList(); + } + + @Override + public Boolean getBoolean(T name) { + return null; + } + + @Override + public boolean getBoolean(T name, boolean defaultValue) { + return defaultValue; + } + + @Override + public Byte getByte(T name) { + return null; + } + + @Override + public byte getByte(T name, byte defaultValue) { + return defaultValue; + } + + @Override + public Character getChar(T name) { + return null; + } + + @Override + public char getChar(T name, char defaultValue) { + return defaultValue; + } + + @Override + public Short getShort(T name) { + return null; + } + + @Override + public short getInt(T name, short defaultValue) { + return defaultValue; + } + + @Override + public Integer getInt(T name) { + return null; + } + + @Override + public int getInt(T name, int defaultValue) { + return defaultValue; + } + + @Override + public Long getLong(T name) { + return null; + } + + @Override + public long getLong(T name, long defaultValue) { + return defaultValue; + } + + @Override + public Float getFloat(T name) { + return null; + } + + @Override + public float getFloat(T name, float defaultValue) { + return defaultValue; + } + + @Override + public Double getDouble(T name) { + return null; + } + + @Override + public double getDouble(T name, double defaultValue) { + return defaultValue; + } + + @Override + public Long getTimeMillis(T name) { + return null; + } + + @Override + public long getTimeMillis(T name, long defaultValue) { + return defaultValue; + } + + @Override + public Boolean getBooleanAndRemove(T name) { + return null; + } + + @Override + public boolean getBooleanAndRemove(T name, boolean defaultValue) { + return defaultValue; + } + + @Override + public Byte getByteAndRemove(T name) { + return null; + } + + @Override + public byte getByteAndRemove(T name, byte defaultValue) { + return defaultValue; + } + + @Override + public Character getCharAndRemove(T name) { + return null; + } + + @Override + public char getCharAndRemove(T name, char defaultValue) { + return defaultValue; + } + + @Override + public Short getShortAndRemove(T name) { + return null; + } + + @Override + public short getShortAndRemove(T name, short defaultValue) { + return defaultValue; + } + + @Override + public Integer getIntAndRemove(T name) { + return null; + } + + @Override + public int getIntAndRemove(T name, int defaultValue) { + return defaultValue; + } + + @Override + public Long getLongAndRemove(T name) { + return null; + } + + @Override + public long getLongAndRemove(T name, long defaultValue) { + return defaultValue; + } + + @Override + public Float getFloatAndRemove(T name) { + return null; + } + + @Override + public float getFloatAndRemove(T name, float defaultValue) { + return defaultValue; + } + + @Override + public Double getDoubleAndRemove(T name) { + return null; + } + + @Override + public double getDoubleAndRemove(T name, double defaultValue) { + return defaultValue; + } + + @Override + public Long getTimeMillisAndRemove(T name) { + return null; + } + + @Override + public long getTimeMillisAndRemove(T name, long defaultValue) { + return defaultValue; + } + + @Override + public List> entries() { + return Collections.emptyList(); + } + + @Override + public boolean contains(T name) { + return false; + } + + @Override + public boolean contains(T name, T value) { + return false; + } + + @Override + public boolean containsObject(T name, Object value) { + return false; + } + + @Override + public boolean containsBoolean(T name, int value) { + return false; + } + + @Override + public boolean containsByte(T name, byte value) { + return false; + } + + @Override + public boolean containsChar(T name, char value) { + return false; + } + + @Override + public boolean containsShort(T name, byte value) { + return false; + } + + @Override + public boolean containsInt(T name, int value) { + return false; + } + + @Override + public boolean containsLong(T name, long value) { + return false; + } + + @Override + public boolean containsFloat(T name, float value) { + return false; + } + + @Override + public boolean containsDouble(T name, double value) { + return false; + } + + @Override + public boolean contains(T name, T value, Comparator comparator) { + return false; + } + + @Override + public boolean contains(T name, T value, + Comparator keyComparator, Comparator valueComparator) { + return false; + } + + @Override + public boolean containsObject(T name, Object value, Comparator comparator) { + return false; + } + + @Override + public boolean containsObject(T name, Object value, Comparator keyComparator, + Comparator valueComparator) { + return false; + } + + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public Set names() { + return Collections.emptySet(); + } + + @Override + public List namesList() { + return Collections.emptyList(); + } + + @Override + public Headers add(T name, T value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers add(T name, Iterable values) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers add(T name, T... values) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers addObject(T name, Object value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers addObject(T name, Iterable values) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers addObject(T name, Object... values) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers addBoolean(T name, boolean value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers addByte(T name, byte value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers addChar(T name, char value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers addShort(T name, short value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers addInt(T name, int value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers addLong(T name, long value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers addFloat(T name, float value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers addDouble(T name, double value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers add(Headers headers) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers set(T name, T value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers set(T name, Iterable values) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers set(T name, T... values) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers setObject(T name, Object value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers setObject(T name, Iterable values) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers setObject(T name, Object... values) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers setBoolean(T name, boolean value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers setByte(T name, byte value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers setChar(T name, char value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers setShort(T name, short value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers setInt(T name, int value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers setLong(T name, long value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers setFloat(T name, float value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers setDouble(T name, double value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers set(Headers headers) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Headers setAll(Headers headers) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public boolean remove(T name) { + return false; + } + + @Override + public Headers clear() { + return this; + } + + @Override + public Iterator> iterator() { + return entries().iterator(); + } + + @Override + public Entry forEachEntry(io.netty.handler.codec.Headers.EntryVisitor visitor) throws Exception { + return null; + } + + @Override + public T forEachName(io.netty.handler.codec.Headers.NameVisitor visitor) throws Exception { + return null; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Headers)) { + return false; + } + + Headers rhs = (Headers) o; + return isEmpty() && rhs.isEmpty(); + } + + @Override + public int hashCode() { + return 1; + } + + @Override + public String toString() { + return new StringBuilder(getClass().getSimpleName()).append('[').append(']').toString(); + } +} diff --git a/codec/src/main/java/io/netty/handler/codec/EmptyTextHeaders.java b/codec/src/main/java/io/netty/handler/codec/EmptyTextHeaders.java index ebd218d0d4..1f9a7dc43e 100644 --- a/codec/src/main/java/io/netty/handler/codec/EmptyTextHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/EmptyTextHeaders.java @@ -16,238 +16,209 @@ package io.netty.handler.codec; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; - -public class EmptyTextHeaders implements TextHeaders { - - protected EmptyTextHeaders() { } - - @Override - public String get(CharSequence name) { - return null; +public class EmptyTextHeaders extends EmptyConvertibleHeaders implements TextHeaders { + protected EmptyTextHeaders() { } @Override - public String get(CharSequence name, String defaultValue) { - return defaultValue; - } - - @Override - public Integer getInt(CharSequence name) { - return null; - } - - @Override - public int getInt(CharSequence name, int defaultValue) { - return defaultValue; - } - - @Override - public Long getLong(CharSequence name) { - return null; - } - - @Override - public long getLong(CharSequence name, long defaultValue) { - return defaultValue; - } - - @Override - public Long getTimeMillis(CharSequence name) { - return null; - } - - @Override - public long getTimeMillis(CharSequence name, long defaultValue) { - return defaultValue; - } - - @Override - public String getAndRemove(CharSequence name) { - return null; - } - - @Override - public String getAndRemove(CharSequence name, String defaultValue) { - return defaultValue; - } - - @Override - public Integer getIntAndRemove(CharSequence name) { - return null; - } - - @Override - public int getIntAndRemove(CharSequence name, int defaultValue) { - return defaultValue; - } - - @Override - public Long getLongAndRemove(CharSequence name) { - return null; - } - - @Override - public long getLongAndRemove(CharSequence name, long defaultValue) { - return defaultValue; - } - - @Override - public Long getTimeMillisAndRemove(CharSequence name) { - return null; - } - - @Override - public long getTimeMillisAndRemove(CharSequence name, long defaultValue) { - return defaultValue; - } - - @Override - public CharSequence getUnconverted(CharSequence name) { - return null; - } - - @Override - public CharSequence getUnconvertedAndRemove(CharSequence name) { - return null; - } - - @Override - public List getAll(CharSequence name) { - return Collections.emptyList(); - } - - @Override - public List getAllUnconverted(CharSequence name) { - return Collections.emptyList(); - } - - @Override - public List getAllAndRemove(CharSequence name) { - return Collections.emptyList(); - } - - @Override - public List getAllUnconvertedAndRemove(CharSequence name) { - return Collections.emptyList(); - } - - @Override - public List> entries() { - return Collections.emptyList(); - } - - @Override - public List> unconvertedEntries() { - return Collections.emptyList(); - } - - @Override - public boolean contains(CharSequence name) { + public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) { return false; } @Override - public int size() { - return 0; + public boolean containsObject(CharSequence name, Object value, boolean ignoreCase) { + return false; } @Override - public boolean isEmpty() { - return true; + public TextHeaders add(CharSequence name, CharSequence value) { + super.add(name, value); + return this; } @Override - public Set names() { - return Collections.emptySet(); + public TextHeaders add(CharSequence name, Iterable values) { + super.add(name, values); + return this; } @Override - public Set unconvertedNames() { - return Collections.emptySet(); + public TextHeaders add(CharSequence name, CharSequence... values) { + super.add(name, values); + return this; } @Override - public TextHeaders add(CharSequence name, Object value) { - throw new UnsupportedOperationException("read only"); + public TextHeaders addObject(CharSequence name, Object value) { + super.addObject(name, value); + return this; } @Override - public TextHeaders add(CharSequence name, Iterable values) { - throw new UnsupportedOperationException("read only"); + public TextHeaders addObject(CharSequence name, Iterable values) { + super.addObject(name, values); + return this; } @Override - public TextHeaders add(CharSequence name, Object... values) { - throw new UnsupportedOperationException("read only"); + public TextHeaders addObject(CharSequence name, Object... values) { + super.addObject(name, values); + return this; + } + + @Override + public TextHeaders addBoolean(CharSequence name, boolean value) { + super.addBoolean(name, value); + return this; + } + + @Override + public TextHeaders addChar(CharSequence name, char value) { + super.addChar(name, value); + return this; + } + + @Override + public TextHeaders addByte(CharSequence name, byte value) { + super.addByte(name, value); + return this; + } + + @Override + public TextHeaders addShort(CharSequence name, short value) { + super.addShort(name, value); + return this; + } + + @Override + public TextHeaders addInt(CharSequence name, int value) { + super.addInt(name, value); + return this; + } + + @Override + public TextHeaders addLong(CharSequence name, long value) { + super.addLong(name, value); + return this; + } + + @Override + public TextHeaders addFloat(CharSequence name, float value) { + super.addFloat(name, value); + return this; + } + + @Override + public TextHeaders addDouble(CharSequence name, double value) { + super.addDouble(name, value); + return this; } @Override public TextHeaders add(TextHeaders headers) { - throw new UnsupportedOperationException("read only"); + super.add(headers); + return this; } @Override - public TextHeaders set(CharSequence name, Object value) { - throw new UnsupportedOperationException("read only"); + public TextHeaders set(CharSequence name, CharSequence value) { + super.set(name, value); + return this; } @Override - public TextHeaders set(CharSequence name, Iterable values) { - throw new UnsupportedOperationException("read only"); + public TextHeaders set(CharSequence name, Iterable values) { + super.set(name, values); + return this; } @Override - public TextHeaders set(CharSequence name, Object... values) { - throw new UnsupportedOperationException("read only"); + public TextHeaders set(CharSequence name, CharSequence... values) { + super.set(name, values); + return this; + } + + @Override + public TextHeaders setObject(CharSequence name, Object value) { + super.setObject(name, value); + return this; + } + + @Override + public TextHeaders setObject(CharSequence name, Iterable values) { + super.setObject(name, values); + return this; + } + + @Override + public TextHeaders setObject(CharSequence name, Object... values) { + super.setObject(name, values); + return this; + } + + @Override + public TextHeaders setBoolean(CharSequence name, boolean value) { + super.setBoolean(name, value); + return this; + } + + @Override + public TextHeaders setChar(CharSequence name, char value) { + super.setChar(name, value); + return this; + } + + @Override + public TextHeaders setByte(CharSequence name, byte value) { + super.setByte(name, value); + return this; + } + + @Override + public TextHeaders setShort(CharSequence name, short value) { + super.setShort(name, value); + return this; + } + + @Override + public TextHeaders setInt(CharSequence name, int value) { + super.setInt(name, value); + return this; + } + + @Override + public TextHeaders setLong(CharSequence name, long value) { + super.setLong(name, value); + return this; + } + + @Override + public TextHeaders setFloat(CharSequence name, float value) { + super.setFloat(name, value); + return this; + } + + @Override + public TextHeaders setDouble(CharSequence name, double value) { + super.setDouble(name, value); + return this; } @Override public TextHeaders set(TextHeaders headers) { - throw new UnsupportedOperationException("read only"); + super.set(headers); + return this; } @Override - public TextHeaders setAll(TextHeaders rhs) { - throw new UnsupportedOperationException("read only"); - } - - @Override - public boolean remove(CharSequence name) { - return false; + public TextHeaders setAll(TextHeaders headers) { + super.setAll(headers); + return this; } @Override public TextHeaders clear() { - return this; - } - - @Override - public boolean contains(CharSequence name, Object value) { - return false; - } - - @Override - public boolean contains(CharSequence name, Object value, boolean ignoreCase) { - return false; - } - - @Override - public Iterator> iterator() { - return entries().iterator(); - } - - @Override - public Iterator> unconvertedIterator() { - return unconvertedEntries().iterator(); - } - - @Override - public TextHeaders forEachEntry(TextHeaderProcessor processor) { + super.clear(); return this; } } diff --git a/codec/src/main/java/io/netty/handler/codec/HeaderMap.java b/codec/src/main/java/io/netty/handler/codec/HeaderMap.java deleted file mode 100644 index f741e70ce4..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/HeaderMap.java +++ /dev/null @@ -1,839 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package io.netty.handler.codec; - -import static io.netty.util.internal.ObjectUtil.checkNotNull; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.TreeSet; - -/** - * Basic map of header names to values. This is meant to be a central storage mechanism by all - * headers implementations. All keys and values are stored as {@link CharSequence}. - */ -public class HeaderMap implements Iterable> { - private static final int BUCKET_SIZE = 17; - private static final int HASH_CODE_PRIME = 31; - - public static final NameConverter IDENTITY_NAME_CONVERTER = new NameConverter() { - @Override - public CharSequence convertName(CharSequence name) { - return name; - } - }; - - public interface EntryVisitor { - boolean visit(Entry entry); - } - - public interface NameVisitor { - boolean visit(CharSequence name); - } - - public interface NameConverter { - CharSequence convertName(CharSequence name); - } - - public interface ValueMarshaller { - CharSequence marshal(Object value); - } - - public interface ValueUnmarshaller { - T unmarshal(CharSequence value); - } - - private final HeaderEntry[] entries = new HeaderEntry[BUCKET_SIZE]; - private final HeaderEntry head = new HeaderEntry(); - private final NameConverter nameConverter; - private final boolean ignoreCase; - int size; - - public HeaderMap() { - this(true); - } - - public HeaderMap(boolean ignoreCase) { - this(ignoreCase, IDENTITY_NAME_CONVERTER); - } - - public HeaderMap(boolean ignoreCase, NameConverter nameConverter) { - this.nameConverter = checkNotNull(nameConverter, "nameConverter"); - head.before = head.after = head; - this.ignoreCase = ignoreCase; - } - - public boolean isIgnoreCase() { - return ignoreCase; - } - - public HeaderMap add(CharSequence name, CharSequence value) { - name = convertName(name); - checkNotNull(value, "value"); - int h = hashCode(name); - int i = index(h); - add0(h, i, name, value); - return this; - } - - public HeaderMap add(CharSequence name, Iterable values) { - name = convertName(name); - checkNotNull(values, "values"); - - int h = hashCode(name); - int i = index(h); - for (CharSequence v: values) { - if (v == null) { - break; - } - add0(h, i, name, v); - } - return this; - } - - public HeaderMap add(CharSequence name, CharSequence... values) { - name = convertName(name); - checkNotNull(values, "values"); - - int h = hashCode(name); - int i = index(h); - for (CharSequence v: values) { - if (v == null) { - break; - } - add0(h, i, name, v); - } - return this; - } - - public HeaderMap addConvertedValues(CharSequence name, ValueMarshaller converter, Iterable values) { - name = convertName(name); - checkNotNull(values, "values"); - checkNotNull(converter, "converter"); - - int h = hashCode(name); - int i = index(h); - for (Object v : values) { - if (v == null) { - break; - } - CharSequence convertedVal = converter.marshal(v); - add0(h, i, name, convertedVal); - } - return this; - } - - public HeaderMap addConvertedValues(CharSequence name, ValueMarshaller converter, Object... values) { - name = convertName(name); - checkNotNull(values, "values"); - checkNotNull(converter, "converter"); - - int h = hashCode(name); - int i = index(h); - for (Object v : values) { - if (v == null) { - break; - } - CharSequence convertedVal = converter.marshal(v); - add0(h, i, name, convertedVal); - } - return this; - } - - private void add0(int h, int i, CharSequence name, CharSequence value) { - // Update the hash table. - HeaderEntry e = entries[i]; - HeaderEntry newEntry; - entries[i] = newEntry = new HeaderEntry(h, name, value); - newEntry.next = e; - - // Update the linked list. - newEntry.addBefore(head); - } - - public HeaderMap add(HeaderMap headers) { - checkNotNull(headers, "headers"); - - add0(headers); - return this; - } - - private void add0(HeaderMap headers) { - if (headers.isEmpty()) { - return; - } - - HeaderMap m = (HeaderMap) headers; - HeaderEntry e = m.head.after; - while (e != m.head) { - add(e.name, e.value); - e = e.after; - } - } - - public boolean remove(CharSequence name) { - checkNotNull(name, "name"); - int h = hashCode(name); - int i = index(h); - return remove0(h, i, name); - } - - private boolean remove0(int h, int i, CharSequence name) { - HeaderEntry e = entries[i]; - if (e == null) { - return false; - } - - boolean removed = false; - for (;;) { - if (e.hash == h && nameEquals(e.name, name)) { - e.remove(); - HeaderEntry next = e.next; - if (next != null) { - entries[i] = next; - e = next; - } else { - entries[i] = null; - return true; - } - removed = true; - } else { - break; - } - } - - for (;;) { - HeaderEntry next = e.next; - if (next == null) { - break; - } - if (next.hash == h && nameEquals(next.name, name)) { - e.next = next.next; - next.remove(); - removed = true; - } else { - e = next; - } - } - - return removed; - } - - public HeaderMap set(CharSequence name, CharSequence value) { - name = convertName(name); - checkNotNull(value, "value"); - int h = hashCode(name); - int i = index(h); - remove0(h, i, name); - add0(h, i, name, value); - return this; - } - - public HeaderMap set(CharSequence name, Iterable values) { - name = convertName(name); - checkNotNull(values, "values"); - - int h = hashCode(name); - int i = index(h); - - remove0(h, i, name); - for (CharSequence v: values) { - if (v == null) { - break; - } - add0(h, i, name, v); - } - - return this; - } - - public HeaderMap set(CharSequence name, CharSequence... values) { - name = convertName(name); - checkNotNull(values, "values"); - - int h = hashCode(name); - int i = index(h); - - remove0(h, i, name); - for (CharSequence v: values) { - if (v == null) { - break; - } - add0(h, i, name, v); - } - - return this; - } - - public HeaderMap set(CharSequence name, ValueMarshaller converter, Iterable values) { - name = convertName(name); - checkNotNull(converter, "converter"); - checkNotNull(values, "values"); - - int h = hashCode(name); - int i = index(h); - - remove0(h, i, name); - for (Object v: values) { - if (v == null) { - break; - } - CharSequence convertedVal = converter.marshal(v); - add0(h, i, name, convertedVal); - } - - return this; - } - - public HeaderMap set(CharSequence name, ValueMarshaller converter, Object... values) { - name = convertName(name); - checkNotNull(converter, "converter"); - checkNotNull(values, "values"); - - int h = hashCode(name); - int i = index(h); - - remove0(h, i, name); - for (Object v: values) { - if (v == null) { - break; - } - CharSequence convertedVal = converter.marshal(v); - add0(h, i, name, convertedVal); - } - - return this; - } - - public HeaderMap set(HeaderMap headers) { - checkNotNull(headers, "headers"); - - clear(); - add0(headers); - return this; - } - - public HeaderMap setAll(HeaderMap headers) { - checkNotNull(headers, "headers"); - - HeaderEntry e = headers.head.after; - while (e != headers.head) { - set(e.name, e.value); - e = e.after; - } - - return this; - } - - public HeaderMap clear() { - Arrays.fill(entries, null); - head.before = head.after = head; - size = 0; - return this; - } - - public CharSequence get(CharSequence name) { - checkNotNull(name, "name"); - - int h = hashCode(name); - int i = index(h); - HeaderEntry e = entries[i]; - CharSequence value = null; - // loop until the first header was found - while (e != null) { - if (e.hash == h && nameEquals(e.name, name)) { - value = e.value; - } - - e = e.next; - } - return value; - } - - public CharSequence get(CharSequence name, CharSequence defaultValue) { - CharSequence v = get(name); - if (v == null) { - return defaultValue; - } - return v; - } - - public CharSequence getAndRemove(CharSequence name) { - checkNotNull(name, "name"); - int h = hashCode(name); - int i = index(h); - HeaderEntry e = entries[i]; - if (e == null) { - return null; - } - - CharSequence value = null; - for (;;) { - if (e.hash == h && nameEquals(e.name, name)) { - value = e.value; - e.remove(); - HeaderEntry next = e.next; - if (next != null) { - entries[i] = next; - e = next; - } else { - entries[i] = null; - return value; - } - } else { - break; - } - } - - for (;;) { - HeaderEntry next = e.next; - if (next == null) { - break; - } - if (next.hash == h && nameEquals(next.name, name)) { - value = next.value; - e.next = next.next; - next.remove(); - } else { - e = next; - } - } - - return value; - } - - public CharSequence getAndRemove(CharSequence name, CharSequence defaultValue) { - CharSequence v = getAndRemove(name); - if (v == null) { - return defaultValue; - } - return v; - } - - public List getAll(CharSequence name) { - checkNotNull(name, "name"); - - List values = new ArrayList(4); - int h = hashCode(name); - int i = index(h); - HeaderEntry e = entries[i]; - while (e != null) { - if (e.hash == h && nameEquals(e.name, name)) { - values.add(e.getValue()); - } - e = e.next; - } - - Collections.reverse(values); - return values; - } - - public List getAllAndRemove(CharSequence name) { - checkNotNull(name, "name"); - int h = hashCode(name); - int i = index(h); - HeaderEntry e = entries[i]; - if (e == null) { - return null; - } - - List values = new ArrayList(4); - for (;;) { - if (e.hash == h && nameEquals(e.name, name)) { - values.add(e.getValue()); - e.remove(); - HeaderEntry next = e.next; - if (next != null) { - entries[i] = next; - e = next; - } else { - entries[i] = null; - Collections.reverse(values); - return values; - } - } else { - break; - } - } - - for (;;) { - HeaderEntry next = e.next; - if (next == null) { - break; - } - if (next.hash == h && nameEquals(next.name, name)) { - values.add(next.getValue()); - e.next = next.next; - next.remove(); - } else { - e = next; - } - } - - Collections.reverse(values); - return values; - } - - public List getAll(CharSequence name, ValueUnmarshaller unmarshaller) { - checkNotNull(name, "name"); - checkNotNull(unmarshaller, "unmarshaller"); - - List values = new ArrayList(4); - int h = hashCode(name); - int i = index(h); - HeaderEntry e = entries[i]; - while (e != null) { - if (e.hash == h && nameEquals(e.name, name)) { - values.add(unmarshaller.unmarshal(e.value)); - } - e = e.next; - } - - Collections.reverse(values); - return values; - } - - public List getAllAndRemove(CharSequence name, ValueUnmarshaller unmarshaller) { - checkNotNull(name, "name"); - checkNotNull(unmarshaller, "unmarshaller"); - - int h = hashCode(name); - int i = index(h); - HeaderEntry e = entries[i]; - if (e == null) { - return null; - } - - List values = new ArrayList(4); - for (;;) { - if (e.hash == h && nameEquals(e.name, name)) { - values.add(unmarshaller.unmarshal(e.value)); - e.remove(); - HeaderEntry next = e.next; - if (next != null) { - entries[i] = next; - e = next; - } else { - entries[i] = null; - Collections.reverse(values); - return values; - } - } else { - break; - } - } - - for (;;) { - HeaderEntry next = e.next; - if (next == null) { - break; - } - if (next.hash == h && nameEquals(next.name, name)) { - values.add(unmarshaller.unmarshal(next.getValue())); - e.next = next.next; - next.remove(); - } else { - e = next; - } - } - - Collections.reverse(values); - return values; - } - - public List> entries() { - int cnt = 0; - int size = size(); - @SuppressWarnings("unchecked") - Map.Entry[] all = new Map.Entry[size]; - - HeaderEntry e = head.after; - while (e != head) { - all[cnt ++] = e; - e = e.after; - } - - assert size == cnt; - return Arrays.asList(all); - } - - @Override - public Iterator> iterator() { - return new HeaderIterator(); - } - - public boolean contains(CharSequence name) { - return get(name) != null; - } - - public int size() { - return size; - } - - public boolean isEmpty() { - return head == head.after; - } - - public boolean contains(CharSequence name, CharSequence value) { - return contains(name, value, false); - } - - public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) { - checkNotNull(name, "name"); - checkNotNull(value, "value"); - int h = hashCode(name); - int i = index(h); - HeaderEntry e = entries[i]; - while (e != null) { - if (e.hash == h && nameEquals(e.name, name)) { - if (valueEquals(e.value, value, ignoreCase)) { - return true; - } - } - e = e.next; - } - return false; - } - - public Set names() { - return names(ignoreCase); - } - - /** - * Get the set of names for all text headers - * @param caseInsensitive {@code true} if names should be added in a case insensitive - * @return The set of names for all text headers - */ - public Set names(boolean caseInsensitive) { - final Set names = - caseInsensitive ? new TreeSet( - AsciiString.CHARSEQUENCE_CASE_INSENSITIVE_ORDER) - : new LinkedHashSet(size()); - forEachName(new NameVisitor() { - @Override - public boolean visit(CharSequence name) { - names.add(name); - return true; - } - }); - return names; - } - - public HeaderMap forEachEntry(EntryVisitor visitor) { - HeaderEntry e = head.after; - while (e != head) { - if (!visitor.visit(e)) { - break; - } - e = e.after; - } - return this; - } - - public void forEachName(NameVisitor visitor) { - HeaderEntry e = head.after; - while (e != head) { - if (!visitor.visit(e.getKey())) { - return; - } - e = e.after; - } - } - - @Override - public int hashCode() { - int result = 1; - for (CharSequence name : names()) { - result = HASH_CODE_PRIME * result + name.hashCode(); - Set values = new TreeSet(getAll(name)); - for (CharSequence value : values) { - result = HASH_CODE_PRIME * result + value.hashCode(); - } - } - return result; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof HeaderMap)) { - return false; - } - - // First, check that the set of names match. - HeaderMap h2 = (HeaderMap) o; - Set names = names(); - if (!names.equals(h2.names())) { - return false; - } - - // Compare the values for each name. - for (CharSequence name : names) { - List values = getAll(name); - List otherValues = h2.getAll(name); - if (values.size() != otherValues.size()) { - return false; - } - - // Convert the values to a set and remove values from the other object to see if - // they match. - Set valueSet = new HashSet(values); - valueSet.removeAll(otherValues); - if (!valueSet.isEmpty()) { - return false; - } - } - - return true; - } - - @Override - public String toString() { - StringBuilder builder = - new StringBuilder('['); - Set names = names(true); - for (CharSequence name : names) { - Set valueSet = new TreeSet(getAll(name)); - for (CharSequence value : valueSet) { - builder.append(name).append(": ").append(value).append(", "); - } - } - // Now remove the last ", " if there is one. - if (builder.length() >= 3) { - builder.setLength(builder.length() - 2); - } - return builder.append("]").toString(); - } - - private boolean nameEquals(CharSequence a, CharSequence b) { - return equals(a, b, ignoreCase); - } - - private static boolean valueEquals(CharSequence a, CharSequence b, boolean ignoreCase) { - return equals(a, b, ignoreCase); - } - - private static boolean equals(CharSequence a, CharSequence b, boolean ignoreCase) { - if (ignoreCase) { - return AsciiString.equalsIgnoreCase(a, b); - } else { - return AsciiString.equals(a, b); - } - } - - private static int index(int hash) { - return Math.abs(hash % BUCKET_SIZE); - } - - private CharSequence convertName(CharSequence name) { - return nameConverter.convertName(checkNotNull(name, "name")); - } - - private static int hashCode(CharSequence name) { - return AsciiString.caseInsensitiveHashCode(name); - } - - private final class HeaderEntry implements Map.Entry { - final int hash; - final CharSequence name; - CharSequence value; - HeaderEntry next; - HeaderEntry before, after; - - HeaderEntry(int hash, CharSequence name, CharSequence value) { - this.hash = hash; - this.name = name; - this.value = value; - } - - HeaderEntry() { - hash = -1; - name = null; - value = null; - } - - void remove() { - before.after = after; - after.before = before; - --size; - } - - void addBefore(HeaderEntry e) { - after = e; - before = e.before; - before.after = this; - after.before = this; - ++size; - } - - @Override - public CharSequence getKey() { - return name; - } - - @Override - public CharSequence getValue() { - return value; - } - - @Override - public CharSequence setValue(CharSequence value) { - checkNotNull(value, "value"); - checkNotNull(value, "value"); - CharSequence oldValue = this.value; - this.value = value; - return oldValue; - } - - @Override - public String toString() { - return new StringBuilder(name).append('=').append(value).toString(); - } - } - - protected final class HeaderIterator implements Iterator> { - - private HeaderEntry current = head; - - @Override - public boolean hasNext() { - return current.after != head; - } - - @Override - public Entry next() { - current = current.after; - - if (current == head) { - throw new NoSuchElementException(); - } - - return current; - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/Headers.java b/codec/src/main/java/io/netty/handler/codec/Headers.java new file mode 100644 index 0000000000..c00b5f2d8a --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/Headers.java @@ -0,0 +1,1085 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package io.netty.handler.codec; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +public interface Headers extends Iterable> { + /** + * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}. + */ + public interface EntryVisitor { + /** + * @return
    + *
  • {@code true} if the processor wants to continue the loop and handle the entry.
  • + *
  • {@code false} if the processor wants to stop handling headers and abort the loop.
  • + *
+ */ + boolean visit(Map.Entry entry) throws Exception; + } + + /** + * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}. + */ + public interface NameVisitor { + /** + * @return
    + *
  • {@code true} if the processor wants to continue the loop and handle the entry.
  • + *
  • {@code false} if the processor wants to stop handling headers and abort the loop.
  • + *
+ */ + boolean visit(T name) throws Exception; + } + + /** + * Converts to/from a generic object to the type of the name for this map + */ + public interface ValueConverter { + T convert(Object value); + + T convert(boolean value); + + boolean convertToBoolean(T value); + + T convert(byte value); + + byte convertToByte(T value); + + T convert(char value); + + char convertToChar(T value); + + T convert(short value); + + short convertToShort(T value); + + T convert(int value); + + int convertToInt(T value); + + T convert(long value); + + long convertToLong(T value); + + long convertToTimeMillis(T value); + + T convert(float value); + + float convertToFloat(T value); + + T convert(double value); + + double convertToDouble(T value); + } + + /** + * Returns the value of a header with the specified name. If there are more than one values for the specified name, + * the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found. {@code null} if there's no such header. + */ + T get(T name); + + /** + * Returns the value of a header with the specified name. If there are more than one values for the specified name, + * the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found. {@code defaultValue} if there's no such header. + */ + T get(T name, T defaultValue); + + /** + * Returns and removes the value of a header with the specified name. If there are more than one values for the + * specified name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value or {@code null} if there is no such header + */ + T getAndRemove(T name); + + /** + * Returns and removes the value of a header with the specified name. If there are more than one values for the + * specified name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value or {@code defaultValue} if there is no such header + */ + T getAndRemove(T name, T defaultValue); + + /** + * Returns the values of headers with the specified name + * + * @param name The name of the headers to search + * @return A {@link List} of header values which will be empty if no values are found + */ + List getAll(T name); + + /** + * Returns and Removes the values of headers with the specified name + * + * @param name The name of the headers to search + * @return A {@link List} of header values which will be empty if no values are found + */ + List getAllAndRemove(T name); + + /** + * Returns the boolean value of a header with the specified name. If there are more than one values for the + * specified name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found and its value is a boolean. {@code null} if there's no such + * header or its value is not a boolean. + */ + Boolean getBoolean(T name); + + /** + * Returns the boolean value of a header with the specified name. If there are more than one values for the + * specified name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found and its value is a boolean. {@code defaultValue} if there's + * no such header or its value is not a boolean. + */ + boolean getBoolean(T name, boolean defaultValue); + + /** + * Returns the byte value of a header with the specified name. If there are more than one values for the specified + * name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found and its value is a byte. {@code null} if there's no such + * header or its value is not a byte. + */ + Byte getByte(T name); + + /** + * Returns the byte value of a header with the specified name. If there are more than one values for the specified + * name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found and its value is a byte. {@code defaultValue} if there's no + * such header or its value is not a byte. + */ + byte getByte(T name, byte defaultValue); + + /** + * Returns the char value of a header with the specified name. If there are more than one values for the specified + * name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found and its value is a char. {@code null} if there's no such + * header or its value is not a char. + */ + Character getChar(T name); + + /** + * Returns the char value of a header with the specified name. If there are more than one values for the specified + * name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found and its value is a char. {@code defaultValue} if there's no + * such header or its value is not a char. + */ + char getChar(T name, char defaultValue); + + /** + * Returns the short value of a header with the specified name. If there are more than one values for the specified + * name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found and its value is a short. {@code null} if there's no such + * header or its value is not a short. + */ + Short getShort(T name); + + /** + * Returns the short value of a header with the specified name. If there are more than one values for the specified + * name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found and its value is a short. {@code defaultValue} if there's + * no such header or its value is not a short. + */ + short getInt(T name, short defaultValue); + + /** + * Returns the integer value of a header with the specified name. If there are more than one values for the + * specified name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found and its value is an integer. {@code null} if there's no + * such header or its value is not an integer. + */ + Integer getInt(T name); + + /** + * Returns the integer value of a header with the specified name. If there are more than one values for the + * specified name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found and its value is an integer. {@code defaultValue} if + * there's no such header or its value is not an integer. + */ + int getInt(T name, int defaultValue); + + /** + * Returns the long value of a header with the specified name. If there are more than one values for the specified + * name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found and its value is a long. {@code null} if there's no such + * header or its value is not a long. + */ + Long getLong(T name); + + /** + * Returns the long value of a header with the specified name. If there are more than one values for the specified + * name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found and its value is a long. {@code defaultValue} if there's no + * such header or its value is not a long. + */ + long getLong(T name, long defaultValue); + + /** + * Returns the float value of a header with the specified name. If there are more than one values for the specified + * name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found and its value is a float. {@code null} if there's no such + * header or its value is not a float. + */ + Float getFloat(T name); + + /** + * Returns the float value of a header with the specified name. If there are more than one values for the specified + * name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found and its value is a float. {@code defaultValue} if there's + * no such header or its value is not a float. + */ + float getFloat(T name, float defaultValue); + + /** + * Returns the double value of a header with the specified name. If there are more than one values for the specified + * name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found and its value is a double. {@code null} if there's no such + * header or its value is not a double. + */ + Double getDouble(T name); + + /** + * Returns the double value of a header with the specified name. If there are more than one values for the specified + * name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found and its value is a double. {@code defaultValue} if there's + * no such header or its value is not a double. + */ + double getDouble(T name, double defaultValue); + + /** + * Returns the date value of a header with the specified name as milliseconds. If there are more than one values for + * the specified name, the first value is returned. + * + * @param name The name of the header to search + * @return the first header value in milliseconds if the header is found and its value is a date. {@code null} if + * there's no such header or its value is not a date. + */ + Long getTimeMillis(T name); + + /** + * Returns the date value of a header with the specified name as milliseconds. If there are more than one values for + * the specified name, the first value is returned. + * + * @param name The name of the header to search + * @param defaultValue default value + * @return the first header value in milliseconds if the header is found and its value is a date. + * {@code defaultValue} if there's no such header or its value is not a date. + */ + long getTimeMillis(T name, long defaultValue); + + /** + * Returns and removes the boolean value of a header with the specified name. If there are more than one values for + * the specified name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found and its value is a boolean. {@code null} if there's no such + * header or its value is not a boolean. + */ + Boolean getBooleanAndRemove(T name); + + /** + * Returns and removes the boolean value of a header with the specified name. If there are more than one values for + * the specified name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found and its value is a boolean. {@code defaultValue} if there + * is no such header or its value of header is not a boolean. + */ + boolean getBooleanAndRemove(T name, boolean defaultValue); + + /** + * Returns and removes the byte value of a header with the specified name. If there are more than one values for the + * specified name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found and its value is a byte. {@code null} if there's no such + * header or its value is not a byte. + */ + Byte getByteAndRemove(T name); + + /** + * Returns and removes the byte value of a header with the specified name. If there are more than one values for the + * specified name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found and its value is a byte. {@code defaultValue} if there is + * no such header or its value of header is not a byte. + */ + byte getByteAndRemove(T name, byte defaultValue); + + /** + * Returns and removes the char value of a header with the specified name. If there are more than one values for the + * specified name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found and its value is a char. {@code null} if there's no such + * header or its value is not a char. + */ + Character getCharAndRemove(T name); + + /** + * Returns and removes the char value of a header with the specified name. If there are more than one values for the + * specified name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found and its value is a char. {@code defaultValue} if there is + * no such header or its value of header is not a char. + */ + char getCharAndRemove(T name, char defaultValue); + + /** + * Returns and removes the short value of a header with the specified name. If there are more than one values for + * the specified name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found and its value is a short. {@code null} if there's no such + * header or its value is not a short. + */ + Short getShortAndRemove(T name); + + /** + * Returns and removes the short value of a header with the specified name. If there are more than one values for + * the specified name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found and its value is a short. {@code defaultValue} if there is + * no such header or its value of header is not a short. + */ + short getShortAndRemove(T name, short defaultValue); + + /** + * Returns and removes the integer value of a header with the specified name. If there are more than one values for + * the specified name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found and its value is an integer. {@code null} if there's no + * such header or its value is not an integer. + */ + Integer getIntAndRemove(T name); + + /** + * Returns and removes the integer value of a header with the specified name. If there are more than one values for + * the specified name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found and its value is an integer. {@code defaultValue} if there + * is no such header or its value of header is not an integer. + */ + int getIntAndRemove(T name, int defaultValue); + + /** + * Returns and removes the long value of a header with the specified name. If there are more than one values for the + * specified name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found and its value is a long. {@code null} if there's no such + * header or its value is not a long. + */ + Long getLongAndRemove(T name); + + /** + * Returns and removes the long value of a header with the specified name. If there are more than one values for the + * specified name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found and its value is a long. {@code defaultValue} if there's no + * such header or its value is not a long. + */ + long getLongAndRemove(T name, long defaultValue); + + /** + * Returns and removes the float value of a header with the specified name. If there are more than one values for + * the specified name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found and its value is a float. {@code null} if there's no such + * header or its value is not a float. + */ + Float getFloatAndRemove(T name); + + /** + * Returns and removes the float value of a header with the specified name. If there are more than one values for + * the specified name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found and its value is a float. {@code defaultValue} if there's + * no such header or its value is not a float. + */ + float getFloatAndRemove(T name, float defaultValue); + + /** + * Returns and removes the double value of a header with the specified name. If there are more than one values for + * the specified name, the first value is returned. + * + * @param name the name of the header to search + * @return the first header value if the header is found and its value is a double. {@code null} if there's no such + * header or its value is not a double. + */ + Double getDoubleAndRemove(T name); + + /** + * Returns and removes the double value of a header with the specified name. If there are more than one values for + * the specified name, the first value is returned. + * + * @param name the name of the header to search + * @param defaultValue the default value + * @return the first header value if the header is found and its value is a double. {@code defaultValue} if there's + * no such header or its value is not a double. + */ + double getDoubleAndRemove(T name, double defaultValue); + + /** + * Returns and removes the date value of a header with the specified name as milliseconds. If there are more than + * one values for the specified name, the first value is returned. + * + * @param name The name of the header to search + * @return the first header value in milliseconds if the header is found and its value is a date. {@code null} if + * there's no such header or its value is not a date. + */ + Long getTimeMillisAndRemove(T name); + + /** + * Returns and removes the date value of a header with the specified name as milliseconds. If there are more than + * one values for the specified name, the first value is returned. + * + * @param name The name of the header to search + * @param defaultValue default value + * @return the first header value in milliseconds if the header is found and its value is a date. + * {@code defaultValue} if there's no such header or its value is not a date. + */ + long getTimeMillisAndRemove(T name, long defaultValue); + + /** + * Returns a new {@link List} that contains all headers in this object. Note that modifying the returned + * {@link List} will not affect the state of this object. If you intend to enumerate over the header entries only, + * use {@link #iterator()} instead, which has much less overhead. + */ + List> entries(); + + /** + * Returns {@code true} if and only if this collection contains the header with the specified name. + * + * @param name The name of the header to search for + * @return {@code true} if at least one header is found + */ + boolean contains(T name); + + /** + * Returns {@code true} if a header with the name and value exists. + * + * @param name the header name + * @param value the header value + * @return {@code true} if it contains it {@code false} otherwise + */ + boolean contains(T name, T value); + + /** + * Returns {@code true} if a header with the name and value exists. + * + * @param name the header name + * @param value the header value + * @return {@code true} if it contains it {@code false} otherwise + */ + boolean containsObject(T name, Object value); + + /** + * Returns {@code true} if a header with the name and value exists. + * + * @param name the header name + * @param value the header value + * @return {@code true} if it contains it {@code false} otherwise + */ + boolean containsBoolean(T name, int value); + + /** + * Returns {@code true} if a header with the name and value exists. + * + * @param name the header name + * @param value the header value + * @return {@code true} if it contains it {@code false} otherwise + */ + boolean containsByte(T name, byte value); + + /** + * Returns {@code true} if a header with the name and value exists. + * + * @param name the header name + * @param value the header value + * @return {@code true} if it contains it {@code false} otherwise + */ + boolean containsChar(T name, char value); + + /** + * Returns {@code true} if a header with the name and value exists. + * + * @param name the header name + * @param value the header value + * @return {@code true} if it contains it {@code false} otherwise + */ + boolean containsShort(T name, byte value); + + /** + * Returns {@code true} if a header with the name and value exists. + * + * @param name the header name + * @param value the header value + * @return {@code true} if it contains it {@code false} otherwise + */ + boolean containsInt(T name, int value); + + /** + * Returns {@code true} if a header with the name and value exists. + * + * @param name the header name + * @param value the header value + * @return {@code true} if it contains it {@code false} otherwise + */ + boolean containsLong(T name, long value); + + /** + * Returns {@code true} if a header with the name and value exists. + * + * @param name the header name + * @param value the header value + * @return {@code true} if it contains it {@code false} otherwise + */ + boolean containsFloat(T name, float value); + + /** + * Returns {@code true} if a header with the name and value exists. + * + * @param name the header name + * @param value the header value + * @return {@code true} if it contains it {@code false} otherwise + */ + boolean containsDouble(T name, double value); + + /** + * Returns {@code true} if a header with the name and value exists. + * + * @param name the header name + * @param value the header value + * @param comparator The comparator to use when comparing {@code name} and {@code value} to entries in this map + * @return {@code true} if it contains it {@code false} otherwise + */ + boolean contains(T name, T value, Comparator comparator); + + /** + * Returns {@code true} if a header with the name and value exists. + * + * @param name the header name + * @param value the header value + * @param keyComparator The comparator to use when comparing {@code name} to names in this map + * @param valueComparator The comparator to use when comparing {@code value} to values in this map + * @return {@code true} if it contains it {@code false} otherwise + */ + boolean contains(T name, T value, Comparator keyComparator, Comparator valueComparator); + + /** + * Returns {@code true} if a header with the name and value exists. + * + * @param name the header name + * @param value the header value + * @param comparator The comparator to use when comparing {@code name} and {@code value} to entries in this map + * @return {@code true} if it contains it {@code false} otherwise + */ + boolean containsObject(T name, Object value, Comparator comparator); + + /** + * Returns {@code true} if a header with the name and value exists. + * + * @param name the header name + * @param value the header value + * @param keyComparator The comparator to use when comparing {@code name} to names in this map + * @param valueComparator The comparator to use when comparing {@code value} to values in this map + * @return {@code true} if it contains it {@code false} otherwise + */ + boolean containsObject(T name, Object value, Comparator keyComparator, + Comparator valueComparator); + + /** + * Returns the number of header entries in this collection. + */ + int size(); + + /** + * Returns {@code true} if and only if this collection contains no header entries. + */ + boolean isEmpty(); + + /** + * Returns a new {@link Set} that contains the names of all headers in this object. Note that modifying the returned + * {@link Set} will not affect the state of this object. If you intend to enumerate over the header entries only, + * use {@link #iterator()} instead, which has much less overhead. + */ + Set names(); + + /** + * Returns a new {@link List} that contains the names of all headers in this object. Note that modifying the + * returned {@link List} will not affect the state of this object. If you intend to enumerate over the header + * entries only, use {@link #iterator()} instead, which has much less overhead. + */ + List namesList(); + + /** + * Adds a new header with the specified name and value. If the specified value is not a {@link String}, it is + * converted into a {@link String} by {@link Object#toString()}, except in the cases of {@link java.util.Date} and + * {@link java.util.Calendar}, which are formatted to the date format defined in RFC2616. + * + * @param name the name of the header being added + * @param value the value of the header being added + * @return {@code this} + */ + Headers add(T name, T value); + + /** + * Adds a new header with the specified name and values. This getMethod can be represented approximately as the + * following code: + * + *
+     * for (Object v : values) {
+     *     if (v == null) {
+     *         break;
+     *     }
+     *     headers.add(name, v);
+     * }
+     * 
+ * + * @param name the name of the headepublic abstract rs being set + * @param values the values of the headers being set + * @return {@code this} + */ + Headers add(T name, Iterable values); + + /** + * Adds a new header with the specified name and values. This getMethod can be represented approximately as the + * following code: + * + *
+     * for (Object v : values) {
+     *     if (v == null) {
+     *         break;
+     *     }
+     *     headers.add(name, v);
+     * }
+     * 
+ * + * @param name the name of the headepublic abstract rs being set + * @param values the values of the headers being set + * @return {@code this} + */ + Headers add(T name, T... values); + + /** + * Adds a new header with the specified name and value. If the specified value is not a {@link String}, it is + * converted into a {@link String} by {@link Object#toString()}, except in the cases of {@link java.util.Date} and + * {@link java.util.Calendar}, which are formatted to the date format defined in RFC2616. + * + * @param name the name of the header being added + * @param value the value of the header being added + * @return {@code this} + */ + Headers addObject(T name, Object value); + + /** + * Adds a new header with the specified name and values. This getMethod can be represented approximately as the + * following code: + * + *
+     * for (Object v : values) {
+     *     if (v == null) {
+     *         break;
+     *     }
+     *     headers.add(name, v);
+     * }
+     * 
+ * + * @param name the name of the headepublic abstract rs being set + * @param values the values of the headers being set + * @return {@code this} + */ + Headers addObject(T name, Iterable values); + + /** + * Adds a new header with the specified name and values. This getMethod can be represented approximately as the + * following code: + * + *
+     * for (Object v : values) {
+     *     if (v == null) {
+     *         break;
+     *     }
+     *     headers.add(name, v);
+     * }
+     * 
+ * + * @param name the name of the headepublic abstract rs being set + * @param values the values of the headers being set + * @return {@code this} + */ + Headers addObject(T name, Object... values); + + /** + * Add the {@code name} to {@code value}. + * @param name The name to modify + * @param value The value + * @return {@code this} + */ + Headers addBoolean(T name, boolean value); + + /** + * Add the {@code name} to {@code value}. + * @param name The name to modify + * @param value The value + * @return {@code this} + */ + Headers addByte(T name, byte value); + + /** + * Add the {@code name} to {@code value}. + * @param name The name to modify + * @param value The value + * @return {@code this} + */ + Headers addChar(T name, char value); + + /** + * Add the {@code name} to {@code value}. + * @param name The name to modify + * @param value The value + * @return {@code this} + */ + Headers addShort(T name, short value); + + /** + * Add the {@code name} to {@code value}. + * @param name The name to modify + * @param value The value + * @return {@code this} + */ + Headers addInt(T name, int value); + + /** + * Add the {@code name} to {@code value}. + * @param name The name to modify + * @param value The value + * @return {@code this} + */ + Headers addLong(T name, long value); + + /** + * Add the {@code name} to {@code value}. + * @param name The name to modify + * @param value The value + * @return {@code this} + */ + Headers addFloat(T name, float value); + + /** + * Add the {@code name} to {@code value}. + * @param name The name to modify + * @param value The value + * @return {@code this} + */ + Headers addDouble(T name, double value); + + /** + * Adds all header entries of the specified {@code headers}. + * + * @return {@code this} + */ + Headers add(Headers headers); + + /** + * Sets a header with the specified name and value. If there is an existing header with the same name, it is + * removed. If the specified value is not a {@link String}, it is converted into a {@link String} by + * {@link Object#toString()}, except for {@link java.util.Date} and {@link java.util.Calendar}, which are formatted + * to the date format defined in RFC2616. + * + * @param name The name of the header being set + * @param value The value of the header being set + * @return {@code this} + */ + Headers set(T name, T value); + + /** + * Sets a header with the specified name and values. If there is an existing header with the same name, it is + * removed. This getMethod can be represented approximately as the following code: + * + *
+     * headers.remove(name);
+     * for (Object v : values) {
+     *     if (v == null) {
+     *         break;
+     *     }
+     *     headers.add(name, v);
+     * }
+     * 
+ * + * @param name the name of the headers being set + * @param values the values of the headers being set + * @return {@code this} + */ + Headers set(T name, Iterable values); + + /** + * Sets a header with the specified name and values. If there is an existing header with the same name, it is + * removed. This getMethod can be represented approximately as the following code: + * + *
+     * headers.remove(name);
+     * for (Object v : values) {
+     *     if (v == null) {
+     *         break;
+     *     }
+     *     headers.add(name, v);
+     * }
+     * 
+ * + * @param name the name of the headers being set + * @param values the values of the headers being set + * @return {@code this} + */ + Headers set(T name, T... values); + + /** + * Sets a header with the specified name and value. If there is an existing header with the same name, it is + * removed. If the specified value is not a {@link String}, it is converted into a {@link String} by + * {@link Object#toString()}, except for {@link java.util.Date} and {@link java.util.Calendar}, which are formatted + * to the date format defined in RFC2616. + * + * @param name The name of the header being set + * @param value The value of the header being set + * @return {@code this} + */ + Headers setObject(T name, Object value); + + /** + * Sets a header with the specified name and values. If there is an existing header with the same name, it is + * removed. This getMethod can be represented approximately as the following code: + * + *
+     * headers.remove(name);
+     * for (Object v : values) {
+     *     if (v == null) {
+     *         break;
+     *     }
+     *     headers.add(name, v);
+     * }
+     * 
+ * + * @param name the name of the headers being set + * @param values the values of the headers being set + * @return {@code this} + */ + Headers setObject(T name, Iterable values); + + /** + * Sets a header with the specified name and values. If there is an existing header with the same name, it is + * removed. This getMethod can be represented approximately as the following code: + * + *
+     * headers.remove(name);
+     * for (Object v : values) {
+     *     if (v == null) {
+     *         break;
+     *     }
+     *     headers.add(name, v);
+     * }
+     * 
+ * + * @param name the name of the headers being set + * @param values the values of the headers being set + * @return {@code this} + */ + Headers setObject(T name, Object... values); + + /** + * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. + * @param name The name to modify + * @param value The value + * @return {@code this} + */ + Headers setBoolean(T name, boolean value); + + /** + * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. + * @param name The name to modify + * @param value The value + * @return {@code this} + */ + Headers setByte(T name, byte value); + + /** + * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. + * @param name The name to modify + * @param value The value + * @return {@code this} + */ + Headers setChar(T name, char value); + + /** + * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. + * @param name The name to modify + * @param value The value + * @return {@code this} + */ + Headers setShort(T name, short value); + + /** + * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. + * @param name The name to modify + * @param value The value + * @return {@code this} + */ + Headers setInt(T name, int value); + + /** + * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. + * @param name The name to modify + * @param value The value + * @return {@code this} + */ + Headers setLong(T name, long value); + + /** + * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. + * @param name The name to modify + * @param value The value + * @return {@code this} + */ + Headers setFloat(T name, float value); + + /** + * Set the {@code name} to {@code value}. This will remove all previous values associated with {@code name}. + * @param name The name to modify + * @param value The value + * @return {@code this} + */ + Headers setDouble(T name, double value); + + /** + * Cleans the current header entries and copies all header entries of the specified {@code headers}. + * + * @return {@code this} + */ + Headers set(Headers headers); + + /** + * Retains all current headers but calls {@link #set(AsciiString, Object)} for each entry in {@code headers} + * + * @param headers The headers used to {@link #set(AsciiString, Object)} values in this instance + * @return {@code this} + */ + Headers setAll(Headers headers); + + /** + * Removes the header with the specified name. + * + * @param name The name of the header to remove + * @return {@code true} if and only if at least one entry has been removed + */ + boolean remove(T name); + + /** + * Removes all headers. + * + * @return {@code this} + */ + Headers clear(); + + @Override + Iterator> iterator(); + + /** + * Provide a means of iterating over elements in this map with low GC + * + * @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 + * + * @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 + */ + T forEachName(NameVisitor visitor) throws Exception; +} diff --git a/codec/src/main/java/io/netty/handler/codec/TextHeaderProcessor.java b/codec/src/main/java/io/netty/handler/codec/TextHeaderProcessor.java deleted file mode 100644 index 43aa9df7a6..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/TextHeaderProcessor.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.codec; - -public interface TextHeaderProcessor { - boolean process(CharSequence name, CharSequence value) throws Exception; -} diff --git a/codec/src/main/java/io/netty/handler/codec/TextHeaders.java b/codec/src/main/java/io/netty/handler/codec/TextHeaders.java index c840544259..614843a6bd 100644 --- a/codec/src/main/java/io/netty/handler/codec/TextHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/TextHeaders.java @@ -16,451 +16,142 @@ package io.netty.handler.codec; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - /** - * A typical string multimap used by text protocols such as HTTP for the representation of arbitrary key-value data. - * One thing to note is that it uses {@link CharSequence} as its primary key and value type rather than {@link String}. - * When you invoke the operations that produce {@link String}s such as {@link #get(CharSequence)}, - * a {@link CharSequence} is implicitly converted to a {@link String}. This is particularly useful for speed - * optimization because this multimap can hold a special {@link CharSequence} implementation that a codec can - * treat specially, such as {@link AsciiString}. + * A typical string multimap used by text protocols such as HTTP for the representation of arbitrary key-value data. One + * thing to note is that it uses {@link CharSequence} as its primary key and value type rather than {@link String}. When + * you invoke the operations that produce {@link String}s such as {@link #get(CharSequence)}, a {@link CharSequence} is + * implicitly converted to a {@link String}. This is particularly useful for speed optimization because this multimap + * can hold a special {@link CharSequence} implementation that a codec can treat specially, such as {@link CharSequence} + * . */ -public interface TextHeaders extends Iterable> { +public interface TextHeaders extends ConvertibleHeaders { /** - * Returns the value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name the name of the header to search - * @return the first header value if the header is found. - * {@code null} if there's no such header. + * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}. */ - String get(CharSequence name); + public interface EntryVisitor extends Headers.EntryVisitor { + } /** - * Returns the value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name the name of the header to search - * @param defaultValue the default value - * @return the first header value if the header is found. - * {@code defaultValue} if there's no such header. + * A visitor that helps reduce GC pressure while iterating over a collection of {@link Headers}. */ - String get(CharSequence name, String defaultValue); + public interface NameVisitor extends Headers.NameVisitor { + } /** - * Returns the integer value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name the name of the header to search - * @return the first header value if the header is found and its value is an integer. - * {@code null} if there's no such header or its value is not an integer. + * Returns {@code true} if a header with the name and value exists. + * @param name the header name + * @param value the header value + * @return {@code true} if it contains it {@code false} otherwise */ - Integer getInt(CharSequence name); + boolean contains(CharSequence name, CharSequence value, boolean ignoreCase); /** - * Returns the integer value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name the name of the header to search - * @param defaultValue the default value - * @return the first header value if the header is found and its value is an integer. - * {@code defaultValue} if there's no such header or its value is not an integer. + * Returns {@code true} if a header with the name and value exists. + * @param name the header name + * @param value the header value + * @return {@code true} if it contains it {@code false} otherwise */ - int getInt(CharSequence name, int defaultValue); + boolean containsObject(CharSequence name, Object value, boolean ignoreCase); + + @Override + TextHeaders add(CharSequence name, CharSequence value); + + @Override + TextHeaders add(CharSequence name, Iterable values); + + @Override + TextHeaders add(CharSequence name, CharSequence... values); + + @Override + TextHeaders addObject(CharSequence name, Object value); + + @Override + TextHeaders addObject(CharSequence name, Iterable values); + + @Override + TextHeaders addObject(CharSequence name, Object... values); + + @Override + TextHeaders addBoolean(CharSequence name, boolean value); + + @Override + TextHeaders addByte(CharSequence name, byte value); + + @Override + TextHeaders addChar(CharSequence name, char value); + + @Override + TextHeaders addShort(CharSequence name, short value); + + @Override + TextHeaders addInt(CharSequence name, int value); + + @Override + TextHeaders addLong(CharSequence name, long value); + + @Override + TextHeaders addFloat(CharSequence name, float value); + + @Override + TextHeaders addDouble(CharSequence name, double value); /** - * Returns the long integer value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name the name of the header to search - * @return the first header value if the header is found and its value is a long integer. - * {@code null} if there's no such header or its value is not a long integer. - */ - Long getLong(CharSequence name); - - /** - * Returns the long integer value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name the name of the header to search - * @param defaultValue the default value - * @return the first header value if the header is found and its value is a long integer. - * {@code defaultValue} if there's no such header or its value is not a long integer. - */ - long getLong(CharSequence name, long defaultValue); - - /** - * Returns the date value of a header with the specified name as milliseconds. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name The name of the header to search - * @return the first header value in milliseconds if the header is found and its value is a date. - * {@code null} if there's no such header or its value is not a date. - */ - Long getTimeMillis(CharSequence name); - - /** - * Returns the date value of a header with the specified name as milliseconds. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name The name of the header to search - * @param defaultValue default value - * @return the first header value in milliseconds if the header is found and its value is a date. - * {@code defaultValue} if there's no such header or its value is not a date. - */ - long getTimeMillis(CharSequence name, long defaultValue); - - /** - * Returns and removes the value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name the name of the header to search - * @return the first header value or {@code null} if there is no such header - */ - String getAndRemove(CharSequence name); - - /** - * Returns and removes the value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name the name of the header to search - * @param defaultValue the default value - * @return the first header value or {@code defaultValue} if there is no such header - */ - String getAndRemove(CharSequence name, String defaultValue); - - /** - * Returns and removes the integer value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name the name of the header to search - * @return the first header value if the header is found and its value is an integer. - * {@code null} if there's no such header or its value is not an integer. - */ - Integer getIntAndRemove(CharSequence name); - - /** - * Returns and removes the integer value of a header with the specified name. If there are more than one values for - * the specified name, the first value is returned. - * - * @param name the name of the header to search - * @param defaultValue the default value - * @return the first header value if the header is found and its value is an integer. - * {@code defaultValue} if there is no such header or its value of header is not an integer. - */ - int getIntAndRemove(CharSequence name, int defaultValue); - - /** - * Returns and removes the long value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name the name of the header to search - * @return the first header value if the header is found and its value is an integer. - * {@code null} if there's no such header or its value is not an integer. - */ - Long getLongAndRemove(CharSequence name); - - /** - * Returns and removes the long value of a header with the specified name. If there are more than one values for - * the specified name, the first value is returned. - * - * @param name the name of the header to search - * @param defaultValue the default value - * @return the first header value if the header is found and its value is an integer. - * {@code defaultValue} if there's no such header or its value is not an integer. - */ - long getLongAndRemove(CharSequence name, long defaultValue); - - /** - * Returns and removes the date value of a header with the specified name as milliseconds. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name The name of the header to search - * @return the first header value in milliseconds if the header is found and its value is a date. - * {@code null} if there's no such header or its value is not a date. - */ - Long getTimeMillisAndRemove(CharSequence name); - - /** - * Returns and removes the date value of a header with the specified name as milliseconds. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name The name of the header to search - * @param defaultValue default value - * @return the first header value in milliseconds if the header is found and its value is a date. - * {@code defaultValue} if there's no such header or its value is not a date. - */ - long getTimeMillisAndRemove(CharSequence name, long defaultValue); - - /** - * Returns the value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name The name of the header to search - * @return The first header value or {@code null} if there is no such header - */ - CharSequence getUnconverted(CharSequence name); - - /** - * Returns and Removes the value of a header with the specified name. If there are - * more than one values for the specified name, the first value is returned. - * - * @param name The name of the header to search - * @return The first header value or {@code null} if there is no such header - */ - CharSequence getUnconvertedAndRemove(CharSequence name); - - /** - * Returns the values of headers with the specified name - * - * @param name The name of the headers to search - * @return A {@link List} of header values which will be empty if no values are found - */ - List getAll(CharSequence name); - - /** - * Returns the values of headers with the specified name - * - * @param name The name of the headers to search - * @return A {@link List} of header values which will be empty if no values are found - */ - List getAllUnconverted(CharSequence name); - - /** - * Returns and Removes the values of headers with the specified name - * - * @param name The name of the headers to search - * @return A {@link List} of header values which will be empty if no values are found - */ - List getAllAndRemove(CharSequence name); - - /** - * Returns and Removes the values of headers with the specified name - * - * @param name The name of the headers to search - * @return A {@link List} of header values which will be empty if no values are found - */ - List getAllUnconvertedAndRemove(CharSequence name); - - /** - * Returns a new {@link List} that contains all headers in this object. Note that modifying the - * returned {@link List} will not affect the state of this object. If you intend to enumerate over the header - * entries only, use {@link #iterator()} instead, which has much less overhead. - */ - List> entries(); - - /** - * Returns a new {@link List} that contains all headers in this object. Note that modifying the - * returned {@link List} will not affect the state of this object. If you intend to enumerate over the header - * entries only, use {@link #iterator()} instead, which has much less overhead. - */ - List> unconvertedEntries(); - - /** - * Returns {@code true} if and only if this collection contains the header with the specified name. - * - * @param name The name of the header to search for - * @return {@code true} if at least one header is found - */ - boolean contains(CharSequence name); - - /** - * Returns the number of header entries in this collection. - */ - int size(); - - /** - * Returns {@code true} if and only if this collection contains no header entries. - */ - boolean isEmpty(); - - /** - * Returns a new {@link Set} that contains the names of all headers in this object. Note that modifying the - * returned {@link Set} will not affect the state of this object. If you intend to enumerate over the header - * entries only, use {@link #iterator()} instead, which has much less overhead. - */ - Set names(); - - /** - * Returns a new {@link Set} that contains the names of all headers in this object. Note that modifying the - * returned {@link Set} will not affect the state of this object. If you intend to enumerate over the header - * entries only, use {@link #iterator()} instead, which has much less overhead. - */ - Set unconvertedNames(); - - /** - * Adds a new header with the specified name and value. - * - * If the specified value is not a {@link String}, it is converted - * into a {@link String} by {@link Object#toString()}, except in the cases - * of {@link java.util.Date} and {@link java.util.Calendar}, which are formatted to the date - * format defined in RFC2616. - * - * @param name the name of the header being added - * @param value the value of the header being added - * - * @return {@code this} - */ - TextHeaders add(CharSequence name, Object value); - - /** - * Adds a new header with the specified name and values. - * - * This getMethod can be represented approximately as the following code: - *
-     * for (Object v: values) {
-     *     if (v == null) {
-     *         break;
-     *     }
-     *     headers.add(name, v);
-     * }
-     * 
- * - * @param name the name of the headepublic abstract rs being set - * @param values the values of the headers being set - * @return {@code this} - */ - TextHeaders add(CharSequence name, Iterable values); - - /** - * Adds a new header with the specified name and values. - * - * This getMethod can be represented approximately as the following code: - *
-     * for (Object v: values) {
-     *     if (v == null) {
-     *         break;
-     *     }
-     *     headers.add(name, v);
-     * }
-     * 
- * - * @param name the name of the headepublic abstract rs being set - * @param values the values of the headers being set - * @return {@code this} - */ - TextHeaders add(CharSequence name, Object... values); - - /** - * Adds all header entries of the specified {@code headers}. - * - * @return {@code this} + * See {@link Headers#add(Headers)} */ TextHeaders add(TextHeaders headers); - /** - * Sets a header with the specified name and value. - * - * If there is an existing header with the same name, it is removed. - * If the specified value is not a {@link String}, it is converted into a - * {@link String} by {@link Object#toString()}, except for {@link java.util.Date} - * and {@link java.util.Calendar}, which are formatted to the date format defined in - * RFC2616. - * - * @param name The name of the header being set - * @param value The value of the header being set - * @return {@code this} - */ - TextHeaders set(CharSequence name, Object value); + @Override + TextHeaders set(CharSequence name, CharSequence value); + + @Override + TextHeaders set(CharSequence name, Iterable values); + + @Override + TextHeaders set(CharSequence name, CharSequence... values); + + @Override + TextHeaders setObject(CharSequence name, Object value); + + @Override + TextHeaders setObject(CharSequence name, Iterable values); + + @Override + TextHeaders setObject(CharSequence name, Object... values); + + @Override + TextHeaders setBoolean(CharSequence name, boolean value); + + @Override + TextHeaders setByte(CharSequence name, byte value); + + @Override + TextHeaders setChar(CharSequence name, char value); + + @Override + TextHeaders setShort(CharSequence name, short value); + + @Override + TextHeaders setInt(CharSequence name, int value); + + @Override + TextHeaders setLong(CharSequence name, long value); + + @Override + TextHeaders setFloat(CharSequence name, float value); + + @Override + TextHeaders setDouble(CharSequence name, double value); /** - * Sets a header with the specified name and values. - * - * If there is an existing header with the same name, it is removed. - * This getMethod can be represented approximately as the following code: - *
-     * headers.remove(name);
-     * for (Object v: values) {
-     *     if (v == null) {
-     *         break;
-     *     }
-     *     headers.add(name, v);
-     * }
-     * 
- * - * @param name the name of the headers being set - * @param values the values of the headers being set - * @return {@code this} - */ - TextHeaders set(CharSequence name, Iterable values); - - /** - * Sets a header with the specified name and values. - * - * If there is an existing header with the same name, it is removed. - * This getMethod can be represented approximately as the following code: - *
-     * headers.remove(name);
-     * for (Object v: values) {
-     *     if (v == null) {
-     *         break;
-     *     }
-     *     headers.add(name, v);
-     * }
-     * 
- * - * @param name the name of the headers being set - * @param values the values of the headers being set - * @return {@code this} - */ - TextHeaders set(CharSequence name, Object... values); - - /** - * Cleans the current header entries and copies all header entries of the specified {@code headers}. - * - * @return {@code this} + * See {@link Headers#set(Headers)} */ TextHeaders set(TextHeaders headers); /** - * Retains all current headers but calls {@link #set(CharSequence, Object)} for each entry in {@code headers} - * @param headers The headers used to {@link #set(CharSequence, Object)} values in this instance - * @return {@code this} + * See {@link Headers#setAll(Headers)} */ TextHeaders setAll(TextHeaders headers); - /** - * Removes the header with the specified name. - * - * @param name The name of the header to remove - * @return {@code true} if and only if at least one entry has been removed - */ - boolean remove(CharSequence name); - - /** - * Removes all headers. - * - * @return {@code this} - */ - TextHeaders clear(); - - /** - * Returns {@code true} if a header with the name and value exists. - * - * @param name the header name - * @param value the header value - * @return {@code true} if it contains it {@code false} otherwise - */ - boolean contains(CharSequence name, Object value); - - /** - * Returns {@code true} if a header with the name and value exists. - * - * @param name the header name - * @param value the header value - * @return {@code true} if it contains it {@code false} otherwise - */ - boolean contains(CharSequence name, Object value, boolean ignoreCase); - @Override - Iterator> iterator(); - - Iterator> unconvertedIterator(); - - TextHeaders forEachEntry(TextHeaderProcessor processor); + TextHeaders clear(); } diff --git a/codec/src/test/java/io/netty/handler/codec/AsciiStringTest.java b/codec/src/test/java/io/netty/handler/codec/AsciiStringTest.java new file mode 100644 index 0000000000..52fbcd999a --- /dev/null +++ b/codec/src/test/java/io/netty/handler/codec/AsciiStringTest.java @@ -0,0 +1,88 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec; + +import 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.Test; + +/** + * Test for the {@link AsciiString} class + */ +public class AsciiStringTest { + + @Test + public void testGetBytesStringBuilder() { + final StringBuilder b = new StringBuilder(); + for (int i = 0; i < 1 << 16; ++i) { + b.append("eéaà"); + } + final String bString = b.toString(); + 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); + assertArrayEquals("failure for " + charset, expected, actual); + } + } + + @Test + public void testGetBytesString() { + final StringBuilder b = new StringBuilder(); + for (int i = 0; i < 1 << 16; ++i) { + b.append("eéaà"); + } + final String bString = b.toString(); + final Charset[] charsets = CharsetUtil.values(); + for (int i = 0; i < charsets.length; ++i) { + final Charset charset = charsets[i]; + byte[] expected = bString.getBytes(charset); + byte[] actual = AsciiString.getBytes(bString, charset); + assertArrayEquals("failure for " + charset, expected, actual); + } + } + + @Test + public void testGetBytesAsciiString() { + final StringBuilder b = new StringBuilder(); + for (int i = 0; i < 1 << 16; ++i) { + b.append("eéaà"); + } + 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); + } + } + + 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/codec/src/test/java/io/netty/handler/codec/HeaderMapTest.java b/codec/src/test/java/io/netty/handler/codec/DefaultBinaryHeadersTest.java similarity index 62% rename from codec/src/test/java/io/netty/handler/codec/HeaderMapTest.java rename to codec/src/test/java/io/netty/handler/codec/DefaultBinaryHeadersTest.java index c6662ed379..6d477e07bc 100644 --- a/codec/src/test/java/io/netty/handler/codec/HeaderMapTest.java +++ b/codec/src/test/java/io/netty/handler/codec/DefaultBinaryHeadersTest.java @@ -29,9 +29,9 @@ import java.util.Set; import org.junit.Test; /** - * Tests for {@link HeaderMap}. + * Tests for {@link DefaultBinaryHeaders}. */ -public class HeaderMapTest { +public class DefaultBinaryHeadersTest { @Test public void binaryHeadersWithSameValuesShouldBeEquivalent() { @@ -40,11 +40,11 @@ public class HeaderMapTest { byte[] key2 = randomBytes(); byte[] value2 = randomBytes(); - HeaderMap h1 = new HeaderMap(false); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false); h1.set(as(key1), as(value1)); h1.set(as(key2), as(value2)); - HeaderMap h2 = new HeaderMap(false); + DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false); h2.set(as(key1), as(value1)); h2.set(as(key2), as(value2)); @@ -63,13 +63,13 @@ public class HeaderMapTest { byte[] v3 = randomBytes(); byte[] v4 = randomBytes(); - HeaderMap h1 = new HeaderMap(false); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false); h1.set(as(k1), as(v1)); h1.set(as(k2), as(v2)); h1.add(as(k2), as(v3)); h1.add(as(k1), as(v4)); - HeaderMap h2 = new HeaderMap(false); + DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false); h2.set(as(k1), as(v1)); h2.set(as(k2), as(v2)); h2.add(as(k1), as(v4)); @@ -90,13 +90,13 @@ public class HeaderMapTest { byte[] v3 = randomBytes(); byte[] v4 = randomBytes(); - HeaderMap h1 = new HeaderMap(false); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false); h1.set(as(k1), as(v1)); h1.set(as(k2), as(v2)); h1.add(as(k2), as(v3)); h1.add(as(k1), as(v4)); - HeaderMap h2 = new HeaderMap(false); + DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false); h2.set(as(k1), as(v1)); h2.set(as(k2), as(v2)); h2.add(as(k1), as(v4)); @@ -116,18 +116,18 @@ public class HeaderMapTest { byte[] v3 = randomBytes(); byte[] v4 = randomBytes(); - HeaderMap h1 = new HeaderMap(false); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false); h1.set(as(k1), as(v1)); h1.set(as(k2), as(v2)); h1.add(as(k2), as(v3)); h1.add(as(k1), as(v4)); - HeaderMap h2 = new HeaderMap(false); + DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(false); h2.set(as(k1), as(v1)); h2.set(as(k2), as(v2)); h2.add(as(k1), as(v4)); - HeaderMap expected = new HeaderMap(false); + DefaultBinaryHeaders expected = new DefaultBinaryHeaders(false); expected.set(as(k1), as(v1)); expected.set(as(k2), as(v2)); expected.add(as(k2), as(v3)); @@ -148,27 +148,27 @@ public class HeaderMapTest { byte[] v2 = randomBytes(); byte[] v3 = randomBytes(); - HeaderMap h1 = new HeaderMap(false); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(false); 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)); } @Test public void headersWithSameValuesShouldBeEquivalent() { - HeaderMap h1 = new HeaderMap(); - h1.set("foo", "goo"); - h1.set("foo2", "goo2"); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); + h1.set(as("foo"), as("goo")); + h1.set(as("foo2"), as("goo2")); - HeaderMap h2 = new HeaderMap(); - h2.set("foo", "goo"); - h2.set("foo2", "goo2"); + DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(); + h2.set(as("foo"), as("goo")); + h2.set(as("foo2"), as("goo2")); assertTrue(h1.equals(h2)); assertTrue(h2.equals(h1)); @@ -178,17 +178,17 @@ public class HeaderMapTest { @Test public void headersWithSameDuplicateValuesShouldBeEquivalent() { - HeaderMap h1 = new HeaderMap(); - h1.set("foo", "goo"); - h1.set("foo2", "goo2"); - h1.add("foo2", "goo3"); - h1.add("foo", "goo4"); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); + h1.set(as("foo"), as("goo")); + h1.set(as("foo2"), as("goo2")); + h1.add(as("foo2"), as("goo3")); + h1.add(as("foo"), as("goo4")); - HeaderMap h2 = new HeaderMap(); - h2.set("foo", "goo"); - h2.set("foo2", "goo2"); - h2.add("foo", "goo4"); - h2.add("foo2", "goo3"); + DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(); + h2.set(as("foo"), as("goo")); + h2.set(as("foo2"), as("goo2")); + h2.add(as("foo"), as("goo4")); + h2.add(as("foo2"), as("goo3")); assertTrue(h1.equals(h2)); assertTrue(h2.equals(h1)); @@ -198,16 +198,16 @@ public class HeaderMapTest { @Test public void headersWithDifferentValuesShouldNotBeEquivalent() { - HeaderMap h1 = new HeaderMap(); - h1.set("foo", "goo"); - h1.set("foo2", "goo2"); - h1.add("foo2", "goo3"); - h1.add("foo", "goo4"); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); + h1.set(as("foo"), as("goo")); + h1.set(as("foo2"), as("goo2")); + h1.add(as("foo2"), as("goo3")); + h1.add(as("foo"), as("goo4")); - HeaderMap h2 = new HeaderMap(); - h2.set("foo", "goo"); - h2.set("foo2", "goo2"); - h2.add("foo", "goo4"); + DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(); + h2.set(as("foo"), as("goo")); + h2.set(as("foo2"), as("goo2")); + h2.add(as("foo"), as("goo4")); assertFalse(h1.equals(h2)); assertFalse(h2.equals(h1)); @@ -217,25 +217,25 @@ public class HeaderMapTest { @Test public void setAllShouldMergeHeaders() { - HeaderMap h1 = new HeaderMap(); - h1.set("foo", "goo"); - h1.set("foo2", "goo2"); - h1.add("foo2", "goo3"); - h1.add("foo", "goo4"); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); + h1.set(as("foo"), as("goo")); + h1.set(as("foo2"), as("goo2")); + h1.add(as("foo2"), as("goo3")); + h1.add(as("foo"), as("goo4")); - HeaderMap h2 = new HeaderMap(); - h2.set("foo", "goo"); - h2.set("foo2", "goo2"); - h2.add("foo", "goo4"); + DefaultBinaryHeaders h2 = new DefaultBinaryHeaders(); + h2.set(as("foo"), as("goo")); + h2.set(as("foo2"), as("goo2")); + h2.add(as("foo"), as("goo4")); - HeaderMap expected = new HeaderMap(); - expected.set("foo", "goo"); - expected.set("foo2", "goo2"); - expected.add("foo2", "goo3"); - expected.add("foo", "goo4"); - expected.set("foo", "goo"); - expected.set("foo2", "goo2"); - expected.set("foo", "goo4"); + DefaultBinaryHeaders expected = new DefaultBinaryHeaders(); + expected.set(as("foo"), as("goo")); + expected.set(as("foo2"), as("goo2")); + expected.add(as("foo2"), as("goo3")); + expected.add(as("foo"), as("goo4")); + expected.set(as("foo"), as("goo")); + expected.set(as("foo2"), as("goo2")); + expected.set(as("foo"), as("goo4")); h1.setAll(h2); @@ -244,22 +244,21 @@ public class HeaderMapTest { @Test public void setShouldReplacePreviousValues() { - HeaderMap h1 = new HeaderMap(); - h1.add("foo", "goo"); - h1.add("foo", "goo2"); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); + h1.add(as("foo"), as("goo")); + h1.add(as("foo"), as("goo2")); assertEquals(2, h1.size()); - h1.set("foo", "goo3"); + h1.set(as("foo"), as("goo3")); assertEquals(1, h1.size()); - List list = h1.getAll("foo"); + List list = h1.getAll(as("foo")); assertEquals(1, list.size()); - assertEquals("goo3", list.get(0)); + assertEquals(as("goo3"), list.get(0)); } @Test(expected = NoSuchElementException.class) public void iterateEmptyHeadersShouldThrow() { - Iterator> iterator = - new HeaderMap().iterator(); + Iterator> iterator = new DefaultBinaryHeaders().iterator(); assertFalse(iterator.hasNext()); iterator.next(); } @@ -275,16 +274,15 @@ public class HeaderMapTest { headers.add("c:1"); // Build the headers from the input set. - HeaderMap h1 = new HeaderMap(); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); for (String header : headers) { String[] parts = header.split(":"); - h1.add(parts[0], parts[1]); + h1.add(as(parts[0]), as(parts[1])); } // Now iterate through the headers, removing them from the original set. - for (Map.Entry entry : h1) { - assertTrue(headers - .remove(entry.getKey().toString() + ':' + entry.getValue().toString())); + for (Map.Entry entry : h1) { + assertTrue(headers.remove(entry.getKey().toString() + ':' + entry.getValue().toString())); } // Make sure we removed them all. @@ -293,12 +291,12 @@ public class HeaderMapTest { @Test public void getAndRemoveShouldReturnFirstEntry() { - HeaderMap h1 = new HeaderMap(); - h1.add("foo", "goo"); - h1.add("foo", "goo2"); - assertEquals("goo", h1.getAndRemove("foo")); + DefaultBinaryHeaders h1 = new DefaultBinaryHeaders(); + h1.add(as("foo"), as("goo")); + h1.add(as("foo"), as("goo2")); + assertEquals(as("goo"), h1.getAndRemove(as("foo"))); assertEquals(0, h1.size()); - List values = h1.getAll("foo"); + List values = h1.getAll(as("foo")); assertEquals(0, values.size()); } @@ -308,7 +306,11 @@ public class HeaderMapTest { return data; } - private String as(byte[] bytes) { - return new String(bytes); + private AsciiString as(byte[] bytes) { + return new AsciiString(bytes); + } + + private AsciiString as(String value) { + return new AsciiString(value); } } diff --git a/common/src/main/java/io/netty/util/CharsetUtil.java b/common/src/main/java/io/netty/util/CharsetUtil.java index 74c0a3dc91..7da00dd749 100644 --- a/common/src/main/java/io/netty/util/CharsetUtil.java +++ b/common/src/main/java/io/netty/util/CharsetUtil.java @@ -61,6 +61,11 @@ public final class CharsetUtil { */ public static final Charset US_ASCII = Charset.forName("US-ASCII"); + private static final Charset[] CHARSETS = new Charset[] + { UTF_16, UTF_16BE, UTF_16LE, UTF_8, ISO_8859_1, US_ASCII }; + + public static Charset[] values() { return CHARSETS; } + /** * Returns a cached thread-local {@link CharsetEncoder} for the specified * charset. @@ -111,7 +116,5 @@ public final class CharsetUtil { return d; } - private CharsetUtil() { - // Unused - } + private CharsetUtil() { } } diff --git a/common/src/main/java/io/netty/util/collection/CollectionUtils.java b/common/src/main/java/io/netty/util/collection/CollectionUtils.java new file mode 100644 index 0000000000..b9e11044c4 --- /dev/null +++ b/common/src/main/java/io/netty/util/collection/CollectionUtils.java @@ -0,0 +1,52 @@ +/* + * Copyright 2014 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.collection; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * Provides utilities for the primitive collection types that are not supplied by the JDK + */ +public final class CollectionUtils { + + private CollectionUtils() { } + + /** + * Compare two lists using the {@code comparator} for all comparisons (not using the equals() operator) + * @param lhs Left hand side + * @param rhs Right hand side + * @param comparator Comparator which will be used for all comparisons (equals() on objects will not be used) + * @return True if {@code lhs} == {@code rhs} according to {@code comparator}. False otherwise. + */ + public static boolean equals(List lhs, List rhs, Comparator comparator) { + final int lhsSize = lhs.size(); + if (lhsSize != rhs.size()) { + return false; + } + + // Don't use a TreeSet to do the comparison. We want to force the comparator + // to be used instead of the object's equals() + Collections.sort(lhs, comparator); + Collections.sort(rhs, comparator); + for (int i = 0; i < lhsSize; ++i) { + if (comparator.compare(lhs.get(i), rhs.get(i)) != 0) { + return false; + } + } + return true; + } +} diff --git a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java index e084d3e0bc..6f1286796a 100644 --- a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java +++ b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java @@ -150,7 +150,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler LastHttpContent trailer = (LastHttpContent) msg; if (!trailer.trailingHeaders().isEmpty()) { buf.append("\r\n"); - for (String name: trailer.trailingHeaders().names()) { - for (String value: trailer.trailingHeaders().getAll(name)) { + for (CharSequence name: trailer.trailingHeaders().names()) { + for (CharSequence value: trailer.trailingHeaders().getAll(name)) { buf.append("TRAILING HEADER: "); buf.append(name).append(" = ").append(value).append("\r\n"); } @@ -155,14 +155,14 @@ public class HttpSnoopServerHandler extends SimpleChannelInboundHandler if (keepAlive) { // Add 'Content-Length' header only for a keep-alive connection. - response.headers().set(CONTENT_LENGTH, response.content().readableBytes()); + response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes()); // Add keep alive header as per: // - http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01.html#Connection response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); } // Encode the cookie. - String cookieString = request.headers().get(COOKIE); + String cookieString = request.headers().getAndConvert(COOKIE); if (cookieString != null) { Set cookies = CookieDecoder.decode(cookieString); if (!cookies.isEmpty()) { diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadClient.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadClient.java index 4c69312b06..31d402bb1d 100644 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadClient.java +++ b/example/src/main/java/io/netty/example/http/upload/HttpUploadClient.java @@ -182,7 +182,7 @@ public final class HttpUploadClient { ); // send request - List> entries = headers.entries(); + List> entries = headers.entriesConverted(); channel.writeAndFlush(request); // Wait for the server to close the connection. diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java index 5a63f56794..2ac9b0a857 100644 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java +++ b/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java @@ -40,8 +40,8 @@ public class HttpUploadClientHandler extends SimpleChannelInboundHandler entry : request.headers()) { + for (Entry entry : request.headers()) { responseContent.append("HEADER: " + entry.getKey() + '=' + entry.getValue() + "\r\n"); } responseContent.append("\r\n\r\n"); // new getMethod Set cookies; - String value = request.headers().get(COOKIE); + String value = request.headers().getAndConvert(COOKIE); if (value == null) { cookies = Collections.emptySet(); } else { @@ -299,11 +299,11 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler cookies; - String value = request.headers().get(COOKIE); + String value = request.headers().getAndConvert(COOKIE); if (value == null) { cookies = Collections.emptySet(); } else { @@ -401,7 +401,7 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler ctx.writeAndFlush(subscribeFrame); break; case RECEIPT: - String receiptHeader = frame.headers().get(StompHeaders.RECEIPT_ID); + String receiptHeader = frame.headers().getAndConvert(StompHeaders.RECEIPT_ID); if (state == ClientState.AUTHENTICATED && receiptHeader.equals(subscrReceiptId)) { StompFrame msgFrame = new DefaultStompFrame(StompCommand.SEND); msgFrame.headers().set(StompHeaders.DESTINATION, StompClient.TOPIC); diff --git a/handler-proxy/src/test/java/io/netty/handler/proxy/HttpProxyServer.java b/handler-proxy/src/test/java/io/netty/handler/proxy/HttpProxyServer.java index 963a779e83..7cd96eb74c 100644 --- a/handler-proxy/src/test/java/io/netty/handler/proxy/HttpProxyServer.java +++ b/handler-proxy/src/test/java/io/netty/handler/proxy/HttpProxyServer.java @@ -86,7 +86,7 @@ final class HttpProxyServer extends ProxyServer { boolean authzSuccess = false; if (username != null) { - String authz = req.headers().get(Names.AUTHORIZATION); + CharSequence authz = req.headers().get(Names.AUTHORIZATION); if (authz != null) { ByteBuf authzBuf64 = Unpooled.copiedBuffer(authz, CharsetUtil.US_ASCII); ByteBuf authzBuf = Base64.decode(authzBuf64); @@ -113,7 +113,7 @@ final class HttpProxyServer extends ProxyServer { FullHttpResponse res; if (!authenticate(ctx, req)) { res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.UNAUTHORIZED); - res.headers().set(Names.CONTENT_LENGTH, 0); + res.headers().setInt(Names.CONTENT_LENGTH, 0); } else { res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); String uri = req.uri(); @@ -144,10 +144,10 @@ final class HttpProxyServer extends ProxyServer { if (!authenticate(ctx, req)) { res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.UNAUTHORIZED); - res.headers().set(Names.CONTENT_LENGTH, 0); + res.headers().setInt(Names.CONTENT_LENGTH, 0); } else if (!req.uri().equals(destination.getHostString() + ':' + destination.getPort())) { res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN); - res.headers().set(Names.CONTENT_LENGTH, 0); + res.headers().setInt(Names.CONTENT_LENGTH, 0); } else { res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); sendGreeting = true;