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;