From c076c3390145c1a71d481b44278850d3c11ec88b Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Sat, 14 Jun 2014 17:33:34 +0900 Subject: [PATCH] Backport the additional AsciiString/TextHeader changes from master - Add useful static methods to AsciiString - Add more getters in TextHeaders - Remove unnecessary utility methods in SpdyHttpHeaders --- .../codec/http/DefaultLastHttpContent.java | 7 +- .../codec/http/HttpContentCompressor.java | 3 +- .../netty/handler/codec/http/HttpHeaders.java | 47 ++----- .../handler/codec/http/HttpObjectDecoder.java | 7 +- .../multipart/HttpPostRequestEncoder.java | 3 +- .../WebSocketClientHandshaker00.java | 5 +- .../WebSocketClientHandshaker07.java | 5 +- .../WebSocketClientHandshaker08.java | 5 +- .../WebSocketClientHandshaker13.java | 5 +- .../WebSocketServerHandshaker00.java | 5 +- .../handler/codec/spdy/SpdyHttpDecoder.java | 14 +- .../handler/codec/spdy/SpdyHttpEncoder.java | 49 +++---- .../handler/codec/spdy/SpdyHttpHeaders.java | 127 +----------------- .../spdy/SpdyHttpResponseStreamIdHandler.java | 6 +- .../handler/codec/http/HttpHeadersTest.java | 9 +- .../io/netty/handler/codec/AsciiString.java | 59 +++++++- .../handler/codec/DefaultTextHeaders.java | 55 +++++++- .../netty/handler/codec/EmptyTextHeaders.java | 16 +++ .../io/netty/handler/codec/TextHeaders.java | 3 + .../client/SpdyClientStreamIdHandler.java | 4 +- 20 files changed, 220 insertions(+), 214 deletions(-) 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 523c98321f..8583bdfd1f 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 @@ -17,6 +17,7 @@ package io.netty.handler.codec.http; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.DefaultHttpHeaders.NonValidatingTextHeaders; import io.netty.handler.codec.http.DefaultHttpHeaders.ValidatingTextHeaders; import io.netty.util.internal.StringUtil; @@ -113,9 +114,9 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt @Override protected CharSequence convertName(CharSequence name) { name = super.convertName(name); - if (HttpHeaders.equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH, name) || - HttpHeaders.equalsIgnoreCase(HttpHeaders.Names.TRANSFER_ENCODING, name) || - HttpHeaders.equalsIgnoreCase(HttpHeaders.Names.TRAILER, name)) { + 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); } 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 e300881693..3b1bf315ff 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 @@ -16,6 +16,7 @@ package io.netty.handler.codec.http; import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.compression.ZlibCodecFactory; import io.netty.handler.codec.compression.ZlibWrapper; import io.netty.util.internal.StringUtil; @@ -96,7 +97,7 @@ public class HttpContentCompressor extends HttpContentEncoder { protected Result beginEncode(HttpResponse headers, String acceptEncoding) throws Exception { String contentEncoding = headers.headers().get(HttpHeaders.Names.CONTENT_ENCODING); if (contentEncoding != null && - !HttpHeaders.equalsIgnoreCase(HttpHeaders.Values.IDENTITY, contentEncoding)) { + !AsciiString.equalsIgnoreCase(HttpHeaders.Values.IDENTITY, contentEncoding)) { return null; } 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 1181f8cc42..b9331224e0 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 @@ -567,14 +567,14 @@ public abstract class HttpHeaders implements Iterable> */ public static boolean isKeepAlive(HttpMessage message) { String connection = message.headers().get(CONNECTION_ENTITY); - if (connection != null && equalsIgnoreCase(CLOSE_ENTITY, connection)) { + if (connection != null && AsciiString.equalsIgnoreCase(CLOSE_ENTITY, connection)) { return false; } if (message.getProtocolVersion().isKeepAliveDefault()) { - return !equalsIgnoreCase(CLOSE_ENTITY, connection); + return !AsciiString.equalsIgnoreCase(CLOSE_ENTITY, connection); } else { - return equalsIgnoreCase(KEEP_ALIVE_ENTITY, connection); + return AsciiString.equalsIgnoreCase(KEEP_ALIVE_ENTITY, connection); } } @@ -1115,7 +1115,7 @@ public abstract class HttpHeaders implements Iterable> if (value == null) { return false; } - if (equalsIgnoreCase(CONTINUE_ENTITY, value)) { + if (AsciiString.equalsIgnoreCase(CONTINUE_ENTITY, value)) { return true; } @@ -1165,7 +1165,7 @@ public abstract class HttpHeaders implements Iterable> Iterator valuesIt = values.iterator(); while (valuesIt.hasNext()) { String value = valuesIt.next(); - if (equalsIgnoreCase(value, CHUNKED_ENTITY)) { + if (AsciiString.equalsIgnoreCase(value, CHUNKED_ENTITY)) { valuesIt.remove(); } } @@ -1186,39 +1186,11 @@ public abstract class HttpHeaders implements Iterable> } /** - * Returns {@code true} if both {@link CharSequence}'s are equals when ignore the case. - * This only supports US_ASCII. + * @deprecated Use {@link AsciiString#equalsIgnoreCase(CharSequence, CharSequence)} instead. */ + @Deprecated public static boolean equalsIgnoreCase(CharSequence name1, CharSequence name2) { - if (name1 == name2) { - return true; - } - - if (name1 == null || name2 == null) { - return false; - } - - int nameLen = name1.length(); - if (nameLen != name2.length()) { - return false; - } - - for (int i = nameLen - 1; i >= 0; i --) { - char c1 = name1.charAt(i); - char c2 = name2.charAt(i); - if (c1 != c2) { - if (c1 >= 'A' && c1 <= 'Z') { - c1 += 32; - } - if (c2 >= 'A' && c2 <= 'Z') { - c2 += 32; - } - if (c1 != c2) { - return false; - } - } - } - return true; + return AsciiString.equalsIgnoreCase(name1, name2); } static void encode(HttpHeaders headers, ByteBuf buf) { @@ -1238,6 +1210,7 @@ public abstract class HttpHeaders implements Iterable> buf.writeBytes(CRLF); } + @Deprecated public static void encodeAscii(CharSequence seq, ByteBuf buf) { if (seq instanceof AsciiString) { ((AsciiString) seq).copy(0, buf, seq.length()); @@ -1499,7 +1472,7 @@ public abstract class HttpHeaders implements Iterable> for (String v: values) { if (ignoreCase) { - if (equalsIgnoreCase(v, value)) { + if (AsciiString.equalsIgnoreCase(v, value)) { return true; } } else { 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 e7b84c31b3..814698940c 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 @@ -20,6 +20,7 @@ import io.netty.buffer.ByteBufProcessor; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; +import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.TooLongFrameException; @@ -559,9 +560,9 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder { if (transferEncoding != null) { headers.remove(HttpHeaders.Names.TRANSFER_ENCODING); for (String v : transferEncoding) { - if (HttpHeaders.equalsIgnoreCase(v, HttpHeaders.Values.CHUNKED)) { + if (AsciiString.equalsIgnoreCase(v, HttpHeaders.Values.CHUNKED)) { // ignore } else { headers.add(HttpHeaders.Names.TRANSFER_ENCODING, v); 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 c472d528c1..278a8501a1 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java @@ -17,6 +17,7 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -198,13 +199,13 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker { HttpHeaders headers = response.headers(); String upgrade = headers.get(Names.UPGRADE); - if (!HttpHeaders.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { + if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); } String connection = headers.get(Names.CONNECTION); - if (!HttpHeaders.equalsIgnoreCase(Values.UPGRADE, 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 a5e334fa8a..779c6655e3 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 @@ -15,6 +15,7 @@ */ package io.netty.handler.codec.http.websocketx; +import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -173,12 +174,12 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker { } String upgrade = headers.get(Names.UPGRADE); - if (!HttpHeaders.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { + if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); } String connection = headers.get(Names.CONNECTION); - if (!HttpHeaders.equalsIgnoreCase(Values.UPGRADE, 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/WebSocketClientHandshaker08.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java index a56b829b2d..d49e757bdb 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 @@ -15,6 +15,7 @@ */ package io.netty.handler.codec.http.websocketx; +import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -174,12 +175,12 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker { } String upgrade = headers.get(Names.UPGRADE); - if (!HttpHeaders.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { + if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); } String connection = headers.get(Names.CONNECTION); - if (!HttpHeaders.equalsIgnoreCase(Values.UPGRADE, 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/WebSocketClientHandshaker13.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java index d23ebb3ec3..ab37b770bb 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 @@ -15,6 +15,7 @@ */ package io.netty.handler.codec.http.websocketx; +import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -184,12 +185,12 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { } String upgrade = headers.get(Names.UPGRADE); - if (!HttpHeaders.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { + if (!AsciiString.equalsIgnoreCase(Values.WEBSOCKET, upgrade)) { throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); } String connection = headers.get(Names.CONNECTION); - if (!HttpHeaders.equalsIgnoreCase(Values.UPGRADE, 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/WebSocketServerHandshaker00.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java index a32aa67277..6f30d248f3 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 @@ -20,6 +20,7 @@ import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelPromise; +import io.netty.handler.codec.AsciiString; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -109,8 +110,8 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { protected FullHttpResponse newHandshakeResponse(FullHttpRequest req, HttpHeaders headers) { // Serve the WebSocket handshake request. - if (!HttpHeaders.equalsIgnoreCase(Values.UPGRADE, req.headers().get(CONNECTION)) - || !HttpHeaders.equalsIgnoreCase(WEBSOCKET, req.headers().get(Names.UPGRADE))) { + if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, req.headers().get(CONNECTION)) + || !AsciiString.equalsIgnoreCase(WEBSOCKET, req.headers().get(Names.UPGRADE))) { throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade"); } 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 623740fdc1..7daca855d9 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 @@ -28,6 +28,7 @@ import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.spdy.SpdyHttpHeaders.Names; import java.util.HashMap; import java.util.List; @@ -138,10 +139,11 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder { createHttpResponse(spdyVersion, spdySynStreamFrame); // Set the Stream-ID, Associated-To-Stream-ID, Priority, and URL as headers - SpdyHttpHeaders.setStreamId(httpResponseWithEntity, streamId); - SpdyHttpHeaders.setAssociatedToStreamId(httpResponseWithEntity, associatedToStreamId); - SpdyHttpHeaders.setPriority(httpResponseWithEntity, spdySynStreamFrame.getPriority()); - SpdyHttpHeaders.setUrl(httpResponseWithEntity, URL); + HttpHeaders.setIntHeader(httpResponseWithEntity, Names.STREAM_ID, streamId); + HttpHeaders.setIntHeader( + httpResponseWithEntity, Names.ASSOCIATED_TO_STREAM_ID, associatedToStreamId); + HttpHeaders.setIntHeader(httpResponseWithEntity, Names.PRIORITY, spdySynStreamFrame.getPriority()); + httpResponseWithEntity.headers().set(Names.URL, URL); if (spdySynStreamFrame.isLast()) { HttpHeaders.setContentLength(httpResponseWithEntity, 0); @@ -174,7 +176,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder { FullHttpRequest httpRequestWithEntity = createHttpRequest(spdyVersion, spdySynStreamFrame); // Set the Stream-ID as a header - SpdyHttpHeaders.setStreamId(httpRequestWithEntity, streamId); + HttpHeaders.setIntHeader(httpRequestWithEntity, Names.STREAM_ID, streamId); if (spdySynStreamFrame.isLast()) { out.add(httpRequestWithEntity); @@ -213,7 +215,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder { FullHttpResponse httpResponseWithEntity = createHttpResponse(spdyVersion, spdySynReplyFrame); // Set the Stream-ID as a header - SpdyHttpHeaders.setStreamId(httpResponseWithEntity, streamId); + HttpHeaders.setIntHeader(httpResponseWithEntity, Names.STREAM_ID, streamId); if (spdySynReplyFrame.isLast()) { HttpHeaders.setContentLength(httpResponseWithEntity, 0); 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 3914de0419..c17fdb7071 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 @@ -27,6 +27,7 @@ import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.LastHttpContent; +import io.netty.handler.codec.spdy.SpdyHttpHeaders.Names; import java.util.List; import java.util.Map; @@ -207,23 +208,24 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { private SpdySynStreamFrame createSynStreamFrame(HttpMessage httpMessage) throws Exception { // Get the Stream-ID, Associated-To-Stream-ID, Priority, URL, and scheme from the headers - int streamID = SpdyHttpHeaders.getStreamId(httpMessage); - int associatedToStreamId = SpdyHttpHeaders.getAssociatedToStreamId(httpMessage); - byte priority = SpdyHttpHeaders.getPriority(httpMessage); - String URL = SpdyHttpHeaders.getUrl(httpMessage); - String scheme = SpdyHttpHeaders.getScheme(httpMessage); - SpdyHttpHeaders.removeStreamId(httpMessage); - SpdyHttpHeaders.removeAssociatedToStreamId(httpMessage); - SpdyHttpHeaders.removePriority(httpMessage); - SpdyHttpHeaders.removeUrl(httpMessage); - SpdyHttpHeaders.removeScheme(httpMessage); + final HttpHeaders httpHeaders = httpMessage.headers(); + int streamID = HttpHeaders.getIntHeader(httpMessage, Names.STREAM_ID); + int associatedToStreamId = HttpHeaders.getIntHeader(httpMessage, Names.ASSOCIATED_TO_STREAM_ID, 0); + byte priority = (byte) HttpHeaders.getIntHeader(httpMessage, Names.PRIORITY, 0); + String URL = httpHeaders.get(Names.URL); + String scheme = httpHeaders.get(Names.SCHEME); + httpHeaders.remove(Names.STREAM_ID); + httpHeaders.remove(Names.ASSOCIATED_TO_STREAM_ID); + httpHeaders.remove(Names.PRIORITY); + httpHeaders.remove(Names.URL); + httpHeaders.remove(Names.SCHEME); // The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding // headers are not valid and MUST not be sent. - httpMessage.headers().remove(HttpHeaders.Names.CONNECTION); - httpMessage.headers().remove("Keep-Alive"); - httpMessage.headers().remove("Proxy-Connection"); - httpMessage.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING); + httpHeaders.remove(HttpHeaders.Names.CONNECTION); + httpHeaders.remove("Keep-Alive"); + httpHeaders.remove("Proxy-Connection"); + httpHeaders.remove(HttpHeaders.Names.TRANSFER_ENCODING); SpdySynStreamFrame spdySynStreamFrame = new DefaultSpdySynStreamFrame(streamID, associatedToStreamId, priority); @@ -247,7 +249,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { // Replace the HTTP host header with the SPDY host header if (spdyVersion >= 3) { String host = HttpHeaders.getHost(httpMessage); - httpMessage.headers().remove(HttpHeaders.Names.HOST); + httpHeaders.remove(HttpHeaders.Names.HOST); frameHeaders.set(HOST, host); } @@ -258,7 +260,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { frameHeaders.set(SCHEME, scheme); // Transfer the remaining HTTP headers - for (Map.Entry entry: httpMessage.headers()) { + for (Map.Entry entry: httpHeaders) { frameHeaders.add(entry.getKey(), entry.getValue()); } currentStreamId = spdySynStreamFrame.getStreamId(); @@ -270,15 +272,16 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { private SpdySynReplyFrame createSynReplyFrame(HttpResponse httpResponse) throws Exception { // Get the Stream-ID from the headers - int streamID = SpdyHttpHeaders.getStreamId(httpResponse); - SpdyHttpHeaders.removeStreamId(httpResponse); + final HttpHeaders httpHeaders = httpResponse.headers(); + int streamID = HttpHeaders.getIntHeader(httpResponse, Names.STREAM_ID); + httpHeaders.remove(Names.STREAM_ID); // The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding // headers are not valid and MUST not be sent. - httpResponse.headers().remove(HttpHeaders.Names.CONNECTION); - httpResponse.headers().remove("Keep-Alive"); - httpResponse.headers().remove("Proxy-Connection"); - httpResponse.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING); + httpHeaders.remove(HttpHeaders.Names.CONNECTION); + httpHeaders.remove("Keep-Alive"); + httpHeaders.remove("Proxy-Connection"); + httpHeaders.remove(HttpHeaders.Names.TRANSFER_ENCODING); SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID); SpdyHeaders frameHeaders = spdySynReplyFrame.headers(); @@ -287,7 +290,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { frameHeaders.set(VERSION, httpResponse.getProtocolVersion()); // Transfer the remaining HTTP headers - for (Map.Entry entry: httpResponse.headers()) { + 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/SpdyHttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java index 0aa0de913d..e28a3be2a6 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java @@ -15,8 +15,7 @@ */ package io.netty.handler.codec.spdy; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMessage; +import io.netty.handler.codec.AsciiString; /** * Provides the constants for the header names and the utility methods @@ -31,138 +30,26 @@ public final class SpdyHttpHeaders { /** * {@code "X-SPDY-Stream-ID"} */ - public static final String STREAM_ID = "X-SPDY-Stream-ID"; + public static final AsciiString STREAM_ID = new AsciiString("X-SPDY-Stream-ID"); /** * {@code "X-SPDY-Associated-To-Stream-ID"} */ - public static final String ASSOCIATED_TO_STREAM_ID = "X-SPDY-Associated-To-Stream-ID"; + public static final AsciiString ASSOCIATED_TO_STREAM_ID = new AsciiString("X-SPDY-Associated-To-Stream-ID"); /** * {@code "X-SPDY-Priority"} */ - public static final String PRIORITY = "X-SPDY-Priority"; + public static final AsciiString PRIORITY = new AsciiString("X-SPDY-Priority"); /** * {@code "X-SPDY-URL"} */ - public static final String URL = "X-SPDY-URL"; + public static final AsciiString URL = new AsciiString("X-SPDY-URL"); /** * {@code "X-SPDY-Scheme"} */ - public static final String SCHEME = "X-SPDY-Scheme"; + public static final AsciiString SCHEME = new AsciiString("X-SPDY-Scheme"); private Names() { } } - private SpdyHttpHeaders() { - } - - /** - * Removes the {@code "X-SPDY-Stream-ID"} header. - */ - public static void removeStreamId(HttpMessage message) { - message.headers().remove(Names.STREAM_ID); - } - - /** - * Returns the value of the {@code "X-SPDY-Stream-ID"} header. - */ - public static int getStreamId(HttpMessage message) { - return HttpHeaders.getIntHeader(message, Names.STREAM_ID); - } - - /** - * Sets the {@code "X-SPDY-Stream-ID"} header. - */ - public static void setStreamId(HttpMessage message, int streamId) { - HttpHeaders.setIntHeader(message, Names.STREAM_ID, streamId); - } - - /** - * Removes the {@code "X-SPDY-Associated-To-Stream-ID"} header. - */ - public static void removeAssociatedToStreamId(HttpMessage message) { - message.headers().remove(Names.ASSOCIATED_TO_STREAM_ID); - } - - /** - * Returns the value of the {@code "X-SPDY-Associated-To-Stream-ID"} header. - * - * @return the header value or {@code 0} if there is no such header or - * if the header value is not a number - */ - public static int getAssociatedToStreamId(HttpMessage message) { - return HttpHeaders.getIntHeader(message, Names.ASSOCIATED_TO_STREAM_ID, 0); - } - - /** - * Sets the {@code "X-SPDY-Associated-To-Stream-ID"} header. - */ - public static void setAssociatedToStreamId(HttpMessage message, int associatedToStreamId) { - HttpHeaders.setIntHeader(message, Names.ASSOCIATED_TO_STREAM_ID, associatedToStreamId); - } - - /** - * Removes the {@code "X-SPDY-Priority"} header. - */ - public static void removePriority(HttpMessage message) { - message.headers().remove(Names.PRIORITY); - } - - /** - * Returns the value of the {@code "X-SPDY-Priority"} header. - * - * @return the header value or {@code 0} if there is no such header or - * if the header value is not a number - */ - public static byte getPriority(HttpMessage message) { - return (byte) HttpHeaders.getIntHeader(message, Names.PRIORITY, 0); - } - - /** - * Sets the {@code "X-SPDY-Priority"} header. - */ - public static void setPriority(HttpMessage message, byte priority) { - HttpHeaders.setIntHeader(message, Names.PRIORITY, priority); - } - - /** - * Removes the {@code "X-SPDY-URL"} header. - */ - public static void removeUrl(HttpMessage message) { - message.headers().remove(Names.URL); - } - - /** - * Returns the value of the {@code "X-SPDY-URL"} header. - */ - public static String getUrl(HttpMessage message) { - return message.headers().get(Names.URL); - } - - /** - * Sets the {@code "X-SPDY-URL"} header. - */ - public static void setUrl(HttpMessage message, String url) { - message.headers().set(Names.URL, url); - } - - /** - * Removes the {@code "X-SPDY-Scheme"} header. - */ - public static void removeScheme(HttpMessage message) { - message.headers().remove(Names.SCHEME); - } - - /** - * Returns the value of the {@code "X-SPDY-Scheme"} header. - */ - public static String getScheme(HttpMessage message) { - return message.headers().get(Names.SCHEME); - } - - /** - * Sets the {@code "X-SPDY-Scheme"} header. - */ - public static void setScheme(HttpMessage message, String scheme) { - message.headers().set(Names.SCHEME, scheme); - } + private SpdyHttpHeaders() { } } 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 51834a281c..99f4950794 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 @@ -17,7 +17,9 @@ package io.netty.handler.codec.spdy; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageCodec; +import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMessage; +import io.netty.handler.codec.spdy.SpdyHttpHeaders.Names; import io.netty.util.ReferenceCountUtil; import java.util.LinkedList; @@ -43,7 +45,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)) { - SpdyHttpHeaders.setStreamId(msg, id); + HttpHeaders.setIntHeader(msg, Names.STREAM_ID, id); } out.add(ReferenceCountUtil.retain(msg)); @@ -56,7 +58,7 @@ public class SpdyHttpResponseStreamIdHandler extends if (!contains) { ids.add(NO_ID); } else { - ids.add(SpdyHttpHeaders.getStreamId((HttpMessage) msg)); + ids.add(HttpHeaders.getIntHeader((HttpMessage) msg, Names.STREAM_ID)); } } else if (msg instanceof SpdyRstStreamFrame) { ids.remove(((SpdyRstStreamFrame) msg).getStreamId()); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeadersTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeadersTest.java index 559791e486..fa67920f6c 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeadersTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpHeadersTest.java @@ -15,6 +15,7 @@ */ package io.netty.handler.codec.http; +import io.netty.handler.codec.AsciiString; import org.junit.Test; import java.util.List; @@ -50,9 +51,9 @@ public class HttpHeadersTest { @Test public void testEquansIgnoreCase() { - assertThat(HttpHeaders.equalsIgnoreCase(null, null), is(true)); - assertThat(HttpHeaders.equalsIgnoreCase(null, "foo"), is(false)); - assertThat(HttpHeaders.equalsIgnoreCase("bar", null), is(false)); - assertThat(HttpHeaders.equalsIgnoreCase("FoO", "fOo"), is(true)); + assertThat(AsciiString.equalsIgnoreCase(null, null), is(true)); + assertThat(AsciiString.equalsIgnoreCase(null, "foo"), is(false)); + assertThat(AsciiString.equalsIgnoreCase("bar", null), is(false)); + assertThat(AsciiString.equalsIgnoreCase("FoO", "fOo"), is(true)); } } 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 a2a99eed28..bc0b42fb8d 100644 --- a/codec/src/main/java/io/netty/handler/codec/AsciiString.java +++ b/codec/src/main/java/io/netty/handler/codec/AsciiString.java @@ -35,8 +35,12 @@ public final class AsciiString implements CharSequence, Comparable public static final AsciiString EMPTY_STRING = new AsciiString(""); - /** XXX: Make sure that this method and {@link #hashCode()} uses the same hashing algorithm */ - static int hashCode(CharSequence value) { + /** + * 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}. + */ + public static int caseInsensitiveHashCode(CharSequence value) { if (value instanceof AsciiString) { return value.hashCode(); } @@ -50,6 +54,57 @@ public final class AsciiString implements CharSequence, Comparable return hash; } + /** + * 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) { + return true; + } + + if (a instanceof AsciiString) { + AsciiString aa = (AsciiString) a; + return aa.equalsIgnoreCase(b); + } + + if (b instanceof AsciiString) { + AsciiString ab = (AsciiString) b; + return ab.equalsIgnoreCase(a); + } + + if (a == null || b == null) { + return false; + } + + return a.toString().equalsIgnoreCase(b.toString()); + } + + /** + * Returns {@code true} if both {@link CharSequence}'s are equals. This only supports 8-bit ASCII. + */ + public static boolean equals(CharSequence a, CharSequence b) { + if (a == b) { + return true; + } + + if (a instanceof AsciiString) { + AsciiString aa = (AsciiString) a; + return aa.equals(b); + } + + if (b instanceof AsciiString) { + AsciiString ab = (AsciiString) b; + return ab.equals(a); + } + + if (a == null || b == null) { + return false; + } + + return a.equals(b); + } + private final byte[] value; private String string; private int hash; 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 fbe9a815e0..01f91ec9de 100644 --- a/codec/src/main/java/io/netty/handler/codec/DefaultTextHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/DefaultTextHeaders.java @@ -19,6 +19,7 @@ package io.netty.handler.codec; 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; @@ -59,7 +60,7 @@ public class DefaultTextHeaders implements TextHeaders { } protected int hashCode(CharSequence name) { - return AsciiString.hashCode(name); + return AsciiString.caseInsensitiveHashCode(name); } protected CharSequence convertName(CharSequence name) { @@ -376,6 +377,20 @@ public class DefaultTextHeaders implements TextHeaders { return v.toString(); } + @Override + public int getInt(CharSequence name) { + CharSequence v = getUnconverted(name); + if (v == null) { + throw new NoSuchElementException(String.valueOf(name)); + } + + if (v instanceof AsciiString) { + return ((AsciiString) v).parseInt(); + } else { + return Integer.parseInt(v.toString()); + } + } + @Override public int getInt(CharSequence name, int defaultValue) { CharSequence v = getUnconverted(name); @@ -394,6 +409,20 @@ public class DefaultTextHeaders implements TextHeaders { } } + @Override + public long getLong(CharSequence name) { + CharSequence v = getUnconverted(name); + if (v == null) { + throw new NoSuchElementException(String.valueOf(name)); + } + + if (v instanceof AsciiString) { + return ((AsciiString) v).parseLong(); + } else { + return Long.parseLong(v.toString()); + } + } + @Override public long getLong(CharSequence name, long defaultValue) { CharSequence v = getUnconverted(name); @@ -412,6 +441,16 @@ public class DefaultTextHeaders implements TextHeaders { } } + @Override + public long getTimeMillis(CharSequence name) { + CharSequence v = getUnconverted(name); + if (v == null) { + throw new NoSuchElementException(String.valueOf(name)); + } + + return HttpHeaderDateFormat.get().parse(v.toString()); + } + @Override public long getTimeMillis(CharSequence name, long defaultValue) { CharSequence v = getUnconverted(name); @@ -786,6 +825,20 @@ public class DefaultTextHeaders implements TextHeaders { dateFormat3.setTimeZone(tz); } + long parse(String text) { + 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) { + PlatformDependent.throwException(new ParseException(text, 0)); + } + return date.getTime(); + } + long parse(String text, long defaultValue) { Date date = dateFormat1.parse(text, parsePos); if (date == null) { 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 21eff3162c..e2e5ebf8fd 100644 --- a/codec/src/main/java/io/netty/handler/codec/EmptyTextHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/EmptyTextHeaders.java @@ -20,6 +20,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; +import java.util.NoSuchElementException; import java.util.Set; public class EmptyTextHeaders implements TextHeaders { @@ -36,16 +37,31 @@ public class EmptyTextHeaders implements TextHeaders { return defaultValue; } + @Override + public int getInt(CharSequence name) { + throw new NoSuchElementException(String.valueOf(name)); + } + @Override public int getInt(CharSequence name, int defaultValue) { return defaultValue; } + @Override + public long getLong(CharSequence name) { + throw new NoSuchElementException(String.valueOf(name)); + } + @Override public long getLong(CharSequence name, long defaultValue) { return defaultValue; } + @Override + public long getTimeMillis(CharSequence name) { + throw new NoSuchElementException(String.valueOf(name)); + } + @Override public long getTimeMillis(CharSequence name, long defaultValue) { return defaultValue; 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 a84f069bc2..d84b250182 100644 --- a/codec/src/main/java/io/netty/handler/codec/TextHeaders.java +++ b/codec/src/main/java/io/netty/handler/codec/TextHeaders.java @@ -41,8 +41,11 @@ public interface TextHeaders extends Iterable> { String get(CharSequence name); String get(CharSequence name, String defaultValue); + int getInt(CharSequence name); int getInt(CharSequence name, int defaultValue); + long getLong(CharSequence name); long getLong(CharSequence name, long defaultValue); + long getTimeMillis(CharSequence name); long getTimeMillis(CharSequence name, long defaultValue); /** diff --git a/example/src/main/java/io/netty/example/spdy/client/SpdyClientStreamIdHandler.java b/example/src/main/java/io/netty/example/spdy/client/SpdyClientStreamIdHandler.java index 30587ed8f3..a1eaefdd41 100644 --- a/example/src/main/java/io/netty/example/spdy/client/SpdyClientStreamIdHandler.java +++ b/example/src/main/java/io/netty/example/spdy/client/SpdyClientStreamIdHandler.java @@ -18,8 +18,10 @@ package io.netty.example.spdy.client; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; +import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMessage; import io.netty.handler.codec.spdy.SpdyHttpHeaders; +import io.netty.handler.codec.spdy.SpdyHttpHeaders.Names; /** * Adds a unique client stream ID to the SPDY header. Client stream IDs MUST be odd. @@ -37,7 +39,7 @@ public class SpdyClientStreamIdHandler extends ChannelOutboundHandlerAdapter { if (acceptOutboundMessage(msg)) { HttpMessage httpMsg = (HttpMessage) msg; if (!httpMsg.headers().contains(SpdyHttpHeaders.Names.STREAM_ID)) { - SpdyHttpHeaders.setStreamId(httpMsg, currentStreamId); + HttpHeaders.setIntHeader(httpMsg, Names.STREAM_ID, currentStreamId); // Client stream IDs are always odd currentStreamId += 2; }