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
This commit is contained in:
Trustin Lee 2014-06-14 17:33:34 +09:00
parent 681d460938
commit c076c33901
20 changed files with 220 additions and 214 deletions

View File

@ -17,6 +17,7 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; 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.NonValidatingTextHeaders;
import io.netty.handler.codec.http.DefaultHttpHeaders.ValidatingTextHeaders; import io.netty.handler.codec.http.DefaultHttpHeaders.ValidatingTextHeaders;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
@ -113,9 +114,9 @@ public class DefaultLastHttpContent extends DefaultHttpContent implements LastHt
@Override @Override
protected CharSequence convertName(CharSequence name) { protected CharSequence convertName(CharSequence name) {
name = super.convertName(name); name = super.convertName(name);
if (HttpHeaders.equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH, name) || if (AsciiString.equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH, name) ||
HttpHeaders.equalsIgnoreCase(HttpHeaders.Names.TRANSFER_ENCODING, name) || AsciiString.equalsIgnoreCase(HttpHeaders.Names.TRANSFER_ENCODING, name) ||
HttpHeaders.equalsIgnoreCase(HttpHeaders.Names.TRAILER, name)) { AsciiString.equalsIgnoreCase(HttpHeaders.Names.TRAILER, name)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"prohibited trailing header: " + name); "prohibited trailing header: " + name);
} }

View File

@ -16,6 +16,7 @@
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.channel.embedded.EmbeddedChannel; 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.ZlibCodecFactory;
import io.netty.handler.codec.compression.ZlibWrapper; import io.netty.handler.codec.compression.ZlibWrapper;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
@ -96,7 +97,7 @@ public class HttpContentCompressor extends HttpContentEncoder {
protected Result beginEncode(HttpResponse headers, String acceptEncoding) throws Exception { protected Result beginEncode(HttpResponse headers, String acceptEncoding) throws Exception {
String contentEncoding = headers.headers().get(HttpHeaders.Names.CONTENT_ENCODING); String contentEncoding = headers.headers().get(HttpHeaders.Names.CONTENT_ENCODING);
if (contentEncoding != null && if (contentEncoding != null &&
!HttpHeaders.equalsIgnoreCase(HttpHeaders.Values.IDENTITY, contentEncoding)) { !AsciiString.equalsIgnoreCase(HttpHeaders.Values.IDENTITY, contentEncoding)) {
return null; return null;
} }

View File

@ -567,14 +567,14 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
*/ */
public static boolean isKeepAlive(HttpMessage message) { public static boolean isKeepAlive(HttpMessage message) {
String connection = message.headers().get(CONNECTION_ENTITY); String connection = message.headers().get(CONNECTION_ENTITY);
if (connection != null && equalsIgnoreCase(CLOSE_ENTITY, connection)) { if (connection != null && AsciiString.equalsIgnoreCase(CLOSE_ENTITY, connection)) {
return false; return false;
} }
if (message.getProtocolVersion().isKeepAliveDefault()) { if (message.getProtocolVersion().isKeepAliveDefault()) {
return !equalsIgnoreCase(CLOSE_ENTITY, connection); return !AsciiString.equalsIgnoreCase(CLOSE_ENTITY, connection);
} else { } else {
return equalsIgnoreCase(KEEP_ALIVE_ENTITY, connection); return AsciiString.equalsIgnoreCase(KEEP_ALIVE_ENTITY, connection);
} }
} }
@ -1115,7 +1115,7 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
if (value == null) { if (value == null) {
return false; return false;
} }
if (equalsIgnoreCase(CONTINUE_ENTITY, value)) { if (AsciiString.equalsIgnoreCase(CONTINUE_ENTITY, value)) {
return true; return true;
} }
@ -1165,7 +1165,7 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
Iterator<String> valuesIt = values.iterator(); Iterator<String> valuesIt = values.iterator();
while (valuesIt.hasNext()) { while (valuesIt.hasNext()) {
String value = valuesIt.next(); String value = valuesIt.next();
if (equalsIgnoreCase(value, CHUNKED_ENTITY)) { if (AsciiString.equalsIgnoreCase(value, CHUNKED_ENTITY)) {
valuesIt.remove(); valuesIt.remove();
} }
} }
@ -1186,39 +1186,11 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
} }
/** /**
* Returns {@code true} if both {@link CharSequence}'s are equals when ignore the case. * @deprecated Use {@link AsciiString#equalsIgnoreCase(CharSequence, CharSequence)} instead.
* This only supports US_ASCII.
*/ */
@Deprecated
public static boolean equalsIgnoreCase(CharSequence name1, CharSequence name2) { public static boolean equalsIgnoreCase(CharSequence name1, CharSequence name2) {
if (name1 == name2) { return AsciiString.equalsIgnoreCase(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;
} }
static void encode(HttpHeaders headers, ByteBuf buf) { static void encode(HttpHeaders headers, ByteBuf buf) {
@ -1238,6 +1210,7 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
buf.writeBytes(CRLF); buf.writeBytes(CRLF);
} }
@Deprecated
public static void encodeAscii(CharSequence seq, ByteBuf buf) { public static void encodeAscii(CharSequence seq, ByteBuf buf) {
if (seq instanceof AsciiString) { if (seq instanceof AsciiString) {
((AsciiString) seq).copy(0, buf, seq.length()); ((AsciiString) seq).copy(0, buf, seq.length());
@ -1499,7 +1472,7 @@ public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>>
for (String v: values) { for (String v: values) {
if (ignoreCase) { if (ignoreCase) {
if (equalsIgnoreCase(v, value)) { if (AsciiString.equalsIgnoreCase(v, value)) {
return true; return true;
} }
} else { } else {

View File

@ -20,6 +20,7 @@ import io.netty.buffer.ByteBufProcessor;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.DecoderResult;
import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.ReplayingDecoder;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
@ -559,9 +560,9 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
} else { } else {
String[] header = splitHeader(line); String[] header = splitHeader(line);
String name = header[0]; String name = header[0];
if (!HttpHeaders.equalsIgnoreCase(name, HttpHeaders.Names.CONTENT_LENGTH) && if (!AsciiString.equalsIgnoreCase(name, HttpHeaders.Names.CONTENT_LENGTH) &&
!HttpHeaders.equalsIgnoreCase(name, HttpHeaders.Names.TRANSFER_ENCODING) && !AsciiString.equalsIgnoreCase(name, HttpHeaders.Names.TRANSFER_ENCODING) &&
!HttpHeaders.equalsIgnoreCase(name, HttpHeaders.Names.TRAILER)) { !AsciiString.equalsIgnoreCase(name, HttpHeaders.Names.TRAILER)) {
trailer.trailingHeaders().add(name, header[1]); trailer.trailingHeaders().add(name, header[1]);
} }
lastHeader = name; lastHeader = name;

View File

@ -17,6 +17,7 @@ package io.netty.handler.codec.http.multipart;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.DecoderResult;
import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.DefaultHttpContent; import io.netty.handler.codec.http.DefaultHttpContent;
@ -741,7 +742,7 @@ public class HttpPostRequestEncoder implements ChunkedInput<HttpContent> {
if (transferEncoding != null) { if (transferEncoding != null) {
headers.remove(HttpHeaders.Names.TRANSFER_ENCODING); headers.remove(HttpHeaders.Names.TRANSFER_ENCODING);
for (String v : transferEncoding) { for (String v : transferEncoding) {
if (HttpHeaders.equalsIgnoreCase(v, HttpHeaders.Values.CHUNKED)) { if (AsciiString.equalsIgnoreCase(v, HttpHeaders.Values.CHUNKED)) {
// ignore // ignore
} else { } else {
headers.add(HttpHeaders.Names.TRANSFER_ENCODING, v); headers.add(HttpHeaders.Names.TRANSFER_ENCODING, v);

View File

@ -17,6 +17,7 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -198,13 +199,13 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
HttpHeaders headers = response.headers(); HttpHeaders headers = response.headers();
String upgrade = headers.get(Names.UPGRADE); 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: " throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
+ upgrade); + upgrade);
} }
String connection = headers.get(Names.CONNECTION); 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: " throw new WebSocketHandshakeException("Invalid handshake response connection: "
+ connection); + connection);
} }

View File

@ -15,6 +15,7 @@
*/ */
package io.netty.handler.codec.http.websocketx; 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.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -173,12 +174,12 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
} }
String upgrade = headers.get(Names.UPGRADE); 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); throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade);
} }
String connection = headers.get(Names.CONNECTION); 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); throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection);
} }

View File

@ -15,6 +15,7 @@
*/ */
package io.netty.handler.codec.http.websocketx; 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.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -174,12 +175,12 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
} }
String upgrade = headers.get(Names.UPGRADE); 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); throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade);
} }
String connection = headers.get(Names.CONNECTION); 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); throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection);
} }

View File

@ -15,6 +15,7 @@
*/ */
package io.netty.handler.codec.http.websocketx; 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.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -184,12 +185,12 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
} }
String upgrade = headers.get(Names.UPGRADE); 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); throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade);
} }
String connection = headers.get(Names.CONNECTION); 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); throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection);
} }

View File

@ -20,6 +20,7 @@ import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -109,8 +110,8 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
protected FullHttpResponse newHandshakeResponse(FullHttpRequest req, HttpHeaders headers) { protected FullHttpResponse newHandshakeResponse(FullHttpRequest req, HttpHeaders headers) {
// Serve the WebSocket handshake request. // Serve the WebSocket handshake request.
if (!HttpHeaders.equalsIgnoreCase(Values.UPGRADE, req.headers().get(CONNECTION)) if (!AsciiString.equalsIgnoreCase(Values.UPGRADE, req.headers().get(CONNECTION))
|| !HttpHeaders.equalsIgnoreCase(WEBSOCKET, req.headers().get(Names.UPGRADE))) { || !AsciiString.equalsIgnoreCase(WEBSOCKET, req.headers().get(Names.UPGRADE))) {
throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade"); throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade");
} }

View File

@ -28,6 +28,7 @@ import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.spdy.SpdyHttpHeaders.Names;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -138,10 +139,11 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
createHttpResponse(spdyVersion, spdySynStreamFrame); createHttpResponse(spdyVersion, spdySynStreamFrame);
// Set the Stream-ID, Associated-To-Stream-ID, Priority, and URL as headers // Set the Stream-ID, Associated-To-Stream-ID, Priority, and URL as headers
SpdyHttpHeaders.setStreamId(httpResponseWithEntity, streamId); HttpHeaders.setIntHeader(httpResponseWithEntity, Names.STREAM_ID, streamId);
SpdyHttpHeaders.setAssociatedToStreamId(httpResponseWithEntity, associatedToStreamId); HttpHeaders.setIntHeader(
SpdyHttpHeaders.setPriority(httpResponseWithEntity, spdySynStreamFrame.getPriority()); httpResponseWithEntity, Names.ASSOCIATED_TO_STREAM_ID, associatedToStreamId);
SpdyHttpHeaders.setUrl(httpResponseWithEntity, URL); HttpHeaders.setIntHeader(httpResponseWithEntity, Names.PRIORITY, spdySynStreamFrame.getPriority());
httpResponseWithEntity.headers().set(Names.URL, URL);
if (spdySynStreamFrame.isLast()) { if (spdySynStreamFrame.isLast()) {
HttpHeaders.setContentLength(httpResponseWithEntity, 0); HttpHeaders.setContentLength(httpResponseWithEntity, 0);
@ -174,7 +176,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
FullHttpRequest httpRequestWithEntity = createHttpRequest(spdyVersion, spdySynStreamFrame); FullHttpRequest httpRequestWithEntity = createHttpRequest(spdyVersion, spdySynStreamFrame);
// Set the Stream-ID as a header // Set the Stream-ID as a header
SpdyHttpHeaders.setStreamId(httpRequestWithEntity, streamId); HttpHeaders.setIntHeader(httpRequestWithEntity, Names.STREAM_ID, streamId);
if (spdySynStreamFrame.isLast()) { if (spdySynStreamFrame.isLast()) {
out.add(httpRequestWithEntity); out.add(httpRequestWithEntity);
@ -213,7 +215,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyFrame> {
FullHttpResponse httpResponseWithEntity = createHttpResponse(spdyVersion, spdySynReplyFrame); FullHttpResponse httpResponseWithEntity = createHttpResponse(spdyVersion, spdySynReplyFrame);
// Set the Stream-ID as a header // Set the Stream-ID as a header
SpdyHttpHeaders.setStreamId(httpResponseWithEntity, streamId); HttpHeaders.setIntHeader(httpResponseWithEntity, Names.STREAM_ID, streamId);
if (spdySynReplyFrame.isLast()) { if (spdySynReplyFrame.isLast()) {
HttpHeaders.setContentLength(httpResponseWithEntity, 0); HttpHeaders.setContentLength(httpResponseWithEntity, 0);

View File

@ -27,6 +27,7 @@ import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.spdy.SpdyHttpHeaders.Names;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -207,23 +208,24 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
private SpdySynStreamFrame createSynStreamFrame(HttpMessage httpMessage) private SpdySynStreamFrame createSynStreamFrame(HttpMessage httpMessage)
throws Exception { throws Exception {
// Get the Stream-ID, Associated-To-Stream-ID, Priority, URL, and scheme from the headers // Get the Stream-ID, Associated-To-Stream-ID, Priority, URL, and scheme from the headers
int streamID = SpdyHttpHeaders.getStreamId(httpMessage); final HttpHeaders httpHeaders = httpMessage.headers();
int associatedToStreamId = SpdyHttpHeaders.getAssociatedToStreamId(httpMessage); int streamID = HttpHeaders.getIntHeader(httpMessage, Names.STREAM_ID);
byte priority = SpdyHttpHeaders.getPriority(httpMessage); int associatedToStreamId = HttpHeaders.getIntHeader(httpMessage, Names.ASSOCIATED_TO_STREAM_ID, 0);
String URL = SpdyHttpHeaders.getUrl(httpMessage); byte priority = (byte) HttpHeaders.getIntHeader(httpMessage, Names.PRIORITY, 0);
String scheme = SpdyHttpHeaders.getScheme(httpMessage); String URL = httpHeaders.get(Names.URL);
SpdyHttpHeaders.removeStreamId(httpMessage); String scheme = httpHeaders.get(Names.SCHEME);
SpdyHttpHeaders.removeAssociatedToStreamId(httpMessage); httpHeaders.remove(Names.STREAM_ID);
SpdyHttpHeaders.removePriority(httpMessage); httpHeaders.remove(Names.ASSOCIATED_TO_STREAM_ID);
SpdyHttpHeaders.removeUrl(httpMessage); httpHeaders.remove(Names.PRIORITY);
SpdyHttpHeaders.removeScheme(httpMessage); httpHeaders.remove(Names.URL);
httpHeaders.remove(Names.SCHEME);
// The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding // The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding
// headers are not valid and MUST not be sent. // headers are not valid and MUST not be sent.
httpMessage.headers().remove(HttpHeaders.Names.CONNECTION); httpHeaders.remove(HttpHeaders.Names.CONNECTION);
httpMessage.headers().remove("Keep-Alive"); httpHeaders.remove("Keep-Alive");
httpMessage.headers().remove("Proxy-Connection"); httpHeaders.remove("Proxy-Connection");
httpMessage.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING); httpHeaders.remove(HttpHeaders.Names.TRANSFER_ENCODING);
SpdySynStreamFrame spdySynStreamFrame = SpdySynStreamFrame spdySynStreamFrame =
new DefaultSpdySynStreamFrame(streamID, associatedToStreamId, priority); new DefaultSpdySynStreamFrame(streamID, associatedToStreamId, priority);
@ -247,7 +249,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
// Replace the HTTP host header with the SPDY host header // Replace the HTTP host header with the SPDY host header
if (spdyVersion >= 3) { if (spdyVersion >= 3) {
String host = HttpHeaders.getHost(httpMessage); String host = HttpHeaders.getHost(httpMessage);
httpMessage.headers().remove(HttpHeaders.Names.HOST); httpHeaders.remove(HttpHeaders.Names.HOST);
frameHeaders.set(HOST, host); frameHeaders.set(HOST, host);
} }
@ -258,7 +260,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
frameHeaders.set(SCHEME, scheme); frameHeaders.set(SCHEME, scheme);
// Transfer the remaining HTTP headers // Transfer the remaining HTTP headers
for (Map.Entry<String, String> entry: httpMessage.headers()) { for (Map.Entry<String, String> entry: httpHeaders) {
frameHeaders.add(entry.getKey(), entry.getValue()); frameHeaders.add(entry.getKey(), entry.getValue());
} }
currentStreamId = spdySynStreamFrame.getStreamId(); currentStreamId = spdySynStreamFrame.getStreamId();
@ -270,15 +272,16 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
private SpdySynReplyFrame createSynReplyFrame(HttpResponse httpResponse) private SpdySynReplyFrame createSynReplyFrame(HttpResponse httpResponse)
throws Exception { throws Exception {
// Get the Stream-ID from the headers // Get the Stream-ID from the headers
int streamID = SpdyHttpHeaders.getStreamId(httpResponse); final HttpHeaders httpHeaders = httpResponse.headers();
SpdyHttpHeaders.removeStreamId(httpResponse); int streamID = HttpHeaders.getIntHeader(httpResponse, Names.STREAM_ID);
httpHeaders.remove(Names.STREAM_ID);
// The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding // The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding
// headers are not valid and MUST not be sent. // headers are not valid and MUST not be sent.
httpResponse.headers().remove(HttpHeaders.Names.CONNECTION); httpHeaders.remove(HttpHeaders.Names.CONNECTION);
httpResponse.headers().remove("Keep-Alive"); httpHeaders.remove("Keep-Alive");
httpResponse.headers().remove("Proxy-Connection"); httpHeaders.remove("Proxy-Connection");
httpResponse.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING); httpHeaders.remove(HttpHeaders.Names.TRANSFER_ENCODING);
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID); SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
SpdyHeaders frameHeaders = spdySynReplyFrame.headers(); SpdyHeaders frameHeaders = spdySynReplyFrame.headers();
@ -287,7 +290,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
frameHeaders.set(VERSION, httpResponse.getProtocolVersion()); frameHeaders.set(VERSION, httpResponse.getProtocolVersion());
// Transfer the remaining HTTP headers // Transfer the remaining HTTP headers
for (Map.Entry<String, String> entry: httpResponse.headers()) { for (Map.Entry<String, String> entry: httpHeaders) {
spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue()); spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue());
} }

View File

@ -15,8 +15,7 @@
*/ */
package io.netty.handler.codec.spdy; package io.netty.handler.codec.spdy;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.AsciiString;
import io.netty.handler.codec.http.HttpMessage;
/** /**
* Provides the constants for the header names and the utility methods * Provides the constants for the header names and the utility methods
@ -31,138 +30,26 @@ public final class SpdyHttpHeaders {
/** /**
* {@code "X-SPDY-Stream-ID"} * {@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"} * {@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"} * {@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"} * {@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"} * {@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 Names() { }
} }
private SpdyHttpHeaders() { 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);
}
} }

View File

@ -17,7 +17,9 @@ package io.netty.handler.codec.spdy;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec; 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.http.HttpMessage;
import io.netty.handler.codec.spdy.SpdyHttpHeaders.Names;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import java.util.LinkedList; import java.util.LinkedList;
@ -43,7 +45,7 @@ public class SpdyHttpResponseStreamIdHandler extends
protected void encode(ChannelHandlerContext ctx, HttpMessage msg, List<Object> out) throws Exception { protected void encode(ChannelHandlerContext ctx, HttpMessage msg, List<Object> out) throws Exception {
Integer id = ids.poll(); Integer id = ids.poll();
if (id != null && id.intValue() != NO_ID && !msg.headers().contains(SpdyHttpHeaders.Names.STREAM_ID)) { 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)); out.add(ReferenceCountUtil.retain(msg));
@ -56,7 +58,7 @@ public class SpdyHttpResponseStreamIdHandler extends
if (!contains) { if (!contains) {
ids.add(NO_ID); ids.add(NO_ID);
} else { } else {
ids.add(SpdyHttpHeaders.getStreamId((HttpMessage) msg)); ids.add(HttpHeaders.getIntHeader((HttpMessage) msg, Names.STREAM_ID));
} }
} else if (msg instanceof SpdyRstStreamFrame) { } else if (msg instanceof SpdyRstStreamFrame) {
ids.remove(((SpdyRstStreamFrame) msg).getStreamId()); ids.remove(((SpdyRstStreamFrame) msg).getStreamId());

View File

@ -15,6 +15,7 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.handler.codec.AsciiString;
import org.junit.Test; import org.junit.Test;
import java.util.List; import java.util.List;
@ -50,9 +51,9 @@ public class HttpHeadersTest {
@Test @Test
public void testEquansIgnoreCase() { public void testEquansIgnoreCase() {
assertThat(HttpHeaders.equalsIgnoreCase(null, null), is(true)); assertThat(AsciiString.equalsIgnoreCase(null, null), is(true));
assertThat(HttpHeaders.equalsIgnoreCase(null, "foo"), is(false)); assertThat(AsciiString.equalsIgnoreCase(null, "foo"), is(false));
assertThat(HttpHeaders.equalsIgnoreCase("bar", null), is(false)); assertThat(AsciiString.equalsIgnoreCase("bar", null), is(false));
assertThat(HttpHeaders.equalsIgnoreCase("FoO", "fOo"), is(true)); assertThat(AsciiString.equalsIgnoreCase("FoO", "fOo"), is(true));
} }
} }

View File

@ -35,8 +35,12 @@ public final class AsciiString implements CharSequence, Comparable<CharSequence>
public static final AsciiString EMPTY_STRING = new AsciiString(""); 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) { if (value instanceof AsciiString) {
return value.hashCode(); return value.hashCode();
} }
@ -50,6 +54,57 @@ public final class AsciiString implements CharSequence, Comparable<CharSequence>
return hash; 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 final byte[] value;
private String string; private String string;
private int hash; private int hash;

View File

@ -19,6 +19,7 @@ package io.netty.handler.codec;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.ParseException;
import java.text.ParsePosition; import java.text.ParsePosition;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
@ -59,7 +60,7 @@ public class DefaultTextHeaders implements TextHeaders {
} }
protected int hashCode(CharSequence name) { protected int hashCode(CharSequence name) {
return AsciiString.hashCode(name); return AsciiString.caseInsensitiveHashCode(name);
} }
protected CharSequence convertName(CharSequence name) { protected CharSequence convertName(CharSequence name) {
@ -376,6 +377,20 @@ public class DefaultTextHeaders implements TextHeaders {
return v.toString(); 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 @Override
public int getInt(CharSequence name, int defaultValue) { public int getInt(CharSequence name, int defaultValue) {
CharSequence v = getUnconverted(name); 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 @Override
public long getLong(CharSequence name, long defaultValue) { public long getLong(CharSequence name, long defaultValue) {
CharSequence v = getUnconverted(name); 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 @Override
public long getTimeMillis(CharSequence name, long defaultValue) { public long getTimeMillis(CharSequence name, long defaultValue) {
CharSequence v = getUnconverted(name); CharSequence v = getUnconverted(name);
@ -786,6 +825,20 @@ public class DefaultTextHeaders implements TextHeaders {
dateFormat3.setTimeZone(tz); 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) { long parse(String text, long defaultValue) {
Date date = dateFormat1.parse(text, parsePos); Date date = dateFormat1.parse(text, parsePos);
if (date == null) { if (date == null) {

View File

@ -20,6 +20,7 @@ import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
public class EmptyTextHeaders implements TextHeaders { public class EmptyTextHeaders implements TextHeaders {
@ -36,16 +37,31 @@ public class EmptyTextHeaders implements TextHeaders {
return defaultValue; return defaultValue;
} }
@Override
public int getInt(CharSequence name) {
throw new NoSuchElementException(String.valueOf(name));
}
@Override @Override
public int getInt(CharSequence name, int defaultValue) { public int getInt(CharSequence name, int defaultValue) {
return defaultValue; return defaultValue;
} }
@Override
public long getLong(CharSequence name) {
throw new NoSuchElementException(String.valueOf(name));
}
@Override @Override
public long getLong(CharSequence name, long defaultValue) { public long getLong(CharSequence name, long defaultValue) {
return defaultValue; return defaultValue;
} }
@Override
public long getTimeMillis(CharSequence name) {
throw new NoSuchElementException(String.valueOf(name));
}
@Override @Override
public long getTimeMillis(CharSequence name, long defaultValue) { public long getTimeMillis(CharSequence name, long defaultValue) {
return defaultValue; return defaultValue;

View File

@ -41,8 +41,11 @@ public interface TextHeaders extends Iterable<Map.Entry<String, String>> {
String get(CharSequence name); String get(CharSequence name);
String get(CharSequence name, String defaultValue); String get(CharSequence name, String defaultValue);
int getInt(CharSequence name);
int getInt(CharSequence name, int defaultValue); int getInt(CharSequence name, int defaultValue);
long getLong(CharSequence name);
long getLong(CharSequence name, long defaultValue); long getLong(CharSequence name, long defaultValue);
long getTimeMillis(CharSequence name);
long getTimeMillis(CharSequence name, long defaultValue); long getTimeMillis(CharSequence name, long defaultValue);
/** /**

View File

@ -18,8 +18,10 @@ package io.netty.example.spdy.client;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMessage; import io.netty.handler.codec.http.HttpMessage;
import io.netty.handler.codec.spdy.SpdyHttpHeaders; 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. * 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)) { if (acceptOutboundMessage(msg)) {
HttpMessage httpMsg = (HttpMessage) msg; HttpMessage httpMsg = (HttpMessage) msg;
if (!httpMsg.headers().contains(SpdyHttpHeaders.Names.STREAM_ID)) { 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 // Client stream IDs are always odd
currentStreamId += 2; currentStreamId += 2;
} }