From 2e5cbeabc490ed060fb4efb1c11f6d30709c5859 Mon Sep 17 00:00:00 2001 From: Jeff Pinner Date: Thu, 17 Oct 2013 10:58:28 -0700 Subject: [PATCH] Backport HttpHeaders / SpdyHeaders from Netty 4. --- .../HttpTunnelingClientSocketChannel.java | 10 +- .../file/HttpStaticFileServerHandler.java | 16 +- .../HttpHelloWorldServerHandler.java | 6 +- .../example/http/snoop/HttpSnoopClient.java | 8 +- .../http/snoop/HttpSnoopClientHandler.java | 6 +- .../http/snoop/HttpSnoopServerHandler.java | 22 +- .../example/http/upload/HttpUploadClient.java | 28 +- .../http/upload/HttpUploadClientHandler.java | 6 +- .../http/upload/HttpUploadServerHandler.java | 21 +- .../autobahn/AutobahnServerHandler.java | 2 +- .../server/WebSocketServerHandler.java | 4 +- .../sslserver/WebSocketSslServerHandler.java | 4 +- .../codec/http/DefaultHttpChunkTrailer.java | 122 ++- .../codec/http/DefaultHttpHeaders.java | 488 +++++++++ .../codec/http/DefaultHttpMessage.java | 51 +- .../netty/handler/codec/http/HttpChunk.java | 14 + .../codec/http/HttpChunkAggregator.java | 6 +- .../handler/codec/http/HttpChunkTrailer.java | 41 +- .../handler/codec/http/HttpCodecUtil.java | 10 +- .../codec/http/HttpContentCompressor.java | 2 +- .../codec/http/HttpContentDecoder.java | 10 +- .../codec/http/HttpContentEncoder.java | 10 +- .../netty/handler/codec/http/HttpHeaders.java | 930 ++++++++++++------ .../netty/handler/codec/http/HttpMessage.java | 64 +- .../codec/http/HttpMessageDecoder.java | 14 +- .../codec/http/HttpMessageEncoder.java | 6 +- .../multipart/HttpPostRequestDecoder.java | 4 +- .../multipart/HttpPostRequestEncoder.java | 24 +- .../WebSocketClientHandshaker00.java | 24 +- .../WebSocketClientHandshaker07.java | 28 +- .../WebSocketClientHandshaker08.java | 28 +- .../WebSocketClientHandshaker13.java | 28 +- .../WebSocketServerHandshaker00.java | 30 +- .../WebSocketServerHandshaker07.java | 12 +- .../WebSocketServerHandshaker08.java | 12 +- .../WebSocketServerHandshaker13.java | 12 +- .../WebSocketServerHandshakerFactory.java | 4 +- ...bSocketServerProtocolHandshakeHandler.java | 2 +- .../codec/rtsp/RtspMessageDecoder.java | 2 +- .../codec/spdy/DefaultSpdyHeaders.java | 379 +++++++ .../codec/spdy/DefaultSpdyHeadersFrame.java | 38 +- .../codec/spdy/SpdyHeaderBlockRawDecoder.java | 6 +- .../codec/spdy/SpdyHeaderBlockRawEncoder.java | 23 +- .../netty/handler/codec/spdy/SpdyHeaders.java | 459 +++------ .../handler/codec/spdy/SpdyHeadersFrame.java | 48 +- .../handler/codec/spdy/SpdyHttpDecoder.java | 18 +- .../handler/codec/spdy/SpdyHttpEncoder.java | 38 +- .../handler/codec/spdy/SpdyHttpHeaders.java | 18 +- .../spdy/SpdyHttpResponseStreamIdHandler.java | 4 +- .../codec/http/DefaultHttpMessageTest.java | 8 +- .../codec/http/HttpChunkAggregatorTest.java | 18 +- .../multipart/HttpPostRequestDecoderTest.java | 4 +- .../websocketx/WebSocketRequestBuilder.java | 12 +- .../codec/spdy/SpdyFrameDecoderTest.java | 4 +- .../codec/spdy/SpdySessionHandlerTest.java | 32 +- 55 files changed, 2147 insertions(+), 1073 deletions(-) create mode 100644 src/main/java/org/jboss/netty/handler/codec/http/DefaultHttpHeaders.java create mode 100644 src/main/java/org/jboss/netty/handler/codec/spdy/DefaultSpdyHeaders.java diff --git a/src/main/java/org/jboss/netty/channel/socket/http/HttpTunnelingClientSocketChannel.java b/src/main/java/org/jboss/netty/channel/socket/http/HttpTunnelingClientSocketChannel.java index c46f328bf1..612929527c 100644 --- a/src/main/java/org/jboss/netty/channel/socket/http/HttpTunnelingClientSocketChannel.java +++ b/src/main/java/org/jboss/netty/channel/socket/http/HttpTunnelingClientSocketChannel.java @@ -184,12 +184,12 @@ class HttpTunnelingClientSocketChannel extends AbstractChannel final HttpRequest req = new DefaultHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.POST, serverPath); if (serverName != null) { - req.setHeader(HttpHeaders.Names.HOST, serverName); + req.headers().set(HttpHeaders.Names.HOST, serverName); } - req.setHeader(HttpHeaders.Names.CONTENT_TYPE, "application/octet-stream"); - req.setHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED); - req.setHeader(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING, HttpHeaders.Values.BINARY); - req.setHeader(HttpHeaders.Names.USER_AGENT, HttpTunnelingClientSocketChannel.class.getName()); + req.headers().set(HttpHeaders.Names.CONTENT_TYPE, "application/octet-stream"); + req.headers().set(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED); + req.headers().set(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING, HttpHeaders.Values.BINARY); + req.headers().set(HttpHeaders.Names.USER_AGENT, HttpTunnelingClientSocketChannel.class.getName()); if (sslHandshakeFuture == null) { realChannel.write(req); diff --git a/src/main/java/org/jboss/netty/example/http/file/HttpStaticFileServerHandler.java b/src/main/java/org/jboss/netty/example/http/file/HttpStaticFileServerHandler.java index 4b7698c6f9..f42ad0427e 100644 --- a/src/main/java/org/jboss/netty/example/http/file/HttpStaticFileServerHandler.java +++ b/src/main/java/org/jboss/netty/example/http/file/HttpStaticFileServerHandler.java @@ -131,7 +131,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelUpstreamHandler { } // Cache Validation - String ifModifiedSince = request.getHeader(IF_MODIFIED_SINCE); + String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE); if (ifModifiedSince != null && ifModifiedSince.length() != 0) { SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US); Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince); @@ -239,7 +239,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelUpstreamHandler { private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) { HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status); - response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8"); + response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); response.setContent(ChannelBuffers.copiedBuffer( "Failure: " + status.toString() + "\r\n", CharsetUtil.UTF_8)); @@ -273,7 +273,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelUpstreamHandler { dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE)); Calendar time = new GregorianCalendar(); - response.setHeader(DATE, dateFormatter.format(time.getTime())); + response.headers().set(DATE, dateFormatter.format(time.getTime())); } /** @@ -290,13 +290,13 @@ public class HttpStaticFileServerHandler extends SimpleChannelUpstreamHandler { // Date header Calendar time = new GregorianCalendar(); - response.setHeader(DATE, dateFormatter.format(time.getTime())); + response.headers().set(DATE, dateFormatter.format(time.getTime())); // Add cache headers time.add(Calendar.SECOND, HTTP_CACHE_SECONDS); - response.setHeader(EXPIRES, dateFormatter.format(time.getTime())); - response.setHeader(CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS); - response.setHeader( + response.headers().set(EXPIRES, dateFormatter.format(time.getTime())); + response.headers().set(CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS); + response.headers().set( LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified()))); } @@ -310,7 +310,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelUpstreamHandler { */ private static void setContentTypeHeader(HttpResponse response, File file) { MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap(); - response.setHeader(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath())); + response.headers().set(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath())); } } diff --git a/src/main/java/org/jboss/netty/example/http/helloworld/HttpHelloWorldServerHandler.java b/src/main/java/org/jboss/netty/example/http/helloworld/HttpHelloWorldServerHandler.java index 9ee1e64563..4ef8997e60 100644 --- a/src/main/java/org/jboss/netty/example/http/helloworld/HttpHelloWorldServerHandler.java +++ b/src/main/java/org/jboss/netty/example/http/helloworld/HttpHelloWorldServerHandler.java @@ -51,15 +51,15 @@ public class HttpHelloWorldServerHandler extends SimpleChannelUpstreamHandler { boolean keepAlive = isKeepAlive(req); HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); response.setContent(ChannelBuffers.wrappedBuffer(CONTENT)); - response.setHeader(CONTENT_TYPE, "text/plain"); - response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes()); + response.headers().set(CONTENT_TYPE, "text/plain"); + response.headers().set(CONTENT_LENGTH, response.getContent().readableBytes()); if (!keepAlive) { ChannelFuture f = Channels.future(ch); f.addListener(ChannelFutureListener.CLOSE); Channels.write(ctx, f, response); } else { - response.setHeader(CONNECTION, Values.KEEP_ALIVE); + response.headers().set(CONNECTION, Values.KEEP_ALIVE); Channels.write(ctx, Channels.future(ch), response); } } diff --git a/src/main/java/org/jboss/netty/example/http/snoop/HttpSnoopClient.java b/src/main/java/org/jboss/netty/example/http/snoop/HttpSnoopClient.java index 5b9a22796f..c8f72d795f 100644 --- a/src/main/java/org/jboss/netty/example/http/snoop/HttpSnoopClient.java +++ b/src/main/java/org/jboss/netty/example/http/snoop/HttpSnoopClient.java @@ -84,15 +84,15 @@ public class HttpSnoopClient { // Prepare the HTTP request. HttpRequest request = new DefaultHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath()); - request.setHeader(HttpHeaders.Names.HOST, host); - request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); - request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP); + request.headers().set(HttpHeaders.Names.HOST, host); + request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); + request.headers().set(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP); // Set some example cookies. CookieEncoder httpCookieEncoder = new CookieEncoder(false); httpCookieEncoder.addCookie("my-cookie", "foo"); httpCookieEncoder.addCookie("another-cookie", "bar"); - request.setHeader(HttpHeaders.Names.COOKIE, httpCookieEncoder.encode()); + request.headers().set(HttpHeaders.Names.COOKIE, httpCookieEncoder.encode()); // Send the HTTP request. channel.write(request); diff --git a/src/main/java/org/jboss/netty/example/http/snoop/HttpSnoopClientHandler.java b/src/main/java/org/jboss/netty/example/http/snoop/HttpSnoopClientHandler.java index c4e1cc773e..2a3fc6cc2e 100644 --- a/src/main/java/org/jboss/netty/example/http/snoop/HttpSnoopClientHandler.java +++ b/src/main/java/org/jboss/netty/example/http/snoop/HttpSnoopClientHandler.java @@ -36,9 +36,9 @@ public class HttpSnoopClientHandler extends SimpleChannelUpstreamHandler { System.out.println("VERSION: " + response.getProtocolVersion()); System.out.println(); - if (!response.getHeaderNames().isEmpty()) { - for (String name: response.getHeaderNames()) { - for (String value: response.getHeaders(name)) { + if (!response.headers().names().isEmpty()) { + for (String name: response.headers().names()) { + for (String value: response.headers().getAll(name)) { System.out.println("HEADER: " + name + " = " + value); } } diff --git a/src/main/java/org/jboss/netty/example/http/snoop/HttpSnoopServerHandler.java b/src/main/java/org/jboss/netty/example/http/snoop/HttpSnoopServerHandler.java index 95cbef2d3f..33b8e21b45 100644 --- a/src/main/java/org/jboss/netty/example/http/snoop/HttpSnoopServerHandler.java +++ b/src/main/java/org/jboss/netty/example/http/snoop/HttpSnoopServerHandler.java @@ -69,7 +69,7 @@ public class HttpSnoopServerHandler extends SimpleChannelUpstreamHandler { buf.append("HOSTNAME: " + getHost(request, "unknown") + "\r\n"); buf.append("REQUEST_URI: " + request.getUri() + "\r\n\r\n"); - for (Map.Entry h: request.getHeaders()) { + for (Map.Entry h: request.headers()) { buf.append("HEADER: " + h.getKey() + " = " + h.getValue() + "\r\n"); } buf.append("\r\n"); @@ -103,10 +103,10 @@ public class HttpSnoopServerHandler extends SimpleChannelUpstreamHandler { buf.append("END OF CONTENT\r\n"); HttpChunkTrailer trailer = (HttpChunkTrailer) chunk; - if (!trailer.getHeaderNames().isEmpty()) { + if (!trailer.trailingHeaders().names().isEmpty()) { buf.append("\r\n"); - for (String name: trailer.getHeaderNames()) { - for (String value: trailer.getHeaders(name)) { + for (String name: trailer.trailingHeaders().names()) { + for (String value: trailer.trailingHeaders().getAll(name)) { buf.append("TRAILING HEADER: " + name + " = " + value + "\r\n"); } } @@ -127,18 +127,18 @@ public class HttpSnoopServerHandler extends SimpleChannelUpstreamHandler { // Build the response object. HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); response.setContent(ChannelBuffers.copiedBuffer(buf.toString(), CharsetUtil.UTF_8)); - response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8"); + response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8"); if (keepAlive) { // Add 'Content-Length' header only for a keep-alive connection. - response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes()); + response.headers().set(CONTENT_LENGTH, response.getContent().readableBytes()); // Add keep alive header as per: // - http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01.html#Connection - response.setHeader(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); + response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); } // Encode the cookie. - String cookieString = request.getHeader(COOKIE); + String cookieString = request.headers().get(COOKIE); if (cookieString != null) { CookieDecoder cookieDecoder = new CookieDecoder(); Set cookies = cookieDecoder.decode(cookieString); @@ -147,16 +147,16 @@ public class HttpSnoopServerHandler extends SimpleChannelUpstreamHandler { CookieEncoder cookieEncoder = new CookieEncoder(true); for (Cookie cookie : cookies) { cookieEncoder.addCookie(cookie); - response.addHeader(SET_COOKIE, cookieEncoder.encode()); + response.headers().add(SET_COOKIE, cookieEncoder.encode()); } } } else { // Browser sent no cookie. Add some. CookieEncoder cookieEncoder = new CookieEncoder(true); cookieEncoder.addCookie("key1", "value1"); - response.addHeader(SET_COOKIE, cookieEncoder.encode()); + response.headers().add(SET_COOKIE, cookieEncoder.encode()); cookieEncoder.addCookie("key2", "value2"); - response.addHeader(SET_COOKIE, cookieEncoder.encode()); + response.headers().add(SET_COOKIE, cookieEncoder.encode()); } // Write the response. diff --git a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClient.java b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClient.java index 16f61c1a1a..64f4da6a72 100644 --- a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClient.java +++ b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClient.java @@ -187,27 +187,27 @@ public class HttpUploadClient { HttpRequest request = new DefaultHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.GET, uriGet.toASCIIString()); - request.setHeader(HttpHeaders.Names.HOST, host); - request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); - request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP + ',' + + request.headers().set(HttpHeaders.Names.HOST, host); + request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); + request.headers().set(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP + ',' + HttpHeaders.Values.DEFLATE); - request.setHeader(HttpHeaders.Names.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.7"); - request.setHeader(HttpHeaders.Names.ACCEPT_LANGUAGE, "fr"); - request.setHeader(HttpHeaders.Names.REFERER, uriSimple.toString()); - request.setHeader(HttpHeaders.Names.USER_AGENT, "Netty Simple Http Client side"); - request.setHeader(HttpHeaders.Names.ACCEPT, + request.headers().set(HttpHeaders.Names.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.7"); + request.headers().set(HttpHeaders.Names.ACCEPT_LANGUAGE, "fr"); + request.headers().set(HttpHeaders.Names.REFERER, uriSimple.toString()); + request.headers().set(HttpHeaders.Names.USER_AGENT, "Netty Simple Http Client side"); + request.headers().set(HttpHeaders.Names.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); //connection will not close but needed - // request.setHeader("Connection","keep-alive"); - // request.setHeader("Keep-Alive","300"); + // request.headers().set("Connection","keep-alive"); + // request.headers().set("Keep-Alive","300"); CookieEncoder httpCookieEncoder = new CookieEncoder(false); httpCookieEncoder.addCookie("my-cookie", "foo"); httpCookieEncoder.addCookie("another-cookie", "bar"); - request.setHeader(HttpHeaders.Names.COOKIE, httpCookieEncoder.encode()); + request.headers().set(HttpHeaders.Names.COOKIE, httpCookieEncoder.encode()); - List> headers = request.getHeaders(); + List> headers = request.headers().entries(); // send request channel.write(request); @@ -256,7 +256,7 @@ public class HttpUploadClient { // it is legal to add directly header or cookie into the request until finalize for (Entry entry : headers) { - request.setHeader(entry.getKey(), entry.getValue()); + request.headers().set(entry.getKey(), entry.getValue()); } // add Form attribute @@ -342,7 +342,7 @@ public class HttpUploadClient { // it is legal to add directly header or cookie into the request until finalize for (Entry entry : headers) { - request.setHeader(entry.getKey(), entry.getValue()); + request.headers().set(entry.getKey(), entry.getValue()); } // add Form attribute from previous request in formpost() diff --git a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClientHandler.java b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClientHandler.java index 6d248f755a..6576dbfeca 100644 --- a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClientHandler.java +++ b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadClientHandler.java @@ -41,9 +41,9 @@ public class HttpUploadClientHandler extends SimpleChannelUpstreamHandler { logger.info("STATUS: " + response.getStatus()); logger.info("VERSION: " + response.getProtocolVersion()); - if (!response.getHeaderNames().isEmpty()) { - for (String name: response.getHeaderNames()) { - for (String value: response.getHeaders(name)) { + if (!response.headers().names().isEmpty()) { + for (String name: response.headers().names()) { + for (String value: response.headers().getAll(name)) { logger.info("HEADER: " + name + " = " + value); } } diff --git a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadServerHandler.java b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadServerHandler.java index 4541bd8040..58a0eaa488 100644 --- a/src/main/java/org/jboss/netty/example/http/upload/HttpUploadServerHandler.java +++ b/src/main/java/org/jboss/netty/example/http/upload/HttpUploadServerHandler.java @@ -123,8 +123,7 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { responseContent.append("\r\n\r\n"); // new method - List> headers = request.getHeaders(); - for (Entry entry: headers) { + for (Entry entry: request.headers()) { responseContent.append("HEADER: " + entry.getKey() + '=' + entry.getValue() + "\r\n"); } @@ -132,7 +131,7 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { // new method Set cookies; - String value = request.getHeader(HttpHeaders.Names.COOKIE); + String value = request.headers().get(HttpHeaders.Names.COOKIE); if (value == null) { cookies = Collections.emptySet(); } else { @@ -320,27 +319,27 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { // Decide whether to close the connection or not. boolean close = HttpHeaders.Values.CLOSE.equalsIgnoreCase(request - .getHeader(HttpHeaders.Names.CONNECTION)) || + .headers().get(HttpHeaders.Names.CONNECTION)) || request.getProtocolVersion().equals(HttpVersion.HTTP_1_0) && !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request - .getHeader(HttpHeaders.Names.CONNECTION)); + .headers().get(HttpHeaders.Names.CONNECTION)); // Build the response object. HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); response.setContent(buf); - response.setHeader(HttpHeaders.Names.CONTENT_TYPE, + response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=UTF-8"); if (!close) { // There's no need to add 'Content-Length' header // if this is the last response. - response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String + response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, String .valueOf(buf.readableBytes())); } Set cookies; - String value = request.getHeader(HttpHeaders.Names.COOKIE); + String value = request.headers().get(HttpHeaders.Names.COOKIE); if (value == null) { cookies = Collections.emptySet(); } else { @@ -352,7 +351,7 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { CookieEncoder cookieEncoder = new CookieEncoder(true); for (Cookie cookie: cookies) { cookieEncoder.addCookie(cookie); - response.addHeader(HttpHeaders.Names.SET_COOKIE, cookieEncoder + response.headers().add(HttpHeaders.Names.SET_COOKIE, cookieEncoder .encode()); cookieEncoder = new CookieEncoder(true); } @@ -468,9 +467,9 @@ public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); response.setContent(buf); - response.setHeader(HttpHeaders.Names.CONTENT_TYPE, + response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/html; charset=UTF-8"); - response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buf + response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buf .readableBytes())); // Write the response. e.getChannel().write(response); diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/autobahn/AutobahnServerHandler.java b/src/main/java/org/jboss/netty/example/http/websocketx/autobahn/AutobahnServerHandler.java index 1ec48f6b38..d7825d41a9 100644 --- a/src/main/java/org/jboss/netty/example/http/websocketx/autobahn/AutobahnServerHandler.java +++ b/src/main/java/org/jboss/netty/example/http/websocketx/autobahn/AutobahnServerHandler.java @@ -130,6 +130,6 @@ public class AutobahnServerHandler extends SimpleChannelUpstreamHandler { } private static String getWebSocketLocation(HttpRequest req) { - return "ws://" + req.getHeader(HttpHeaders.Names.HOST); + return "ws://" + req.headers().get(HttpHeaders.Names.HOST); } } diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/server/WebSocketServerHandler.java b/src/main/java/org/jboss/netty/example/http/websocketx/server/WebSocketServerHandler.java index 8f7f7af51f..0ebcf3114c 100644 --- a/src/main/java/org/jboss/netty/example/http/websocketx/server/WebSocketServerHandler.java +++ b/src/main/java/org/jboss/netty/example/http/websocketx/server/WebSocketServerHandler.java @@ -76,7 +76,7 @@ public class WebSocketServerHandler extends SimpleChannelUpstreamHandler { ChannelBuffer content = WebSocketServerIndexPage.getContent(getWebSocketLocation(req)); - res.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8"); + res.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8"); setContentLength(res, content.readableBytes()); res.setContent(content); @@ -145,6 +145,6 @@ public class WebSocketServerHandler extends SimpleChannelUpstreamHandler { } private static String getWebSocketLocation(HttpRequest req) { - return "ws://" + req.getHeader(HOST) + WEBSOCKET_PATH; + return "ws://" + req.headers().get(HOST) + WEBSOCKET_PATH; } } diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerHandler.java b/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerHandler.java index 47949f6dad..9c4fcd2642 100644 --- a/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerHandler.java +++ b/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerHandler.java @@ -77,7 +77,7 @@ public class WebSocketSslServerHandler extends SimpleChannelUpstreamHandler { ChannelBuffer content = WebSocketServerIndexPage.getContent(getWebSocketLocation(req)); - res.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8"); + res.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8"); setContentLength(res, content.readableBytes()); res.setContent(content); @@ -147,6 +147,6 @@ public class WebSocketSslServerHandler extends SimpleChannelUpstreamHandler { } private static String getWebSocketLocation(HttpRequest req) { - return "wss://" + req.getHeader(HOST) + WEBSOCKET_PATH; + return "wss://" + req.headers().get(HOST) + WEBSOCKET_PATH; } } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/DefaultHttpChunkTrailer.java b/src/main/java/org/jboss/netty/handler/codec/http/DefaultHttpChunkTrailer.java index 6229dc4dda..12eed7bd96 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/DefaultHttpChunkTrailer.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/DefaultHttpChunkTrailer.java @@ -15,73 +15,73 @@ */ package org.jboss.netty.handler.codec.http; +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.util.internal.StringUtil; + import java.util.List; import java.util.Map; import java.util.Set; -import org.jboss.netty.buffer.ChannelBuffer; -import org.jboss.netty.buffer.ChannelBuffers; - /** * The default {@link HttpChunkTrailer} implementation. */ public class DefaultHttpChunkTrailer implements HttpChunkTrailer { - private final HttpHeaders headers = new HttpHeaders() { - @Override - void validateHeaderName(String name) { - super.validateHeaderName(name); - if (name.equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH) || - name.equalsIgnoreCase(HttpHeaders.Names.TRANSFER_ENCODING) || - name.equalsIgnoreCase(HttpHeaders.Names.TRAILER)) { - throw new IllegalArgumentException( - "prohibited trailing header: " + name); - } - } - }; + private final HttpHeaders trailingHeaders = new TrailingHeaders(true); public boolean isLast() { return true; } + @Deprecated public void addHeader(final String name, final Object value) { - headers.addHeader(name, value); + trailingHeaders.add(name, value); } + @Deprecated public void setHeader(final String name, final Object value) { - headers.setHeader(name, value); + trailingHeaders.set(name, value); } + @Deprecated public void setHeader(final String name, final Iterable values) { - headers.setHeader(name, values); + trailingHeaders.set(name, values); } + @Deprecated public void removeHeader(final String name) { - headers.removeHeader(name); + trailingHeaders.remove(name); } + @Deprecated public void clearHeaders() { - headers.clearHeaders(); + trailingHeaders.clear(); } + @Deprecated public String getHeader(final String name) { - return headers.getHeader(name); + return trailingHeaders.get(name); } + @Deprecated public List getHeaders(final String name) { - return headers.getHeaders(name); + return trailingHeaders.getAll(name); } + @Deprecated public List> getHeaders() { - return headers.getHeaders(); + return trailingHeaders.entries(); } + @Deprecated public boolean containsHeader(final String name) { - return headers.containsHeader(name); + return trailingHeaders.contains(name); } + @Deprecated public Set getHeaderNames() { - return headers.getHeaderNames(); + return trailingHeaders.names(); } public ChannelBuffer getContent() { @@ -91,4 +91,76 @@ public class DefaultHttpChunkTrailer implements HttpChunkTrailer { public void setContent(ChannelBuffer content) { throw new IllegalStateException("read-only"); } + + public HttpHeaders trailingHeaders() { + return trailingHeaders; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(super.toString()); + buf.append(StringUtil.NEWLINE); + appendHeaders(buf); + + // Remove the last newline. + buf.setLength(buf.length() - StringUtil.NEWLINE.length()); + return buf.toString(); + } + + private void appendHeaders(StringBuilder buf) { + for (Map.Entry e: trailingHeaders()) { + buf.append(e.getKey()); + buf.append(": "); + buf.append(e.getValue()); + buf.append(StringUtil.NEWLINE); + } + } + + private static final class TrailingHeaders extends DefaultHttpHeaders { + + TrailingHeaders(boolean validateHeaders) { + super(validateHeaders); + } + + @Override + public HttpHeaders add(String name, Object value) { + if (validate) { + validateName(name); + } + return super.add(name, value); + } + + @Override + public HttpHeaders add(String name, Iterable values) { + if (validate) { + validateName(name); + } + return super.add(name, values); + } + + @Override + public HttpHeaders set(String name, Iterable values) { + if (validate) { + validateName(name); + } + return super.set(name, values); + } + + @Override + public HttpHeaders set(String name, Object value) { + if (validate) { + validateName(name); + } + return super.set(name, value); + } + + private static void validateName(String name) { + if (name.equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH) || + name.equalsIgnoreCase(HttpHeaders.Names.TRANSFER_ENCODING) || + name.equalsIgnoreCase(HttpHeaders.Names.TRAILER)) { + throw new IllegalArgumentException( + "prohibited trailing header: " + name); + } + } + } } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/DefaultHttpHeaders.java b/src/main/java/org/jboss/netty/handler/codec/http/DefaultHttpHeaders.java new file mode 100644 index 0000000000..759378e5f1 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/codec/http/DefaultHttpHeaders.java @@ -0,0 +1,488 @@ +/* + * Copyright 2012 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 org.jboss.netty.handler.codec.http; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +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; + +public class DefaultHttpHeaders extends HttpHeaders { + + private static final int BUCKET_SIZE = 17; + + private static final Set KNOWN_NAMES = createSet(Names.class); + private static final Set KNOWN_VALUES = createSet(Values.class); + + private static Set createSet(Class clazz) { + Set set = new HashSet(); + Field[] fields = clazz.getDeclaredFields(); + + for (Field f: fields) { + int m = f.getModifiers(); + if (Modifier.isPublic(m) && Modifier.isStatic(m) && Modifier.isFinal(m) + && f.getType().isAssignableFrom(String.class)) { + try { + set.add((String) f.get(null)); + } catch (Throwable cause) { + // ignore + } + } + } + return set; + } + + private static int hash(String name, boolean validate) { + int h = 0; + for (int i = name.length() - 1; i >= 0; i --) { + char c = name.charAt(i); + if (validate) { + valideHeaderNameChar(c); + } + c = toLowerCase(c); + h = 31 * h + c; + } + + if (h > 0) { + return h; + } else if (h == Integer.MIN_VALUE) { + return Integer.MAX_VALUE; + } else { + return -h; + } + } + + private static boolean eq(String name1, String name2) { + if (name1 == name2) { + // check for object equality as the user may reuse our static fields in HttpHeaders.Names + return true; + } + 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 (toLowerCase(c1) != toLowerCase(c2)) { + return false; + } + } + } + return true; + } + + private static char toLowerCase(char c) { + if (c >= 'A' && c <= 'Z') { + c += 32; + } + return c; + } + + private static int index(int hash) { + return hash % BUCKET_SIZE; + } + + private final HeaderEntry[] entries = new HeaderEntry[BUCKET_SIZE]; + private final HeaderEntry head = new HeaderEntry(-1, null, null); + protected final boolean validate; + + public DefaultHttpHeaders() { + this(true); + } + + public DefaultHttpHeaders(boolean validate) { + head.before = head.after = head; + this.validate = validate; + } + + void validateHeaderValue0(String headerValue) { + if (KNOWN_VALUES.contains(headerValue)) { + return; + } + validateHeaderValue(headerValue); + } + + @Override + public HttpHeaders add(final String name, final Object value) { + String strVal = toString(value); + boolean validateName = false; + if (validate) { + validateHeaderValue0(strVal); + validateName = !KNOWN_NAMES.contains(name); + } + + int h = hash(name, validateName); + int i = index(h); + add0(h, i, name, strVal); + return this; + } + + @Override + public HttpHeaders add(String name, Iterable values) { + boolean validateName = false; + if (validate) { + validateName = !KNOWN_NAMES.contains(name); + } + + int h = hash(name, validateName); + int i = index(h); + for (Object v: values) { + String vstr = toString(v); + if (validate) { + validateHeaderValue0(vstr); + } + add0(h, i, name, vstr); + } + return this; + } + + private void add0(int h, int i, final String name, final String 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); + } + + @Override + public HttpHeaders remove(final String name) { + if (name == null) { + throw new NullPointerException("name"); + } + int h = hash(name, false); + int i = index(h); + remove0(h, i, name); + return this; + } + + private void remove0(int h, int i, String name) { + HeaderEntry e = entries[i]; + if (e == null) { + return; + } + + for (;;) { + if (e.hash == h && eq(name, e.key)) { + e.remove(); + HeaderEntry next = e.next; + if (next != null) { + entries[i] = next; + e = next; + } else { + entries[i] = null; + return; + } + } else { + break; + } + } + + for (;;) { + HeaderEntry next = e.next; + if (next == null) { + break; + } + if (next.hash == h && eq(name, next.key)) { + e.next = next.next; + next.remove(); + } else { + e = next; + } + } + } + + @Override + public HttpHeaders set(final String name, final Object value) { + String strVal = toString(value); + boolean validateName = false; + if (validate) { + validateHeaderValue0(strVal); + validateName = !KNOWN_NAMES.contains(name); + } + + int h = hash(name, validateName); + int i = index(h); + remove0(h, i, name); + add0(h, i, name, strVal); + return this; + } + + @Override + public HttpHeaders set(final String name, final Iterable values) { + if (values == null) { + throw new NullPointerException("values"); + } + + boolean validateName = false; + if (validate) { + validateName = !KNOWN_NAMES.contains(name); + } + + int h = hash(name, validateName); + int i = index(h); + + remove0(h, i, name); + for (Object v: values) { + if (v == null) { + break; + } + String strVal = toString(v); + if (validate) { + validateHeaderValue0(strVal); + } + add0(h, i, name, strVal); + } + + return this; + } + + @Override + public HttpHeaders clear() { + Arrays.fill(entries, null); + head.before = head.after = head; + return this; + } + + @Override + public String get(final String name) { + return get(name, false); + } + + private String get(final String name, boolean last) { + if (name == null) { + throw new NullPointerException("name"); + } + + int h = hash(name, false); + int i = index(h); + HeaderEntry e = entries[i]; + String value = null; + // loop until the first header was found + while (e != null) { + if (e.hash == h && eq(name, e.key)) { + value = e.value; + if (last) { + break; + } + } + + e = e.next; + } + return value; + } + + @Override + public List getAll(final String name) { + if (name == null) { + throw new NullPointerException("name"); + } + + LinkedList values = new LinkedList(); + + int h = hash(name, false); + int i = index(h); + HeaderEntry e = entries[i]; + while (e != null) { + if (e.hash == h && eq(name, e.key)) { + values.addFirst(e.value); + } + e = e.next; + } + return values; + } + + @Override + public List> entries() { + List> all = + new LinkedList>(); + + HeaderEntry e = head.after; + while (e != head) { + all.add(e); + e = e.after; + } + return all; + } + + @Override + public Iterator> iterator() { + return new HeaderIterator(); + } + + @Override + public boolean contains(String name) { + return get(name, true) != null; + } + + @Override + public boolean isEmpty() { + return head == head.after; + } + + @Override + public boolean contains(String name, String value, boolean ignoreCaseValue) { + if (name == null) { + throw new NullPointerException("name"); + } + + int h = hash(name, false); + int i = index(h); + HeaderEntry e = entries[i]; + while (e != null) { + if (e.hash == h && eq(name, e.key)) { + if (ignoreCaseValue) { + if (e.value.equalsIgnoreCase(value)) { + return true; + } + } else { + if (e.value.equals(value)) { + return true; + } + } + } + e = e.next; + } + return false; + } + + @Override + public Set names() { + + Set names = new TreeSet(String.CASE_INSENSITIVE_ORDER); + + HeaderEntry e = head.after; + while (e != head) { + names.add(e.key); + e = e.after; + } + return names; + } + + private static String toString(Object value) { + if (value == null) { + return null; + } + if (value instanceof String) { + return (String) value; + } + if (value instanceof Number) { + return value.toString(); + } + if (value instanceof Date) { + return HttpHeaderDateFormat.get().format((Date) value); + } + if (value instanceof Calendar) { + return HttpHeaderDateFormat.get().format(((Calendar) value).getTime()); + } + return value.toString(); + } + + private 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(); + } + } + + private final class HeaderEntry implements Map.Entry { + final int hash; + final String key; + String value; + HeaderEntry next; + HeaderEntry before, after; + + HeaderEntry(int hash, String key, String value) { + this.hash = hash; + this.key = key; + this.value = value; + } + + void remove() { + before.after = after; + after.before = before; + } + + void addBefore(HeaderEntry e) { + after = e; + before = e.before; + before.after = this; + after.before = this; + } + + @Override + public String getKey() { + return key; + } + + @Override + public String getValue() { + return value; + } + + @Override + public String setValue(String value) { + if (value == null) { + throw new NullPointerException("value"); + } + if (validate) { + validateHeaderValue0(value); + } + String oldValue = this.value; + this.value = value; + return oldValue; + } + + @Override + public String toString() { + return key + '=' + value; + } + } +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/DefaultHttpMessage.java b/src/main/java/org/jboss/netty/handler/codec/http/DefaultHttpMessage.java index 967acb895c..940769fe7b 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/DefaultHttpMessage.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/DefaultHttpMessage.java @@ -28,7 +28,7 @@ import java.util.Set; */ public class DefaultHttpMessage implements HttpMessage { - private final HttpHeaders headers = new HttpHeaders(); + private final HttpHeaders headers = new DefaultHttpHeaders(true); private HttpVersion version; private ChannelBuffer content = ChannelBuffers.EMPTY_BUFFER; private boolean chunked; @@ -40,30 +40,28 @@ public class DefaultHttpMessage implements HttpMessage { setProtocolVersion(version); } + public HttpHeaders headers() { + return headers; + } + + @Deprecated public void addHeader(final String name, final Object value) { - headers.addHeader(name, value); + headers.add(name, value); } + @Deprecated public void setHeader(final String name, final Object value) { - headers.setHeader(name, value); + headers.set(name, value); } + @Deprecated public void setHeader(final String name, final Iterable values) { - headers.setHeader(name, values); + headers.set(name, values); } + @Deprecated public void removeHeader(final String name) { - headers.removeHeader(name); - } - - @Deprecated - public long getContentLength() { - return HttpHeaders.getContentLength(this); - } - - @Deprecated - public long getContentLength(long defaultValue) { - return HttpHeaders.getContentLength(this, defaultValue); + headers.remove(name); } public boolean isChunked() { @@ -82,12 +80,8 @@ public class DefaultHttpMessage implements HttpMessage { } @Deprecated - public boolean isKeepAlive() { - return HttpHeaders.isKeepAlive(this); - } - public void clearHeaders() { - headers.clearHeaders(); + headers.clear(); } public void setContent(ChannelBuffer content) { @@ -101,24 +95,29 @@ public class DefaultHttpMessage implements HttpMessage { this.content = content; } + @Deprecated public String getHeader(final String name) { - return headers.getHeader(name); + return headers.get(name); } + @Deprecated public List getHeaders(final String name) { - return headers.getHeaders(name); + return headers.getAll(name); } + @Deprecated public List> getHeaders() { - return headers.getHeaders(); + return headers.entries(); } + @Deprecated public boolean containsHeader(final String name) { - return headers.containsHeader(name); + return headers.contains(name); } + @Deprecated public Set getHeaderNames() { - return headers.getHeaderNames(); + return headers.names(); } public HttpVersion getProtocolVersion() { @@ -156,7 +155,7 @@ public class DefaultHttpMessage implements HttpMessage { } void appendHeaders(StringBuilder buf) { - for (Map.Entry e: getHeaders()) { + for (Map.Entry e: headers()) { buf.append(e.getKey()); buf.append(": "); buf.append(e.getValue()); diff --git a/src/main/java/org/jboss/netty/handler/codec/http/HttpChunk.java b/src/main/java/org/jboss/netty/handler/codec/http/HttpChunk.java index 7b997d3389..5fcaf43143 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/HttpChunk.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/HttpChunk.java @@ -51,45 +51,59 @@ public interface HttpChunk { return true; } + @Deprecated public void addHeader(String name, Object value) { throw new IllegalStateException("read-only"); } + @Deprecated public void clearHeaders() { // NOOP } + @Deprecated public boolean containsHeader(String name) { return false; } + @Deprecated public String getHeader(String name) { return null; } + @Deprecated public Set getHeaderNames() { return Collections.emptySet(); } + @Deprecated public List getHeaders(String name) { return Collections.emptyList(); } + @Deprecated public List> getHeaders() { return Collections.emptyList(); } + @Deprecated public void removeHeader(String name) { // NOOP } + @Deprecated public void setHeader(String name, Object value) { throw new IllegalStateException("read-only"); } + @Deprecated public void setHeader(String name, Iterable values) { throw new IllegalStateException("read-only"); } + + public HttpHeaders trailingHeaders() { + return HttpHeaders.EMPTY_HEADERS; + } }; /** diff --git a/src/main/java/org/jboss/netty/handler/codec/http/HttpChunkAggregator.java b/src/main/java/org/jboss/netty/handler/codec/http/HttpChunkAggregator.java index c4561aae17..9e307b20c5 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/HttpChunkAggregator.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/HttpChunkAggregator.java @@ -180,13 +180,13 @@ public class HttpChunkAggregator extends SimpleChannelUpstreamHandler implements // Merge trailing headers into the message. if (chunk instanceof HttpChunkTrailer) { HttpChunkTrailer trailer = (HttpChunkTrailer) chunk; - for (Entry header: trailer.getHeaders()) { - currentMessage.setHeader(header.getKey(), header.getValue()); + for (Entry header: trailer.trailingHeaders()) { + currentMessage.headers().set(header.getKey(), header.getValue()); } } // Set the 'Content-Length' header. - currentMessage.setHeader( + currentMessage.headers().set( HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(content.readableBytes())); diff --git a/src/main/java/org/jboss/netty/handler/codec/http/HttpChunkTrailer.java b/src/main/java/org/jboss/netty/handler/codec/http/HttpChunkTrailer.java index 04c10bf35b..01ca885187 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/HttpChunkTrailer.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/HttpChunkTrailer.java @@ -30,68 +30,57 @@ public interface HttpChunkTrailer extends HttpChunk { boolean isLast(); /** - * Returns the trailing header value with the specified header name. - * If there are more than one trailing header value for the specified - * header name, the first value is returned. - * - * @return the header value or {@code null} if there is no such header + * @deprecated Use {@link HttpChunkTrailer#trailingHeaders()} instead. */ String getHeader(String name); /** - * Returns the trailing header values with the specified header name. - * - * @return the {@link List} of header values. An empty list if there is no - * such header. + * @deprecated Use {@link HttpChunkTrailer#trailingHeaders()} instead. */ List getHeaders(String name); /** - * Returns the all header names and values that this trailer contains. - * - * @return the {@link List} of the header name-value pairs. An empty list - * if there is no header in this trailer. + * @deprecated Use {@link HttpChunkTrailer#trailingHeaders()} instead. */ List> getHeaders(); /** - * Returns {@code true} if and only if there is a trailing header with - * the specified header name. + * @deprecated Use {@link HttpChunkTrailer#trailingHeaders()} instead. */ boolean containsHeader(String name); /** - * Returns the {@link Set} of all trailing header names that this trailer - * contains. + * @deprecated Use {@link HttpChunkTrailer#trailingHeaders()} instead. */ Set getHeaderNames(); /** - * Adds a new trailing header with the specified name and value. + * @deprecated Use {@link HttpChunkTrailer#trailingHeaders()} instead. */ void addHeader(String name, Object value); /** - * Sets a new trailing header with the specified name and value. - * If there is an existing trailing header with the same name, the existing - * one is removed. + * @deprecated Use {@link HttpChunkTrailer#trailingHeaders()} instead. */ void setHeader(String name, Object value); /** - * Sets a new trailing header with the specified name and values. - * If there is an existing trailing header with the same name, the existing - * one is removed. + * @deprecated Use {@link HttpChunkTrailer#trailingHeaders()} instead. */ void setHeader(String name, Iterable values); /** - * Removes the trailing header with the specified name. + * @deprecated Use {@link HttpChunkTrailer#trailingHeaders()} instead. */ void removeHeader(String name); /** - * Removes all trailing headers from this trailer. + * @deprecated Use {@link HttpChunkTrailer#trailingHeaders()} instead. */ void clearHeaders(); + + /** + * Returns the trialing headers of this trailer. + */ + HttpHeaders trailingHeaders(); } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/HttpCodecUtil.java b/src/main/java/org/jboss/netty/handler/codec/http/HttpCodecUtil.java index 109413aeff..06fc15333f 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/HttpCodecUtil.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/HttpCodecUtil.java @@ -106,7 +106,7 @@ final class HttpCodecUtil { } static boolean isTransferEncodingChunked(HttpMessage m) { - List chunked = m.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING); + List chunked = m.headers().getAll(HttpHeaders.Names.TRANSFER_ENCODING); if (chunked.isEmpty()) { return false; } @@ -120,7 +120,7 @@ final class HttpCodecUtil { } static void removeTransferEncodingChunked(HttpMessage m) { - List values = m.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING); + List values = m.headers().getAll(HttpHeaders.Names.TRANSFER_ENCODING); if (values.isEmpty()) { return; } @@ -132,14 +132,14 @@ final class HttpCodecUtil { } } if (values.isEmpty()) { - m.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING); + m.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING); } else { - m.setHeader(HttpHeaders.Names.TRANSFER_ENCODING, values); + m.headers().set(HttpHeaders.Names.TRANSFER_ENCODING, values); } } static boolean isContentLengthSet(HttpMessage m) { - List contentLength = m.getHeaders(HttpHeaders.Names.CONTENT_LENGTH); + List contentLength = m.headers().getAll(HttpHeaders.Names.CONTENT_LENGTH); return !contentLength.isEmpty(); } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/HttpContentCompressor.java b/src/main/java/org/jboss/netty/handler/codec/http/HttpContentCompressor.java index 4a98e54c2f..a06b3ba58b 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/HttpContentCompressor.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/HttpContentCompressor.java @@ -97,7 +97,7 @@ public class HttpContentCompressor extends HttpContentEncoder { @Override protected EncoderEmbedder newContentEncoder( HttpMessage msg, String acceptEncoding) throws Exception { - String contentEncoding = msg.getHeader(HttpHeaders.Names.CONTENT_ENCODING); + String contentEncoding = msg.headers().get(HttpHeaders.Names.CONTENT_ENCODING); if (contentEncoding != null && !HttpHeaders.Values.IDENTITY.equalsIgnoreCase(contentEncoding)) { // Encoded already. diff --git a/src/main/java/org/jboss/netty/handler/codec/http/HttpContentDecoder.java b/src/main/java/org/jboss/netty/handler/codec/http/HttpContentDecoder.java index 410d5458c2..0c89a0f1b0 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/HttpContentDecoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/HttpContentDecoder.java @@ -68,7 +68,7 @@ public abstract class HttpContentDecoder extends SimpleChannelUpstreamHandler finishDecode(); // Determine the content encoding. - String contentEncoding = m.getHeader(HttpHeaders.Names.CONTENT_ENCODING); + String contentEncoding = m.headers().get(HttpHeaders.Names.CONTENT_ENCODING); if (contentEncoding != null) { contentEncoding = contentEncoding.trim(); } else { @@ -83,9 +83,9 @@ public abstract class HttpContentDecoder extends SimpleChannelUpstreamHandler if (HttpHeaders.Values.IDENTITY.equals(targetContentEncoding)) { // Do NOT set the 'Content-Encoding' header if the target encoding is 'identity' // as per: http://tools.ietf.org/html/rfc2616#section-14.11 - m.removeHeader(HttpHeaders.Names.CONTENT_ENCODING); + m.headers().remove(HttpHeaders.Names.CONTENT_ENCODING); } else { - m.setHeader(HttpHeaders.Names.CONTENT_ENCODING, targetContentEncoding); + m.headers().set(HttpHeaders.Names.CONTENT_ENCODING, targetContentEncoding); } if (!m.isChunked()) { @@ -96,8 +96,8 @@ public abstract class HttpContentDecoder extends SimpleChannelUpstreamHandler // Replace the content. m.setContent(content); - if (m.containsHeader(HttpHeaders.Names.CONTENT_LENGTH)) { - m.setHeader( + if (m.headers().contains(HttpHeaders.Names.CONTENT_LENGTH)) { + m.headers().set( HttpHeaders.Names.CONTENT_LENGTH, Integer.toString(content.readableBytes())); } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/HttpContentEncoder.java b/src/main/java/org/jboss/netty/handler/codec/http/HttpContentEncoder.java index ef2f3a18d4..77c1a87934 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/HttpContentEncoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/HttpContentEncoder.java @@ -71,7 +71,7 @@ public abstract class HttpContentEncoder extends SimpleChannelHandler } HttpMessage m = (HttpMessage) msg; - String acceptedEncoding = m.getHeader(HttpHeaders.Names.ACCEPT_ENCODING); + String acceptedEncoding = m.headers().get(HttpHeaders.Names.ACCEPT_ENCODING); if (acceptedEncoding == null) { acceptedEncoding = HttpHeaders.Values.IDENTITY; } @@ -100,7 +100,7 @@ public abstract class HttpContentEncoder extends SimpleChannelHandler throw new IllegalStateException("cannot send more responses than requests"); } - String contentEncoding = m.getHeader(HttpHeaders.Names.CONTENT_ENCODING); + String contentEncoding = m.headers().get(HttpHeaders.Names.CONTENT_ENCODING); if (contentEncoding != null && !HttpHeaders.Values.IDENTITY.equalsIgnoreCase(contentEncoding)) { // Content-Encoding is set already and it is not 'identity'. @@ -111,7 +111,7 @@ public abstract class HttpContentEncoder extends SimpleChannelHandler if (hasContent && (encoder = newContentEncoder(m, acceptEncoding)) != null) { // Encode the content and remove or replace the existing headers // so that the message looks like a decoded message. - m.setHeader( + m.headers().set( HttpHeaders.Names.CONTENT_ENCODING, getTargetContentEncoding(acceptEncoding)); @@ -123,8 +123,8 @@ public abstract class HttpContentEncoder extends SimpleChannelHandler // Replace the content. m.setContent(content); - if (m.containsHeader(HttpHeaders.Names.CONTENT_LENGTH)) { - m.setHeader( + if (m.headers().contains(HttpHeaders.Names.CONTENT_LENGTH)) { + m.headers().set( HttpHeaders.Names.CONTENT_LENGTH, Integer.toString(content.readableBytes())); } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/HttpHeaders.java b/src/main/java/org/jboss/netty/handler/codec/http/HttpHeaders.java index 6edd73251d..0ab187cf1a 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/HttpHeaders.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/HttpHeaders.java @@ -15,29 +15,92 @@ */ package org.jboss.netty.handler.codec.http; -import org.jboss.netty.util.internal.CaseIgnoringComparator; - -import java.util.LinkedList; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +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; /** * Provides the constants for the standard HTTP header names and values and * commonly used utility methods that accesses an {@link HttpMessage}. - * @apiviz.landmark - * @apiviz.stereotype static */ -public class HttpHeaders { +public abstract class HttpHeaders implements Iterable> { + + public static final HttpHeaders EMPTY_HEADERS = new HttpHeaders() { + @Override + public String get(String name) { + return null; + } + + @Override + public List getAll(String name) { + return Collections.emptyList(); + } + + @Override + public List> entries() { + return Collections.emptyList(); + } + + @Override + public boolean contains(String name) { + return false; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public Set names() { + return Collections.emptySet(); + } + + @Override + public HttpHeaders add(String name, Object value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public HttpHeaders add(String name, Iterable values) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public HttpHeaders set(String name, Object value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public HttpHeaders set(String name, Iterable values) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public HttpHeaders remove(String name) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public HttpHeaders clear() { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Iterator> iterator() { + return entries().iterator(); + } + }; /** - * Standard and CORS HTTP header names. - * For CORS headers, see - * https://developer.mozilla.org/en-US/docs/HTTP_access_control - * - * @apiviz.stereotype static + * Standard HTTP header names. */ public static final class Names { /** @@ -339,7 +402,6 @@ public class HttpHeaders { /** * Standard HTTP header values. - * @apiviz.stereotype static */ public static final class Values { /** @@ -482,13 +544,15 @@ public class HttpHeaders { * {@link HttpVersion#isKeepAliveDefault()}. */ public static boolean isKeepAlive(HttpMessage message) { - String connection = message.getHeader(Names.CONNECTION); - if (Values.CLOSE.equalsIgnoreCase(connection)) { + String connection = message.headers().get(Names.CONNECTION); + + boolean close = Values.CLOSE.equalsIgnoreCase(connection); + if (close) { return false; } if (message.getProtocolVersion().isKeepAliveDefault()) { - return !Values.CLOSE.equalsIgnoreCase(connection); + return !close; } else { return Values.KEEP_ALIVE.equalsIgnoreCase(connection); } @@ -514,17 +578,18 @@ public class HttpHeaders { * */ public static void setKeepAlive(HttpMessage message, boolean keepAlive) { + HttpHeaders h = message.headers(); if (message.getProtocolVersion().isKeepAliveDefault()) { if (keepAlive) { - message.removeHeader(Names.CONNECTION); + h.remove(Names.CONNECTION); } else { - message.setHeader(Names.CONNECTION, Values.CLOSE); + h.set(Names.CONNECTION, Values.CLOSE); } } else { if (keepAlive) { - message.setHeader(Names.CONNECTION, Values.KEEP_ALIVE); + h.set(Names.CONNECTION, Values.KEEP_ALIVE); } else { - message.removeHeader(Names.CONNECTION); + h.remove(Names.CONNECTION); } } } @@ -537,7 +602,7 @@ public class HttpHeaders { * @return the header value or {@code null} if there is no such header */ public static String getHeader(HttpMessage message, String name) { - return message.getHeader(name); + return message.headers().get(name); } /** @@ -549,7 +614,7 @@ public class HttpHeaders { * header */ public static String getHeader(HttpMessage message, String name, String defaultValue) { - String value = message.getHeader(name); + String value = message.headers().get(name); if (value == null) { return defaultValue; } @@ -559,24 +624,56 @@ public class HttpHeaders { /** * Sets a new header with the specified name and value. If there is an * existing header with the same name, the existing header 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 Date} + * and {@link Calendar} which are formatted to the date format defined in + * RFC2616. */ public static void setHeader(HttpMessage message, String name, Object value) { - message.setHeader(name, value); + message.headers().set(name, value); } /** * Sets a new header with the specified name and values. If there is an * existing header with the same name, the existing header is removed. + * This getMethod can be represented approximately as the following code: + *
+     * removeHeader(message, name);
+     * for (Object v: values) {
+     *     if (v == null) {
+     *         break;
+     *     }
+     *     addHeader(message, name, v);
+     * }
+     * 
*/ public static void setHeader(HttpMessage message, String name, Iterable values) { - message.setHeader(name, values); + message.headers().set(name, 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 for {@link Date} + * and {@link Calendar} which are formatted to the date format defined in + * RFC2616. */ public static void addHeader(HttpMessage message, String name, Object value) { - message.addHeader(name, value); + message.headers().add(name, value); + } + + /** + * Removes the header with the specified name. + */ + public static void removeHeader(HttpMessage message, String name) { + message.headers().remove(name); + } + + /** + * Removes all headers from the specified message. + */ + public static void clearHeaders(HttpMessage message) { + message.headers().clear(); } /** @@ -591,7 +688,7 @@ public class HttpHeaders { public static int getIntHeader(HttpMessage message, String name) { String value = getHeader(message, name); if (value == null) { - throw new NumberFormatException("null"); + throw new NumberFormatException("header not found: " + name); } return Integer.parseInt(value); } @@ -622,7 +719,7 @@ public class HttpHeaders { * is an existing header with the same name, the existing header is removed. */ public static void setIntHeader(HttpMessage message, String name, int value) { - message.setHeader(name, value); + message.headers().set(name, value); } /** @@ -630,14 +727,85 @@ public class HttpHeaders { * is an existing header with the same name, the existing header is removed. */ public static void setIntHeader(HttpMessage message, String name, Iterable values) { - message.setHeader(name, values); + message.headers().set(name, values); } /** * Adds a new integer header with the specified name and value. */ public static void addIntHeader(HttpMessage message, String name, int value) { - message.addHeader(name, value); + message.headers().add(name, value); + } + + /** + * Returns the date header value with the specified header name. If + * there are more than one header value for the specified header name, the + * first value is returned. + * + * @return the header value + * @throws ParseException + * if there is no such header or the header value is not a formatted date + */ + public static Date getDateHeader(HttpMessage message, String name) throws ParseException { + String value = getHeader(message, name); + if (value == null) { + throw new ParseException("header not found: " + name, 0); + } + return HttpHeaderDateFormat.get().parse(value); + } + + /** + * Returns the date header value with the specified header name. If + * there are more than one header value for the specified header name, the + * first value is returned. + * + * @return the header value or the {@code defaultValue} if there is no such + * header or the header value is not a formatted date + */ + public static Date getDateHeader(HttpMessage message, String name, Date defaultValue) { + final String value = getHeader(message, name); + if (value == null) { + return defaultValue; + } + + try { + return HttpHeaderDateFormat.get().parse(value); + } catch (ParseException e) { + return defaultValue; + } + } + + /** + * Sets a new date header with the specified name and value. If there + * is an existing header with the same name, the existing header is removed. + * The specified value is formatted as defined in + * RFC2616 + */ + public static void setDateHeader(HttpMessage message, String name, Date value) { + if (value != null) { + message.headers().set(name, HttpHeaderDateFormat.get().format(value)); + } else { + message.headers().set(name, null); + } + } + + /** + * Sets a new date header with the specified name and values. If there + * is an existing header with the same name, the existing header is removed. + * The specified values are formatted as defined in + * RFC2616 + */ + public static void setDateHeader(HttpMessage message, String name, Iterable values) { + message.headers().set(name, values); + } + + /** + * Adds a new date header with the specified name and value. The specified + * value is formatted as defined in + * RFC2616 + */ + public static void addDateHeader(HttpMessage message, String name, Date value) { + message.headers().add(name, value); } /** @@ -646,11 +814,27 @@ public class HttpHeaders { * {@code "Content-Length"} header, and thus they are independent from each * other. * - * @return the content length or {@code 0} if this message does not have - * the {@code "Content-Length"} header + * @return the content length + * + * @throws NumberFormatException + * if the message does not have the {@code "Content-Length"} header + * or its value is not a number */ public static long getContentLength(HttpMessage message) { - return getContentLength(message, 0L); + String value = getHeader(message, Names.CONTENT_LENGTH); + if (value != null) { + return Long.parseLong(value); + } + + // We know the content length if it's a Web Socket message even if + // Content-Length header is missing. + long webSocketContentLength = getWebSocketContentLength(message); + if (webSocketContentLength >= 0) { + return webSocketContentLength; + } + + // Otherwise we don't. + throw new NumberFormatException("header not found: " + Names.CONTENT_LENGTH); } /** @@ -660,46 +844,69 @@ public class HttpHeaders { * other. * * @return the content length or {@code defaultValue} if this message does - * not have the {@code "Content-Length"} header + * not have the {@code "Content-Length"} header or its value is not + * a number */ public static long getContentLength(HttpMessage message, long defaultValue) { - String contentLength = message.getHeader(Names.CONTENT_LENGTH); + String contentLength = message.headers().get(Names.CONTENT_LENGTH); if (contentLength != null) { - return Long.parseLong(contentLength); + try { + return Long.parseLong(contentLength); + } catch (NumberFormatException e) { + return defaultValue; + } } + // We know the content length if it's a Web Socket message even if + // Content-Length header is missing. + long webSocketContentLength = getWebSocketContentLength(message); + if (webSocketContentLength >= 0) { + return webSocketContentLength; + } + + // Otherwise we don't. + return defaultValue; + } + + /** + * Returns the content length of the specified web socket message. If the + * specified message is not a web socket message, {@code -1} is returned. + */ + private static int getWebSocketContentLength(HttpMessage message) { // WebSockset messages have constant content-lengths. + HttpHeaders h = message.headers(); if (message instanceof HttpRequest) { HttpRequest req = (HttpRequest) message; if (HttpMethod.GET.equals(req.getMethod()) && - req.containsHeader(Names.SEC_WEBSOCKET_KEY1) && - req.containsHeader(Names.SEC_WEBSOCKET_KEY2)) { + h.contains(Names.SEC_WEBSOCKET_KEY1) && + h.contains(Names.SEC_WEBSOCKET_KEY2)) { return 8; } } else if (message instanceof HttpResponse) { HttpResponse res = (HttpResponse) message; if (res.getStatus().getCode() == 101 && - res.containsHeader(Names.SEC_WEBSOCKET_ORIGIN) && - res.containsHeader(Names.SEC_WEBSOCKET_LOCATION)) { + h.contains(Names.SEC_WEBSOCKET_ORIGIN) && + h.contains(Names.SEC_WEBSOCKET_LOCATION)) { return 16; } } - return defaultValue; + // Not a web socket message + return -1; } /** * Sets the {@code "Content-Length"} header. */ public static void setContentLength(HttpMessage message, long length) { - message.setHeader(Names.CONTENT_LENGTH, length); + message.headers().set(Names.CONTENT_LENGTH, length); } /** * Returns the value of the {@code "Host"} header. */ public static String getHost(HttpMessage message) { - return message.getHeader(Names.HOST); + return message.headers().get(Names.HOST); } /** @@ -714,7 +921,37 @@ public class HttpHeaders { * Sets the {@code "Host"} header. */ public static void setHost(HttpMessage message, String value) { - message.setHeader(Names.HOST, value); + message.headers().set(Names.HOST, value); + } + + /** + * Returns the value of the {@code "Date"} header. + * + * @throws ParseException + * if there is no such header or the header value is not a formatted date + */ + public static Date getDate(HttpMessage message) throws ParseException { + return getDateHeader(message, Names.DATE); + } + + /** + * Returns the value of the {@code "Date"} header. If there is no such + * header or the header is not a formatted date, the {@code defaultValue} + * is returned. + */ + public static Date getDate(HttpMessage message, Date defaultValue) { + return getDateHeader(message, Names.DATE, defaultValue); + } + + /** + * Sets the {@code "Date"} header. + */ + public static void setDate(HttpMessage message, Date value) { + if (value != null) { + message.headers().set(Names.DATE, HttpHeaderDateFormat.get().format(value)); + } else { + message.headers().set(Names.DATE, null); + } } /** @@ -733,7 +970,7 @@ public class HttpHeaders { } // In most cases, there will be one or zero 'Expect' header. - String value = message.getHeader(Names.EXPECT); + String value = message.headers().get(Names.EXPECT); if (value == null) { return false; } @@ -742,12 +979,7 @@ public class HttpHeaders { } // Multiple 'Expect' headers. Search through them. - for (String v: message.getHeaders(Names.EXPECT)) { - if (Values.CONTINUE.equalsIgnoreCase(v)) { - return true; - } - } - return false; + return message.headers().contains(Names.EXPECT, Values.CONTINUE, true); } /** @@ -768,293 +1000,351 @@ public class HttpHeaders { */ public static void set100ContinueExpected(HttpMessage message, boolean set) { if (set) { - message.setHeader(Names.EXPECT, Values.CONTINUE); + message.headers().set(Names.EXPECT, Values.CONTINUE); } else { - message.removeHeader(Names.EXPECT); + message.headers().remove(Names.EXPECT); } } - private static final int BUCKET_SIZE = 17; + /** + * Validates the name of a header + * + * @param headerName The header name being validated + */ + static void validateHeaderName(String headerName) { + //Check to see if the name is null + if (headerName == null) { + throw new NullPointerException("Header names cannot be null"); + } + //Go through each of the characters in the name + for (int index = 0; index < headerName.length(); index ++) { + //Actually get the character + char character = headerName.charAt(index); + valideHeaderNameChar(character); + } + } - private static int hash(String name) { - int h = 0; - for (int i = name.length() - 1; i >= 0; i --) { - char c = name.charAt(i); - if (c >= 'A' && c <= 'Z') { - c += 32; + static void valideHeaderNameChar(char c) { + //Check to see if the character is not an ASCII character + if (c > 127) { + throw new IllegalArgumentException( + "Header name cannot contain non-ASCII characters: " + c); + } + + //Check for prohibited characters. + switch (c) { + case '\t': case '\n': case 0x0b: case '\f': case '\r': + case ' ': case ',': case ':': case ';': case '=': + throw new IllegalArgumentException( + "Header name cannot contain the following prohibited characters: " + + "=,;: \\t\\r\\n\\v\\f "); + } + } + + /** + * Validates the specified header value + * + * @param headerValue The value being validated + */ + static void validateHeaderValue(String headerValue) { + //Check to see if the value is null + if (headerValue == null) { + throw new NullPointerException("Header values cannot be null"); + } + + /* + * Set up the state of the validation + * + * States are as follows: + * + * 0: Previous character was neither CR nor LF + * 1: The previous character was CR + * 2: The previous character was LF + */ + int state = 0; + + //Start looping through each of the character + + for (int index = 0; index < headerValue.length(); index ++) { + char character = headerValue.charAt(index); + + //Check the absolutely prohibited characters. + switch (character) { + case 0x0b: // Vertical tab + throw new IllegalArgumentException( + "Header value contains a prohibited character '\\v': " + headerValue); + case '\f': + throw new IllegalArgumentException( + "Header value contains a prohibited character '\\f': " + headerValue); + } + + // 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': " + headerValue); + } + break; + case 2: + switch (character) { + case '\t': case ' ': + state = 0; + break; + default: + throw new IllegalArgumentException( + "Only ' ' and '\\t' are allowed after '\\n': " + headerValue); + } } - h = 31 * h + c; } - if (h > 0) { - return h; - } else if (h == Integer.MIN_VALUE) { - return Integer.MAX_VALUE; - } else { - return -h; + if (state != 0) { + throw new IllegalArgumentException( + "Header value must not end with '\\r' or '\\n':" + headerValue); } } - private static boolean eq(String name1, String name2) { - int nameLen = name1.length(); - if (nameLen != name2.length()) { + /** + * Checks to see if the transfer encoding in a specified {@link HttpMessage} is chunked + * + * @param message The message to check + * @return True if transfer encoding is chunked, otherwise false + */ + public static boolean isTransferEncodingChunked(HttpMessage message) { + return message.headers().contains(Names.TRANSFER_ENCODING, Values.CHUNKED, true); + } + + public static void removeTransferEncodingChunked(HttpMessage m) { + List values = m.headers().getAll(Names.TRANSFER_ENCODING); + if (values.isEmpty()) { + return; + } + Iterator valuesIt = values.iterator(); + while (valuesIt.hasNext()) { + String value = valuesIt.next(); + if (value.equalsIgnoreCase(Values.CHUNKED)) { + valuesIt.remove(); + } + } + if (values.isEmpty()) { + m.headers().remove(Names.TRANSFER_ENCODING); + } else { + m.headers().set(Names.TRANSFER_ENCODING, values); + } + } + + public static void setTransferEncodingChunked(HttpMessage m) { + addHeader(m, Names.TRANSFER_ENCODING, Values.CHUNKED); + removeHeader(m, Names.CONTENT_LENGTH); + } + + public static boolean isContentLengthSet(HttpMessage m) { + return m.headers().contains(Names.CONTENT_LENGTH); + } + + protected HttpHeaders() { } + + /** + * 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 + */ + public abstract String get(String 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 + */ + public abstract List getAll(String name); + + /** + * Returns the all headers that this message contains. + * + * @return A {@link List} of the header name-value entries, which will be + * empty if no pairs are found + */ + public abstract List> entries(); + + /** + * Checks to see if there is a header with the specified name + * + * @param name The name of the header to search for + * @return True if at least one header is found + */ + public abstract boolean contains(String name); + + /** + * Checks if no header exists. + */ + public abstract boolean isEmpty(); + + /** + * Gets a {@link Set} of all header names that this message contains + * + * @return A {@link Set} of all header names + */ + public abstract 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 Date} and {@link 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} + */ + public abstract HttpHeaders add(String 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 headers being set + * @param values The values of the headers being set + * @return {@code this} + */ + public abstract HttpHeaders add(String name, Iterable values); + + /** + * Adds all header entries of the specified {@code headers}. + * + * @return {@code this} + */ + public HttpHeaders add(HttpHeaders headers) { + if (headers == null) { + throw new NullPointerException("headers"); + } + for (Map.Entry e: headers) { + add(e.getKey(), e.getValue()); + } + return this; + } + + /** + * 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 Date} + * and {@link 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} + */ + public abstract HttpHeaders set(String 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} + */ + public abstract HttpHeaders set(String name, Iterable values); + + /** + * Cleans the current header entries and copies all header entries of the specified {@code headers}. + * + * @return {@code this} + */ + public HttpHeaders set(HttpHeaders headers) { + if (headers == null) { + throw new NullPointerException("headers"); + } + clear(); + for (Map.Entry e: headers) { + add(e.getKey(), e.getValue()); + } + return this; + } + + /** + * Removes the header with the specified name. + * + * @param name The name of the header to remove + * @return {@code this} + */ + public abstract HttpHeaders remove(String name); + + /** + * Removes all headers from this {@link HttpMessage}. + * + * @return {@code this} + */ + public abstract HttpHeaders clear(); + + /** + * Returns {@code true} if a header with the name and value exists. + * + * @param name the headername + * @param value the value + * @param ignoreCaseValue {@code true} if case should be ignored + * @return contains {@code true} if it contains it {@code false} otherwise + */ + public boolean contains(String name, String value, boolean ignoreCaseValue) { + List values = getAll(name); + if (values.isEmpty()) { 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; - } - - private static int index(int hash) { - return hash % BUCKET_SIZE; - } - - private final HeaderEntry[] entries = new HeaderEntry[BUCKET_SIZE]; - private final HeaderEntry head = new HeaderEntry(-1, null, null); - - HttpHeaders() { - head.before = head.after = head; - } - - void validateHeaderName(String name) { - HttpCodecUtil.validateHeaderName(name); - } - - void addHeader(final String name, final Object value) { - validateHeaderName(name); - String strVal = toString(value); - HttpCodecUtil.validateHeaderValue(strVal); - int h = hash(name); - int i = index(h); - addHeader0(h, i, name, strVal); - } - - private void addHeader0(int h, int i, final String name, final String 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); - } - - void removeHeader(final String name) { - if (name == null) { - throw new NullPointerException("name"); - } - int h = hash(name); - int i = index(h); - removeHeader0(h, i, name); - } - - private void removeHeader0(int h, int i, String name) { - HeaderEntry e = entries[i]; - if (e == null) { - return; - } - - for (;;) { - if (e.hash == h && eq(name, e.key)) { - e.remove(); - HeaderEntry next = e.next; - if (next != null) { - entries[i] = next; - e = next; - } else { - entries[i] = null; - return; + for (String v: values) { + if (ignoreCaseValue) { + if (v.equalsIgnoreCase(value)) { + return true; } } else { - break; + if (v.equals(value)) { + return true; + } } } - - for (;;) { - HeaderEntry next = e.next; - if (next == null) { - break; - } - if (next.hash == h && eq(name, next.key)) { - e.next = next.next; - next.remove(); - } else { - e = next; - } - } - } - - void setHeader(final String name, final Object value) { - validateHeaderName(name); - String strVal = toString(value); - HttpCodecUtil.validateHeaderValue(strVal); - int h = hash(name); - int i = index(h); - removeHeader0(h, i, name); - addHeader0(h, i, name, strVal); - } - - void setHeader(final String name, final Iterable values) { - if (values == null) { - throw new NullPointerException("values"); - } - - validateHeaderName(name); - - int h = hash(name); - int i = index(h); - - removeHeader0(h, i, name); - for (Object v: values) { - if (v == null) { - break; - } - String strVal = toString(v); - HttpCodecUtil.validateHeaderValue(strVal); - addHeader0(h, i, name, strVal); - } - } - - void clearHeaders() { - for (int i = 0; i < entries.length; i ++) { - entries[i] = null; - } - head.before = head.after = head; - } - - String getHeader(final String name) { - if (name == null) { - throw new NullPointerException("name"); - } - - int h = hash(name); - int i = index(h); - HeaderEntry e = entries[i]; - while (e != null) { - if (e.hash == h && eq(name, e.key)) { - return e.value; - } - - e = e.next; - } - return null; - } - - List getHeaders(final String name) { - if (name == null) { - throw new NullPointerException("name"); - } - - LinkedList values = new LinkedList(); - - int h = hash(name); - int i = index(h); - HeaderEntry e = entries[i]; - while (e != null) { - if (e.hash == h && eq(name, e.key)) { - values.addFirst(e.value); - } - e = e.next; - } - return values; - } - - List> getHeaders() { - List> all = - new LinkedList>(); - - HeaderEntry e = head.after; - while (e != head) { - all.add(e); - e = e.after; - } - return all; - } - - boolean containsHeader(String name) { - return getHeader(name) != null; - } - - Set getHeaderNames() { - Set names = - new TreeSet(CaseIgnoringComparator.INSTANCE); - - HeaderEntry e = head.after; - while (e != head) { - names.add(e.key); - e = e.after; - } - return names; - } - - private static String toString(Object value) { - if (value == null) { - return null; - } - return value.toString(); - } - - private static final class HeaderEntry implements Map.Entry { - final int hash; - final String key; - String value; - HeaderEntry next; - HeaderEntry before, after; - - HeaderEntry(int hash, String key, String value) { - this.hash = hash; - this.key = key; - this.value = value; - } - - void remove() { - before.after = after; - after.before = before; - } - - void addBefore(HeaderEntry e) { - after = e; - before = e.before; - before.after = this; - after.before = this; - } - - public String getKey() { - return key; - } - - public String getValue() { - return value; - } - - public String setValue(String value) { - if (value == null) { - throw new NullPointerException("value"); - } - HttpCodecUtil.validateHeaderValue(value); - String oldValue = this.value; - this.value = value; - return oldValue; - } - - @Override - public String toString() { - return key + '=' + value; - } + return false; } } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/HttpMessage.java b/src/main/java/org/jboss/netty/handler/codec/http/HttpMessage.java index b212a257c1..55391c2dce 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/HttpMessage.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/HttpMessage.java @@ -33,39 +33,33 @@ import org.jboss.netty.buffer.ChannelBuffers; public interface HttpMessage { /** - * Returns the header value with the specified header name. If there are - * more than one header value for the specified header name, the first - * value is returned. - * - * @return the header value or {@code null} if there is no such header + * @deprecated Use {@link HttpMessage#headers()} instead. */ + @Deprecated String getHeader(String name); /** - * Returns the header values with the specified header name. - * - * @return the {@link List} of header values. An empty list if there is no - * such header. + * @deprecated Use {@link HttpMessage#headers()} instead. */ + @Deprecated List getHeaders(String name); /** - * Returns the all header names and values that this message contains. - * - * @return the {@link List} of the header name-value pairs. An empty list - * if there is no header in this message. + * @deprecated Use {@link HttpMessage#headers()} instead. */ + @Deprecated List> getHeaders(); /** - * Returns {@code true} if and only if there is a header with the specified - * header name. + * @deprecated Use {@link HttpMessage#headers()} instead. */ + @Deprecated boolean containsHeader(String name); /** * Returns the {@link Set} of all header names that this message contains. */ + @Deprecated Set getHeaderNames(); /** @@ -78,6 +72,11 @@ public interface HttpMessage { */ void setProtocolVersion(HttpVersion version); + /** + * Returns the headers of this message. + */ + HttpHeaders headers(); + /** * Returns the content of this message. If there is no content or * {@link #isChunked()} returns {@code true}, an @@ -92,44 +91,35 @@ public interface HttpMessage { void setContent(ChannelBuffer content); /** - * Adds a new header with the specified name and value. + * @deprecated Use {@link HttpMessage#headers()} instead. */ + @Deprecated void addHeader(String name, Object value); /** - * Sets a new header with the specified name and value. If there is an - * existing header with the same name, the existing header is removed. + * @deprecated Use {@link HttpMessage#headers()} instead. */ + @Deprecated void setHeader(String name, Object value); /** - * Sets a new header with the specified name and values. If there is an - * existing header with the same name, the existing header is removed. + * @deprecated Use {@link HttpMessage#headers()} instead. */ + @Deprecated void setHeader(String name, Iterable values); /** - * Removes the header with the specified name. + * @deprecated Use {@link HttpMessage#headers()} instead. */ + @Deprecated void removeHeader(String name); /** - * Removes all headers from this message. + * @deprecated Use {@link HttpMessage#headers()} instead. */ + @Deprecated void clearHeaders(); - /** - * @deprecated Use {@link HttpHeaders#getContentLength(HttpMessage)} instead. - */ - @Deprecated - long getContentLength(); - - /** - * @deprecated Use {@link HttpHeaders#getContentLength(HttpMessage, long)} instead. - */ - @Deprecated - long getContentLength(long defaultValue); - /** * Returns {@code true} if and only if this message does not have any * content but the {@link HttpChunk}s, which is generated by @@ -155,10 +145,4 @@ public interface HttpMessage { * this message is {@code "chunked"}. */ void setChunked(boolean chunked); - - /** - * @deprecated Use {@link HttpHeaders#isKeepAlive(HttpMessage)} instead. - */ - @Deprecated - boolean isKeepAlive(); } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java b/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java index ee17b2ba43..6ca638d537 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/HttpMessageDecoder.java @@ -202,7 +202,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder= 100 && code < 200) { - if (code == 101 && !res.containsHeader(HttpHeaders.Names.SEC_WEBSOCKET_ACCEPT)) { + if (code == 101 && !res.headers().contains(HttpHeaders.Names.SEC_WEBSOCKET_ACCEPT)) { // It's Hixie 76 websocket handshake response return false; } @@ -486,14 +486,14 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder current = trailer.getHeaders(lastHeader); + List current = trailer.trailingHeaders().getAll(lastHeader); if (!current.isEmpty()) { int lastPos = current.size() - 1; String newString = current.get(lastPos) + line.trim(); @@ -552,7 +552,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder h: message.getHeaders()) { + for (Map.Entry h: message.headers()) { encodeHeader(buf, h.getKey(), h.getValue()); } } catch (UnsupportedEncodingException e) { @@ -151,7 +151,7 @@ public abstract class HttpMessageEncoder extends OneToOneEncoder { private static void encodeTrailingHeaders(ChannelBuffer buf, HttpChunkTrailer trailer) { try { - for (Map.Entry h: trailer.getHeaders()) { + for (Map.Entry h: trailer.trailingHeaders()) { encodeHeader(buf, h.getKey(), h.getValue()); } } catch (UnsupportedEncodingException e) { diff --git a/src/main/java/org/jboss/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java b/src/main/java/org/jboss/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java index 4ba482f0f4..e2e8f4c936 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java @@ -178,8 +178,8 @@ public class HttpPostRequestDecoder { this.charset = charset; this.factory = factory; // Fill default values - if (this.request.containsHeader(HttpHeaders.Names.CONTENT_TYPE)) { - checkMultipart(this.request.getHeader(HttpHeaders.Names.CONTENT_TYPE)); + if (this.request.headers().contains(HttpHeaders.Names.CONTENT_TYPE)) { + checkMultipart(this.request.headers().get(HttpHeaders.Names.CONTENT_TYPE)); } else { isMultipart = false; } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java b/src/main/java/org/jboss/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java index 33d2a2f9e9..573158f640 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java @@ -614,6 +614,7 @@ public class HttpPostRequestEncoder implements ChunkedInput { * @throws ErrorDataEncoderException if the encoding is in error or if the finalize were already done */ public HttpRequest finalizeRequest() throws ErrorDataEncoderException { + HttpHeaders headers = request.headers(); // Finalize the multipartHttpDatas if (! headerFinalized) { if (isMultipart) { @@ -632,11 +633,10 @@ public class HttpPostRequestEncoder implements ChunkedInput { } else { throw new ErrorDataEncoderException("Header already encoded"); } - List contentTypes = request.getHeaders(HttpHeaders.Names.CONTENT_TYPE); - List transferEncoding = - request.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING); + List contentTypes = headers.getAll(HttpHeaders.Names.CONTENT_TYPE); + List transferEncoding = headers.getAll(HttpHeaders.Names.TRANSFER_ENCODING); if (contentTypes != null) { - request.removeHeader(HttpHeaders.Names.CONTENT_TYPE); + headers.remove(HttpHeaders.Names.CONTENT_TYPE); for (String contentType: contentTypes) { // "multipart/form-data; boundary=--89421926422648" if (contentType.toLowerCase().startsWith( @@ -646,17 +646,17 @@ public class HttpPostRequestEncoder implements ChunkedInput { HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED)) { // ignore } else { - request.addHeader(HttpHeaders.Names.CONTENT_TYPE, contentType); + headers.add(HttpHeaders.Names.CONTENT_TYPE, contentType); } } } if (isMultipart) { String value = HttpHeaders.Values.MULTIPART_FORM_DATA + "; " + HttpHeaders.Values.BOUNDARY + '=' + multipartDataBoundary; - request.addHeader(HttpHeaders.Names.CONTENT_TYPE, value); + headers.add(HttpHeaders.Names.CONTENT_TYPE, value); } else { // Not multipart - request.addHeader(HttpHeaders.Names.CONTENT_TYPE, + headers.add(HttpHeaders.Names.CONTENT_TYPE, HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED); } // Now consider size for chunk or not @@ -667,22 +667,20 @@ public class HttpPostRequestEncoder implements ChunkedInput { realSize -= 1; // last '&' removed iterator = multipartHttpDatas.listIterator(); } - request.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String - .valueOf(realSize)); + headers.set(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(realSize)); if (realSize > HttpPostBodyUtil.chunkSize || isMultipart) { isChunked = true; if (transferEncoding != null) { - request.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING); + headers.remove(HttpHeaders.Names.TRANSFER_ENCODING); for (String v: transferEncoding) { if (v.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) { // ignore } else { - request.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, v); + headers.add(HttpHeaders.Names.TRANSFER_ENCODING, v); } } } - request.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, - HttpHeaders.Values.CHUNKED); + headers.add(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED); request.setContent(ChannelBuffers.EMPTY_BUFFER); } else { // get the only one body and set it to the request diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java index 8a4e75412f..8eb0d89d4e 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java @@ -161,9 +161,9 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker { // Format request HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); - request.addHeader(Names.UPGRADE, Values.WEBSOCKET); - request.addHeader(Names.CONNECTION, Values.UPGRADE); - request.addHeader(Names.HOST, wsURL.getHost()); + request.headers().add(Names.UPGRADE, Values.WEBSOCKET); + request.headers().add(Names.CONNECTION, Values.UPGRADE); + request.headers().add(Names.HOST, wsURL.getHost()); int wsPort = wsURL.getPort(); String originValue = "http://" + wsURL.getHost(); @@ -172,24 +172,24 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker { // See http://tools.ietf.org/html/rfc6454#section-6.2 originValue = originValue + ':' + wsPort; } - request.addHeader(Names.ORIGIN, originValue); + request.headers().add(Names.ORIGIN, originValue); - request.addHeader(Names.SEC_WEBSOCKET_KEY1, key1); - request.addHeader(Names.SEC_WEBSOCKET_KEY2, key2); + request.headers().add(Names.SEC_WEBSOCKET_KEY1, key1); + request.headers().add(Names.SEC_WEBSOCKET_KEY2, key2); String expectedSubprotocol = getExpectedSubprotocol(); if (expectedSubprotocol != null && expectedSubprotocol.length() != 0) { - request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); + request.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); } if (customHeaders != null) { for (Map.Entry e: customHeaders.entrySet()) { - request.addHeader(e.getKey(), e.getValue()); + request.headers().add(e.getKey(), e.getValue()); } } // Set Content-Length to workaround some known defect. // See also: http://www.ietf.org/mail-archive/web/hybi/current/msg02149.html - request.setHeader(Names.CONTENT_LENGTH, key3.length); + request.headers().set(Names.CONTENT_LENGTH, key3.length); request.setContent(ChannelBuffers.copiedBuffer(key3)); final ChannelFuture handshakeFuture = new DefaultChannelFuture(channel, false); @@ -241,13 +241,13 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker { throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus()); } - String upgrade = response.getHeader(Names.UPGRADE); + String upgrade = response.headers().get(Names.UPGRADE); if (!Values.WEBSOCKET.equals(upgrade)) { throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); } - String connection = response.getHeader(Names.CONNECTION); + String connection = response.headers().get(Names.CONNECTION); if (!Values.UPGRADE.equals(connection)) { throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection); @@ -258,7 +258,7 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker { throw new WebSocketHandshakeException("Invalid challenge"); } - String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); + String subprotocol = response.headers().get(Names.SEC_WEBSOCKET_PROTOCOL); setActualSubprotocol(subprotocol); setHandshakeComplete(); diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java index 8ad26769a8..8005dbac92 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java @@ -128,10 +128,10 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker { // Format request HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); - request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase()); - request.addHeader(Names.CONNECTION, Values.UPGRADE); - request.addHeader(Names.SEC_WEBSOCKET_KEY, key); - request.addHeader(Names.HOST, wsURL.getHost()); + request.headers().add(Names.UPGRADE, Values.WEBSOCKET.toLowerCase()); + request.headers().add(Names.CONNECTION, Values.UPGRADE); + request.headers().add(Names.SEC_WEBSOCKET_KEY, key); + request.headers().add(Names.HOST, wsURL.getHost()); int wsPort = wsURL.getPort(); String originValue = "http://" + wsURL.getHost(); @@ -140,18 +140,18 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker { // See http://tools.ietf.org/html/rfc6454#section-6.2 originValue = originValue + ':' + wsPort; } - request.addHeader(Names.SEC_WEBSOCKET_ORIGIN, originValue); + request.headers().add(Names.SEC_WEBSOCKET_ORIGIN, originValue); String expectedSubprotocol = getExpectedSubprotocol(); if (expectedSubprotocol != null && expectedSubprotocol.length() > 0) { - request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); + request.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); } - request.addHeader(Names.SEC_WEBSOCKET_VERSION, "7"); + request.headers().add(Names.SEC_WEBSOCKET_VERSION, "7"); if (customHeaders != null) { for (Map.Entry e : customHeaders.entrySet()) { - request.addHeader(e.getKey(), e.getValue()); + request.headers().add(e.getKey(), e.getValue()); } } @@ -203,25 +203,25 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker { throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus()); } - String upgrade = response.getHeader(Names.UPGRADE); + String upgrade = response.headers().get(Names.UPGRADE); if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) { throw new WebSocketHandshakeException("Invalid handshake response upgrade: " - + response.getHeader(Names.UPGRADE)); + + response.headers().get(Names.UPGRADE)); } - String connection = response.getHeader(Names.CONNECTION); + String connection = response.headers().get(Names.CONNECTION); if (!Values.UPGRADE.equalsIgnoreCase(connection)) { throw new WebSocketHandshakeException("Invalid handshake response connection: " - + response.getHeader(Names.CONNECTION)); + + response.headers().get(Names.CONNECTION)); } - String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT); + String accept = response.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)); } - String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); + String subprotocol = response.headers().get(Names.SEC_WEBSOCKET_PROTOCOL); setActualSubprotocol(subprotocol); setHandshakeComplete(); diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java index b376b34a25..d2cbd40eaa 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java @@ -147,10 +147,10 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker { // Format request HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); - request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase()); - request.addHeader(Names.CONNECTION, Values.UPGRADE); - request.addHeader(Names.SEC_WEBSOCKET_KEY, key); - request.addHeader(Names.HOST, wsURL.getHost()); + request.headers().add(Names.UPGRADE, Values.WEBSOCKET.toLowerCase()); + request.headers().add(Names.CONNECTION, Values.UPGRADE); + request.headers().add(Names.SEC_WEBSOCKET_KEY, key); + request.headers().add(Names.HOST, wsURL.getHost()); int wsPort = wsURL.getPort(); String originValue = "http://" + wsURL.getHost(); @@ -162,18 +162,18 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker { // Use Sec-WebSocket-Origin // See https://github.com/netty/netty/issues/264 - request.addHeader(Names.SEC_WEBSOCKET_ORIGIN, originValue); + request.headers().add(Names.SEC_WEBSOCKET_ORIGIN, originValue); String expectedSubprotocol = getExpectedSubprotocol(); if (expectedSubprotocol != null && expectedSubprotocol.length() != 0) { - request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); + request.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); } - request.addHeader(Names.SEC_WEBSOCKET_VERSION, "8"); + request.headers().add(Names.SEC_WEBSOCKET_VERSION, "8"); if (customHeaders != null) { for (Map.Entry e: customHeaders.entrySet()) { - request.addHeader(e.getKey(), e.getValue()); + request.headers().add(e.getKey(), e.getValue()); } } @@ -223,29 +223,29 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker { throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus()); } - String upgrade = response.getHeader(Names.UPGRADE); + String upgrade = response.headers().get(Names.UPGRADE); // Upgrade header should be matched case-insensitive. // See https://github.com/netty/netty/issues/278 if (upgrade == null || !upgrade.toLowerCase().equals(Values.WEBSOCKET.toLowerCase())) { throw new WebSocketHandshakeException("Invalid handshake response upgrade: " - + response.getHeader(Names.UPGRADE)); + + response.headers().get(Names.UPGRADE)); } // Connection header should be matched case-insensitive. // See https://github.com/netty/netty/issues/278 - String connection = response.getHeader(Names.CONNECTION); + String connection = response.headers().get(Names.CONNECTION); if (connection == null || !connection.toLowerCase().equals(Values.UPGRADE.toLowerCase())) { throw new WebSocketHandshakeException("Invalid handshake response connection: " - + response.getHeader(Names.CONNECTION)); + + response.headers().get(Names.CONNECTION)); } - String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT); + String accept = response.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)); } - String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); + String subprotocol = response.headers().get(Names.SEC_WEBSOCKET_PROTOCOL); setActualSubprotocol(subprotocol); setHandshakeComplete(); diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java index 6dc04c59bd..f200a0f7cc 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java @@ -148,10 +148,10 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { // Format request int wsPort = wsURL.getPort(); HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); - request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase()); - request.addHeader(Names.CONNECTION, Values.UPGRADE); - request.addHeader(Names.SEC_WEBSOCKET_KEY, key); - request.addHeader(Names.HOST, wsURL.getHost() + ':' + wsPort); + request.headers().add(Names.UPGRADE, Values.WEBSOCKET.toLowerCase()); + request.headers().add(Names.CONNECTION, Values.UPGRADE); + request.headers().add(Names.SEC_WEBSOCKET_KEY, key); + request.headers().add(Names.HOST, wsURL.getHost() + ':' + wsPort); String originValue = "http://" + wsURL.getHost(); if (wsPort != 80 && wsPort != 443) { @@ -159,18 +159,18 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { // See http://tools.ietf.org/html/rfc6454#section-6.2 originValue = originValue + ':' + wsPort; } - request.addHeader(Names.ORIGIN, originValue); + request.headers().add(Names.ORIGIN, originValue); String expectedSubprotocol = getExpectedSubprotocol(); if (expectedSubprotocol != null && expectedSubprotocol.length() != 0) { - request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); + request.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol); } - request.addHeader(Names.SEC_WEBSOCKET_VERSION, "13"); + request.headers().add(Names.SEC_WEBSOCKET_VERSION, "13"); if (customHeaders != null) { for (Map.Entry e: customHeaders.entrySet()) { - request.addHeader(e.getKey(), e.getValue()); + request.headers().add(e.getKey(), e.getValue()); } } @@ -220,29 +220,29 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus()); } - String upgrade = response.getHeader(Names.UPGRADE); + String upgrade = response.headers().get(Names.UPGRADE); // Upgrade header should be matched case-insensitive. // See https://github.com/netty/netty/issues/278 if (upgrade == null || !upgrade.toLowerCase().equals(Values.WEBSOCKET.toLowerCase())) { throw new WebSocketHandshakeException("Invalid handshake response upgrade: " - + response.getHeader(Names.UPGRADE)); + + response.headers().get(Names.UPGRADE)); } // Connection header should be matched case-insensitive. // See https://github.com/netty/netty/issues/278 - String connection = response.getHeader(Names.CONNECTION); + String connection = response.headers().get(Names.CONNECTION); if (connection == null || !connection.toLowerCase().equals(Values.UPGRADE.toLowerCase())) { throw new WebSocketHandshakeException("Invalid handshake response connection: " - + response.getHeader(Names.CONNECTION)); + + response.headers().get(Names.CONNECTION)); } - String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT); + String accept = response.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)); } - String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); + String subprotocol = response.headers().get(Names.SEC_WEBSOCKET_PROTOCOL); setActualSubprotocol(subprotocol); setHandshakeComplete(); diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java index 0da57d028a..1913b03a6a 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java @@ -133,39 +133,39 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { } // Serve the WebSocket handshake request. - if (!Values.UPGRADE.equalsIgnoreCase(req.getHeader(CONNECTION)) - || !WEBSOCKET.equalsIgnoreCase(req.getHeader(Names.UPGRADE))) { + if (!Values.UPGRADE.equalsIgnoreCase(req.headers().get(CONNECTION)) + || !WEBSOCKET.equalsIgnoreCase(req.headers().get(Names.UPGRADE))) { throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade"); } // Hixie 75 does not contain these headers while Hixie 76 does - boolean isHixie76 = req.containsHeader(SEC_WEBSOCKET_KEY1) && req.containsHeader(SEC_WEBSOCKET_KEY2); + boolean isHixie76 = req.headers().contains(SEC_WEBSOCKET_KEY1) && req.headers().contains(SEC_WEBSOCKET_KEY2); // Create the WebSocket handshake response. HttpResponse res = new DefaultHttpResponse(HTTP_1_1, new HttpResponseStatus(101, isHixie76 ? "WebSocket Protocol Handshake" : "Web Socket Protocol Handshake")); - res.addHeader(Names.UPGRADE, WEBSOCKET); - res.addHeader(CONNECTION, Values.UPGRADE); + res.headers().add(Names.UPGRADE, WEBSOCKET); + res.headers().add(CONNECTION, Values.UPGRADE); // Fill in the headers and contents depending on handshake method. if (isHixie76) { // New handshake method with a challenge: - res.addHeader(SEC_WEBSOCKET_ORIGIN, req.getHeader(ORIGIN)); - res.addHeader(SEC_WEBSOCKET_LOCATION, getWebSocketUrl()); - String subprotocols = req.getHeader(SEC_WEBSOCKET_PROTOCOL); + res.headers().add(SEC_WEBSOCKET_ORIGIN, req.headers().get(ORIGIN)); + res.headers().add(SEC_WEBSOCKET_LOCATION, getWebSocketUrl()); + String subprotocols = req.headers().get(SEC_WEBSOCKET_PROTOCOL); if (subprotocols != null) { String selectedSubprotocol = selectSubprotocol(subprotocols); if (selectedSubprotocol == null) { throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols); } else { - res.addHeader(SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); + res.headers().add(SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); setSelectedSubprotocol(selectedSubprotocol); } } // Calculate the answer of the challenge. - String key1 = req.getHeader(SEC_WEBSOCKET_KEY1); - String key2 = req.getHeader(SEC_WEBSOCKET_KEY2); + String key1 = req.headers().get(SEC_WEBSOCKET_KEY1); + String key2 = req.headers().get(SEC_WEBSOCKET_KEY2); int a = (int) (Long.parseLong(key1.replaceAll("[^0-9]", "")) / key1.replaceAll("[^ ]", "").length()); int b = (int) (Long.parseLong(key2.replaceAll("[^0-9]", "")) / key2.replaceAll("[^ ]", "").length()); long c = req.getContent().readLong(); @@ -176,11 +176,11 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { res.setContent(WebSocketUtil.md5(input)); } else { // Old Hixie 75 handshake method with no challenge: - res.addHeader(WEBSOCKET_ORIGIN, req.getHeader(ORIGIN)); - res.addHeader(WEBSOCKET_LOCATION, getWebSocketUrl()); - String protocol = req.getHeader(WEBSOCKET_PROTOCOL); + res.headers().add(WEBSOCKET_ORIGIN, req.headers().get(ORIGIN)); + res.headers().add(WEBSOCKET_LOCATION, getWebSocketUrl()); + String protocol = req.headers().get(WEBSOCKET_PROTOCOL); if (protocol != null) { - res.addHeader(WEBSOCKET_PROTOCOL, selectSubprotocol(protocol)); + res.headers().add(WEBSOCKET_PROTOCOL, selectSubprotocol(protocol)); } } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java index a874753886..68ed3fac5f 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java @@ -117,7 +117,7 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker { } HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS); - String key = req.getHeader(Names.SEC_WEBSOCKET_KEY); + String key = req.headers().get(Names.SEC_WEBSOCKET_KEY); if (key == null) { throw new WebSocketHandshakeException("not a WebSocket request: missing key"); } @@ -130,16 +130,16 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker { } res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS); - res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase()); - res.addHeader(Names.CONNECTION, Names.UPGRADE); - res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept); - String subprotocols = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); + res.headers().add(Names.UPGRADE, WEBSOCKET.toLowerCase()); + res.headers().add(Names.CONNECTION, Names.UPGRADE); + res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept); + String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL); if (subprotocols != null) { String selectedSubprotocol = selectSubprotocol(subprotocols); if (selectedSubprotocol == null) { throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols); } else { - res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); + res.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); setSelectedSubprotocol(selectedSubprotocol); } } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java index cfa174f492..9989b5381f 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java @@ -134,7 +134,7 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker { HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS); - String key = req.getHeader(Names.SEC_WEBSOCKET_KEY); + String key = req.headers().get(Names.SEC_WEBSOCKET_KEY); if (key == null) { throw new WebSocketHandshakeException("not a WebSocket request: missing key"); } @@ -148,16 +148,16 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker { } res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS); - res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase()); - res.addHeader(Names.CONNECTION, Names.UPGRADE); - res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept); - String subprotocols = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); + res.headers().add(Names.UPGRADE, WEBSOCKET.toLowerCase()); + res.headers().add(Names.CONNECTION, Names.UPGRADE); + res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept); + String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL); if (subprotocols != null) { String selectedSubprotocol = selectSubprotocol(subprotocols); if (selectedSubprotocol == null) { throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols); } else { - res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); + res.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); setSelectedSubprotocol(selectedSubprotocol); } } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java index 22e648850c..69bfd3d064 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java @@ -141,7 +141,7 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker { HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS); - String key = req.getHeader(Names.SEC_WEBSOCKET_KEY); + String key = req.headers().get(Names.SEC_WEBSOCKET_KEY); if (key == null) { throw new WebSocketHandshakeException("not a WebSocket request: missing key"); } @@ -154,16 +154,16 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker { } res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS); - res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase()); - res.addHeader(Names.CONNECTION, Names.UPGRADE); - res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept); - String subprotocols = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); + res.headers().add(Names.UPGRADE, WEBSOCKET.toLowerCase()); + res.headers().add(Names.CONNECTION, Names.UPGRADE); + res.headers().add(Names.SEC_WEBSOCKET_ACCEPT, accept); + String subprotocols = req.headers().get(Names.SEC_WEBSOCKET_PROTOCOL); if (subprotocols != null) { String selectedSubprotocol = selectSubprotocol(subprotocols); if (selectedSubprotocol == null) { throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols); } else { - res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); + res.headers().add(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol); setSelectedSubprotocol(selectedSubprotocol); } } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java index 296122aa90..983e3c31cb 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java @@ -79,7 +79,7 @@ public class WebSocketServerHandshakerFactory { */ public WebSocketServerHandshaker newHandshaker(HttpRequest req) { - String version = req.getHeader(Names.SEC_WEBSOCKET_VERSION); + String version = req.headers().get(Names.SEC_WEBSOCKET_VERSION); if (version != null) { if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) { @@ -115,7 +115,7 @@ public class WebSocketServerHandshakerFactory { HttpVersion.HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS); res.setStatus(HttpResponseStatus.UPGRADE_REQUIRED); - res.setHeader(Names.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue()); + res.headers().set(Names.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue()); return channel.write(res); } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java index 97e395376f..7cb344a6b9 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java @@ -101,7 +101,7 @@ public class WebSocketServerProtocolHandshakeHandler extends SimpleChannelUpstre // SSL in use so use Secure WebSockets protocol = "wss"; } - return protocol + "://" + req.getHeader(HttpHeaders.Names.HOST) + path; + return protocol + "://" + req.headers().get(HttpHeaders.Names.HOST) + path; } } diff --git a/src/main/java/org/jboss/netty/handler/codec/rtsp/RtspMessageDecoder.java b/src/main/java/org/jboss/netty/handler/codec/rtsp/RtspMessageDecoder.java index 95d1542bb7..923a58edee 100644 --- a/src/main/java/org/jboss/netty/handler/codec/rtsp/RtspMessageDecoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/rtsp/RtspMessageDecoder.java @@ -93,7 +93,7 @@ public abstract class RtspMessageDecoder extends HttpMessageDecoder { if (empty) { return true; } - if (!msg.containsHeader(RtspHeaders.Names.CONTENT_LENGTH)) { + if (!msg.headers().contains(RtspHeaders.Names.CONTENT_LENGTH)) { return true; } return empty; diff --git a/src/main/java/org/jboss/netty/handler/codec/spdy/DefaultSpdyHeaders.java b/src/main/java/org/jboss/netty/handler/codec/spdy/DefaultSpdyHeaders.java new file mode 100644 index 0000000000..8548b4a22e --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/codec/spdy/DefaultSpdyHeaders.java @@ -0,0 +1,379 @@ +/* + * Copyright 2013 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.handler.codec.spdy; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.TreeSet; +import java.util.Map.Entry; + + +public class DefaultSpdyHeaders extends SpdyHeaders { + + private static final int BUCKET_SIZE = 17; + + private static int hash(String name) { + int h = 0; + for (int i = name.length() - 1; i >= 0; i --) { + char c = name.charAt(i); + if (c >= 'A' && c <= 'Z') { + c += 32; + } + h = 31 * h + c; + } + + if (h > 0) { + return h; + } else if (h == Integer.MIN_VALUE) { + return Integer.MAX_VALUE; + } else { + return -h; + } + } + + private static boolean eq(String name1, String name2) { + 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; + } + + private static int index(int hash) { + return hash % BUCKET_SIZE; + } + + private final HeaderEntry[] entries = new HeaderEntry[BUCKET_SIZE]; + private final HeaderEntry head = new HeaderEntry(-1, null, null); + + DefaultSpdyHeaders() { + head.before = head.after = head; + } + + @Override + public SpdyHeaders add(final String name, final Object value) { + String lowerCaseName = name.toLowerCase(); + SpdyCodecUtil.validateHeaderName(lowerCaseName); + String strVal = toString(value); + SpdyCodecUtil.validateHeaderValue(strVal); + int h = hash(lowerCaseName); + int i = index(h); + add0(h, i, lowerCaseName, strVal); + return this; + } + + private void add0(int h, int i, final String name, final String 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); + } + + @Override + public SpdyHeaders remove(final String name) { + if (name == null) { + throw new NullPointerException("name"); + } + String lowerCaseName = name.toLowerCase(); + int h = hash(lowerCaseName); + int i = index(h); + remove0(h, i, lowerCaseName); + return this; + } + + private void remove0(int h, int i, String name) { + HeaderEntry e = entries[i]; + if (e == null) { + return; + } + + for (;;) { + if (e.hash == h && eq(name, e.key)) { + e.remove(); + HeaderEntry next = e.next; + if (next != null) { + entries[i] = next; + e = next; + } else { + entries[i] = null; + return; + } + } else { + break; + } + } + + for (;;) { + HeaderEntry next = e.next; + if (next == null) { + break; + } + if (next.hash == h && eq(name, next.key)) { + e.next = next.next; + next.remove(); + } else { + e = next; + } + } + } + + @Override + public SpdyHeaders set(final String name, final Object value) { + String lowerCaseName = name.toLowerCase(); + SpdyCodecUtil.validateHeaderName(lowerCaseName); + String strVal = toString(value); + SpdyCodecUtil.validateHeaderValue(strVal); + int h = hash(lowerCaseName); + int i = index(h); + remove0(h, i, lowerCaseName); + add0(h, i, lowerCaseName, strVal); + return this; + } + + @Override + public SpdyHeaders set(final String name, final Iterable values) { + if (values == null) { + throw new NullPointerException("values"); + } + + String lowerCaseName = name.toLowerCase(); + SpdyCodecUtil.validateHeaderName(lowerCaseName); + + int h = hash(lowerCaseName); + int i = index(h); + + remove0(h, i, lowerCaseName); + for (Object v: values) { + if (v == null) { + break; + } + String strVal = toString(v); + SpdyCodecUtil.validateHeaderValue(strVal); + add0(h, i, lowerCaseName, strVal); + } + return this; + } + + @Override + public SpdyHeaders clear() { + for (int i = 0; i < entries.length; i ++) { + entries[i] = null; + } + head.before = head.after = head; + return this; + } + + @Override + public String get(final String name) { + if (name == null) { + throw new NullPointerException("name"); + } + + int h = hash(name); + int i = index(h); + HeaderEntry e = entries[i]; + while (e != null) { + if (e.hash == h && eq(name, e.key)) { + return e.value; + } + + e = e.next; + } + return null; + } + + @Override + public List getAll(final String name) { + if (name == null) { + throw new NullPointerException("name"); + } + + LinkedList values = new LinkedList(); + + int h = hash(name); + int i = index(h); + HeaderEntry e = entries[i]; + while (e != null) { + if (e.hash == h && eq(name, e.key)) { + values.addFirst(e.value); + } + e = e.next; + } + return values; + } + + @Override + public List> entries() { + List> all = + new LinkedList>(); + + HeaderEntry e = head.after; + while (e != head) { + all.add(e); + e = e.after; + } + return all; + } + + @Override + public Iterator> iterator() { + return new HeaderIterator(); + } + + @Override + public boolean contains(String name) { + return get(name) != null; + } + + @Override + public Set names() { + Set names = new TreeSet(); + + HeaderEntry e = head.after; + while (e != head) { + names.add(e.key); + e = e.after; + } + return names; + } + + @Override + public SpdyHeaders add(String name, Iterable values) { + SpdyCodecUtil.validateHeaderValue(name); + int h = hash(name); + int i = index(h); + for (Object v: values) { + String vstr = toString(v); + SpdyCodecUtil.validateHeaderValue(vstr); + add0(h, i, name, vstr); + } + return this; + } + + @Override + public boolean isEmpty() { + return head == head.after; + } + + private static String toString(Object value) { + if (value == null) { + return null; + } + return value.toString(); + } + + private 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(); + } + } + + private static final class HeaderEntry implements Map.Entry { + final int hash; + final String key; + String value; + HeaderEntry next; + HeaderEntry before, after; + + HeaderEntry(int hash, String key, String value) { + this.hash = hash; + this.key = key; + this.value = value; + } + + void remove() { + before.after = after; + after.before = before; + } + + void addBefore(HeaderEntry e) { + after = e; + before = e.before; + before.after = this; + after.before = this; + } + + @Override + public String getKey() { + return key; + } + + @Override + public String getValue() { + return value; + } + + @Override + public String setValue(String value) { + if (value == null) { + throw new NullPointerException("value"); + } + SpdyCodecUtil.validateHeaderValue(value); + String oldValue = this.value; + this.value = value; + return oldValue; + } + + @Override + public String toString() { + return key + '=' + value; + } + } +} diff --git a/src/main/java/org/jboss/netty/handler/codec/spdy/DefaultSpdyHeadersFrame.java b/src/main/java/org/jboss/netty/handler/codec/spdy/DefaultSpdyHeadersFrame.java index 1ccfd7d17f..4953f88fd3 100644 --- a/src/main/java/org/jboss/netty/handler/codec/spdy/DefaultSpdyHeadersFrame.java +++ b/src/main/java/org/jboss/netty/handler/codec/spdy/DefaultSpdyHeadersFrame.java @@ -29,7 +29,7 @@ public class DefaultSpdyHeadersFrame extends DefaultSpdyStreamFrame private boolean invalid; private boolean truncated; - private final SpdyHeaders headers = new SpdyHeaders(); + private final SpdyHeaders headers = new DefaultSpdyHeaders(); /** * Creates a new instance. @@ -56,44 +56,58 @@ public class DefaultSpdyHeadersFrame extends DefaultSpdyStreamFrame truncated = true; } + public SpdyHeaders headers() { + return headers; + } + + @Deprecated public void addHeader(final String name, final Object value) { - headers.addHeader(name, value); + headers.add(name, value); } + @Deprecated public void setHeader(final String name, final Object value) { - headers.setHeader(name, value); + headers.set(name, value); } + @Deprecated public void setHeader(final String name, final Iterable values) { - headers.setHeader(name, values); + headers.set(name, values); } + @Deprecated public void removeHeader(final String name) { - headers.removeHeader(name); + headers.remove(name); } + @Deprecated public void clearHeaders() { - headers.clearHeaders(); + headers.clear(); } + @Deprecated public String getHeader(final String name) { - return headers.getHeader(name); + return headers.get(name); } + @Deprecated public List getHeaders(final String name) { - return headers.getHeaders(name); + return headers.getAll(name); } + @Deprecated public List> getHeaders() { - return headers.getHeaders(); + return headers.entries(); } + @Deprecated public boolean containsHeader(final String name) { - return headers.containsHeader(name); + return headers.contains(name); } + @Deprecated public Set getHeaderNames() { - return headers.getHeaderNames(); + return headers.names(); } @Override @@ -117,7 +131,7 @@ public class DefaultSpdyHeadersFrame extends DefaultSpdyStreamFrame } protected void appendHeaders(StringBuilder buf) { - for (Map.Entry e: getHeaders()) { + for (Map.Entry e: headers()) { buf.append(" "); buf.append(e.getKey()); buf.append(": "); diff --git a/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHeaderBlockRawDecoder.java b/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHeaderBlockRawDecoder.java index 9cc4f6fb6b..17817b628d 100644 --- a/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHeaderBlockRawDecoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHeaderBlockRawDecoder.java @@ -98,7 +98,7 @@ public class SpdyHeaderBlockRawDecoder extends SpdyHeaderBlockDecoder { String name = new String(nameBytes, "UTF-8"); // Check for identically named headers - if (frame.containsHeader(name)) { + if (frame.headers().contains(name)) { frame.setInvalid(); return; } @@ -118,7 +118,7 @@ public class SpdyHeaderBlockRawDecoder extends SpdyHeaderBlockDecoder { // SPDY/3 allows zero-length (empty) header values if (valueLength == 0) { - frame.addHeader(name, ""); + frame.headers().add(name, ""); numHeaders --; this.headerSize = headerSize; continue; @@ -154,7 +154,7 @@ public class SpdyHeaderBlockRawDecoder extends SpdyHeaderBlockDecoder { String value = new String(valueBytes, offset, index - offset, "UTF-8"); try { - frame.addHeader(name, value); + frame.headers().add(name, value); } catch (IllegalArgumentException e) { // Name contains NULL or non-ascii characters frame.setInvalid(); diff --git a/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHeaderBlockRawEncoder.java b/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHeaderBlockRawEncoder.java index f7d2ae6661..af6ad52e06 100644 --- a/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHeaderBlockRawEncoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHeaderBlockRawEncoder.java @@ -35,24 +35,16 @@ public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder { } private void setLengthField(ChannelBuffer buffer, int writerIndex, int length) { - if (version < 3) { - buffer.setShort(writerIndex, length); - } else { - buffer.setInt(writerIndex, length); - } + buffer.setInt(writerIndex, length); } private void writeLengthField(ChannelBuffer buffer, int length) { - if (version < 3) { - buffer.writeShort(length); - } else { - buffer.writeInt(length); - } + buffer.writeInt(length); } @Override public ChannelBuffer encode(SpdyHeadersFrame headerFrame) throws Exception { - Set names = headerFrame.getHeaderNames(); + Set names = headerFrame.headers().names(); int numHeaders = names.size(); if (numHeaders == 0) { return ChannelBuffers.EMPTY_BUFFER; @@ -71,7 +63,7 @@ public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder { int savedIndex = headerBlock.writerIndex(); int valueLength = 0; writeLengthField(headerBlock, valueLength); - for (String value: headerFrame.getHeaders(name)) { + for (String value: headerFrame.headers().getAll(name)) { byte[] valueBytes = value.getBytes("UTF-8"); if (valueBytes.length > 0) { headerBlock.writeBytes(valueBytes); @@ -79,12 +71,7 @@ public class SpdyHeaderBlockRawEncoder extends SpdyHeaderBlockEncoder { valueLength += valueBytes.length + 1; } } - if (valueLength == 0) { - if (version < 3) { - throw new IllegalArgumentException( - "header value cannot be empty: " + name); - } - } else { + if (valueLength != 0) { valueLength --; } if (valueLength > SPDY_MAX_NV_LENGTH) { diff --git a/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHeaders.java b/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHeaders.java index e904417628..cfcc1a2081 100644 --- a/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHeaders.java +++ b/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHeaders.java @@ -19,22 +19,88 @@ import org.jboss.netty.handler.codec.http.HttpMethod; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import org.jboss.netty.handler.codec.http.HttpVersion; -import java.util.LinkedList; +import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeSet; /** * Provides the constants for the standard SPDY HTTP header names and commonly * used utility methods that access a {@link SpdyHeadersFrame}. - * @apiviz.stereotype static */ -public class SpdyHeaders { +public abstract class SpdyHeaders implements Iterable> { + + public static final SpdyHeaders EMPTY_HEADERS = new SpdyHeaders() { + + @Override + public List getAll(String name) { + return Collections.emptyList(); + } + + @Override + public List> entries() { + return Collections.emptyList(); + } + + @Override + public boolean contains(String name) { + return false; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public Set names() { + return Collections.emptySet(); + } + + @Override + public SpdyHeaders add(String name, Object value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public SpdyHeaders add(String name, Iterable values) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public SpdyHeaders set(String name, Object value) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public SpdyHeaders set(String name, Iterable values) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public SpdyHeaders remove(String name) { + throw new UnsupportedOperationException("read only"); + } + + @Override + public SpdyHeaders clear() { + throw new UnsupportedOperationException("read only"); + } + + @Override + public Iterator> iterator() { + return entries().iterator(); + } + + @Override + public String get(String name) { + return null; + } + }; /** * SPDY HTTP header names - * @apiviz.stereotype static */ public static final class HttpNames { /** @@ -62,8 +128,7 @@ public class SpdyHeaders { */ public static final String VERSION = ":version"; - private HttpNames() { - } + private HttpNames() { } } /** @@ -74,7 +139,7 @@ public class SpdyHeaders { * @return the header value or {@code null} if there is no such header */ public static String getHeader(SpdyHeadersFrame frame, String name) { - return frame.getHeader(name); + return frame.headers().get(name); } /** @@ -86,7 +151,7 @@ public class SpdyHeaders { * header */ public static String getHeader(SpdyHeadersFrame frame, String name, String defaultValue) { - String value = frame.getHeader(name); + String value = frame.headers().get(name); if (value == null) { return defaultValue; } @@ -98,7 +163,7 @@ public class SpdyHeaders { * existing header with the same name, the existing header is removed. */ public static void setHeader(SpdyHeadersFrame frame, String name, Object value) { - frame.setHeader(name, value); + frame.headers().set(name, value); } /** @@ -106,42 +171,42 @@ public class SpdyHeaders { * existing header with the same name, the existing header is removed. */ public static void setHeader(SpdyHeadersFrame frame, String name, Iterable values) { - frame.setHeader(name, values); + frame.headers().set(name, values); } /** * Adds a new header with the specified name and value. */ public static void addHeader(SpdyHeadersFrame frame, String name, Object value) { - frame.addHeader(name, value); + frame.headers().add(name, value); } /** * Removes the SPDY host header. */ public static void removeHost(SpdyHeadersFrame frame) { - frame.removeHeader(HttpNames.HOST); + frame.headers().remove(HttpNames.HOST); } /** * Returns the SPDY host header. */ public static String getHost(SpdyHeadersFrame frame) { - return frame.getHeader(HttpNames.HOST); + return frame.headers().get(HttpNames.HOST); } /** * Set the SPDY host header. */ public static void setHost(SpdyHeadersFrame frame, String host) { - frame.setHeader(HttpNames.HOST, host); + frame.headers().set(HttpNames.HOST, host); } /** * Removes the HTTP method header. */ public static void removeMethod(int spdyVersion, SpdyHeadersFrame frame) { - frame.removeHeader(HttpNames.METHOD); + frame.headers().remove(HttpNames.METHOD); } /** @@ -149,7 +214,7 @@ public class SpdyHeaders { */ public static HttpMethod getMethod(int spdyVersion, SpdyHeadersFrame frame) { try { - return HttpMethod.valueOf(frame.getHeader(HttpNames.METHOD)); + return HttpMethod.valueOf(frame.headers().get(HttpNames.METHOD)); } catch (Exception e) { return null; } @@ -159,35 +224,35 @@ public class SpdyHeaders { * Sets the HTTP method header. */ public static void setMethod(int spdyVersion, SpdyHeadersFrame frame, HttpMethod method) { - frame.setHeader(HttpNames.METHOD, method.getName()); + frame.headers().set(HttpNames.METHOD, method.getName()); } /** * Removes the URL scheme header. */ public static void removeScheme(int spdyVersion, SpdyHeadersFrame frame) { - frame.removeHeader(HttpNames.SCHEME); + frame.headers().remove(HttpNames.SCHEME); } /** * Returns the value of the URL scheme header. */ public static String getScheme(int spdyVersion, SpdyHeadersFrame frame) { - return frame.getHeader(HttpNames.SCHEME); + return frame.headers().get(HttpNames.SCHEME); } /** * Sets the URL scheme header. */ public static void setScheme(int spdyVersion, SpdyHeadersFrame frame, String scheme) { - frame.setHeader(HttpNames.SCHEME, scheme); + frame.headers().set(HttpNames.SCHEME, scheme); } /** * Removes the HTTP response status header. */ public static void removeStatus(int spdyVersion, SpdyHeadersFrame frame) { - frame.removeHeader(HttpNames.STATUS); + frame.headers().remove(HttpNames.STATUS); } /** @@ -195,7 +260,7 @@ public class SpdyHeaders { */ public static HttpResponseStatus getStatus(int spdyVersion, SpdyHeadersFrame frame) { try { - String status = frame.getHeader(HttpNames.STATUS); + String status = frame.headers().get(HttpNames.STATUS); int space = status.indexOf(' '); if (space == -1) { return HttpResponseStatus.valueOf(Integer.parseInt(status)); @@ -218,35 +283,35 @@ public class SpdyHeaders { * Sets the HTTP response status header. */ public static void setStatus(int spdyVersion, SpdyHeadersFrame frame, HttpResponseStatus status) { - frame.setHeader(HttpNames.STATUS, status.toString()); + frame.headers().set(HttpNames.STATUS, status.toString()); } /** * Removes the URL path header. */ public static void removeUrl(int spdyVersion, SpdyHeadersFrame frame) { - frame.removeHeader(HttpNames.PATH); + frame.headers().remove(HttpNames.PATH); } /** * Returns the value of the URL path header. */ public static String getUrl(int spdyVersion, SpdyHeadersFrame frame) { - return frame.getHeader(HttpNames.PATH); + return frame.headers().get(HttpNames.PATH); } /** * Sets the URL path header. */ public static void setUrl(int spdyVersion, SpdyHeadersFrame frame, String path) { - frame.setHeader(HttpNames.PATH, path); + frame.headers().set(HttpNames.PATH, path); } /** * Removes the HTTP version header. */ public static void removeVersion(int spdyVersion, SpdyHeadersFrame frame) { - frame.removeHeader(HttpNames.VERSION); + frame.headers().remove(HttpNames.VERSION); } /** @@ -254,7 +319,7 @@ public class SpdyHeaders { */ public static HttpVersion getVersion(int spdyVersion, SpdyHeadersFrame frame) { try { - return HttpVersion.valueOf(frame.getHeader(HttpNames.VERSION)); + return HttpVersion.valueOf(frame.headers().get(HttpNames.VERSION)); } catch (Exception e) { return null; } @@ -264,289 +329,85 @@ public class SpdyHeaders { * Sets the HTTP version header. */ public static void setVersion(int spdyVersion, SpdyHeadersFrame frame, HttpVersion httpVersion) { - frame.setHeader(HttpNames.VERSION, httpVersion.getText()); + frame.headers().set(HttpNames.VERSION, httpVersion.getText()); } - private static final int BUCKET_SIZE = 17; - - private static int hash(String name) { - int h = 0; - for (int i = name.length() - 1; i >= 0; i --) { - char c = name.charAt(i); - if (c >= 'A' && c <= 'Z') { - c += 32; - } - h = 31 * h + c; - } - - if (h > 0) { - return h; - } else if (h == Integer.MIN_VALUE) { - return Integer.MAX_VALUE; - } else { - return -h; - } + @Override + public Iterator> iterator() { + return entries().iterator(); } - private static boolean eq(String name1, String name2) { - int nameLen = name1.length(); - if (nameLen != name2.length()) { - return false; - } + /** + * Returns the header value with the specified header name. If there is + * more than one header value for the specified header name, the first + * value is returned. + * + * @return the header value or {@code null} if there is no such header + */ + public abstract String get(String name); - 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; - } + /** + * Returns the header values with the specified header name. + * + * @return the {@link List} of header values. An empty list if there is no + * such header. + */ + public abstract List getAll(String name); - private static int index(int hash) { - return hash % BUCKET_SIZE; - } + /** + * Returns all header names and values that this frame contains. + * + * @return the {@link List} of the header name-value pairs. An empty list + * if there is no header in this message. + */ + public abstract List> entries(); - private final HeaderEntry[] entries = new HeaderEntry[BUCKET_SIZE]; - private final HeaderEntry head = new HeaderEntry(-1, null, null); + /** + * Returns {@code true} if and only if there is a header with the specified + * header name. + */ + public abstract boolean contains(String name); - SpdyHeaders() { - head.before = head.after = head; - } + /** + * Returns the {@link Set} of all header names that this frame contains. + */ + public abstract Set names(); - void addHeader(final String name, final Object value) { - String lowerCaseName = name.toLowerCase(); - SpdyCodecUtil.validateHeaderName(lowerCaseName); - String strVal = toString(value); - SpdyCodecUtil.validateHeaderValue(strVal); - int h = hash(lowerCaseName); - int i = index(h); - addHeader0(h, i, lowerCaseName, strVal); - } + /** + * Adds a new header with the specified name and value. + */ + public abstract SpdyHeaders add(String name, Object value); - private void addHeader0(int h, int i, final String name, final String value) { - // Update the hash table. - HeaderEntry e = entries[i]; - HeaderEntry newEntry; - entries[i] = newEntry = new HeaderEntry(h, name, value); - newEntry.next = e; + /** + * Adds a new header with the specified name and values. If there is an + * existing header with the same name, the existing header is removed. + */ + public abstract SpdyHeaders add(String name, Iterable values); - // Update the linked list. - newEntry.addBefore(head); - } + /** + * Sets a new header with the specified name and value. If there is an + * existing header with the same name, the existing header is removed. + */ + public abstract SpdyHeaders set(String name, Object value); - void removeHeader(final String name) { - if (name == null) { - throw new NullPointerException("name"); - } - String lowerCaseName = name.toLowerCase(); - int h = hash(lowerCaseName); - int i = index(h); - removeHeader0(h, i, lowerCaseName); - } + /** + * Sets a new header with the specified name and values. If there is an + * existing header with the same name, the existing header is removed. + */ + public abstract SpdyHeaders set(String name, Iterable values); - private void removeHeader0(int h, int i, String name) { - HeaderEntry e = entries[i]; - if (e == null) { - return; - } + /** + * Removes the header with the specified name. + */ + public abstract SpdyHeaders remove(String name); - for (;;) { - if (e.hash == h && eq(name, e.key)) { - e.remove(); - HeaderEntry next = e.next; - if (next != null) { - entries[i] = next; - e = next; - } else { - entries[i] = null; - return; - } - } else { - break; - } - } + /** + * Removes all headers from this frame. + */ + public abstract SpdyHeaders clear(); - for (;;) { - HeaderEntry next = e.next; - if (next == null) { - break; - } - if (next.hash == h && eq(name, next.key)) { - e.next = next.next; - next.remove(); - } else { - e = next; - } - } - } - - void setHeader(final String name, final Object value) { - String lowerCaseName = name.toLowerCase(); - SpdyCodecUtil.validateHeaderName(lowerCaseName); - String strVal = toString(value); - SpdyCodecUtil.validateHeaderValue(strVal); - int h = hash(lowerCaseName); - int i = index(h); - removeHeader0(h, i, lowerCaseName); - addHeader0(h, i, lowerCaseName, strVal); - } - - void setHeader(final String name, final Iterable values) { - if (values == null) { - throw new NullPointerException("values"); - } - - String lowerCaseName = name.toLowerCase(); - SpdyCodecUtil.validateHeaderName(lowerCaseName); - - int h = hash(lowerCaseName); - int i = index(h); - - removeHeader0(h, i, lowerCaseName); - for (Object v: values) { - if (v == null) { - break; - } - String strVal = toString(v); - SpdyCodecUtil.validateHeaderValue(strVal); - addHeader0(h, i, lowerCaseName, strVal); - } - } - - void clearHeaders() { - for (int i = 0; i < entries.length; i ++) { - entries[i] = null; - } - head.before = head.after = head; - } - - String getHeader(final String name) { - if (name == null) { - throw new NullPointerException("name"); - } - - int h = hash(name); - int i = index(h); - HeaderEntry e = entries[i]; - while (e != null) { - if (e.hash == h && eq(name, e.key)) { - return e.value; - } - - e = e.next; - } - return null; - } - - List getHeaders(final String name) { - if (name == null) { - throw new NullPointerException("name"); - } - - LinkedList values = new LinkedList(); - - int h = hash(name); - int i = index(h); - HeaderEntry e = entries[i]; - while (e != null) { - if (e.hash == h && eq(name, e.key)) { - values.addFirst(e.value); - } - e = e.next; - } - return values; - } - - List> getHeaders() { - List> all = - new LinkedList>(); - - HeaderEntry e = head.after; - while (e != head) { - all.add(e); - e = e.after; - } - return all; - } - - boolean containsHeader(String name) { - return getHeader(name) != null; - } - - Set getHeaderNames() { - Set names = new TreeSet(); - - HeaderEntry e = head.after; - while (e != head) { - names.add(e.key); - e = e.after; - } - return names; - } - - private static String toString(Object value) { - if (value == null) { - return null; - } - return value.toString(); - } - - private static final class HeaderEntry implements Map.Entry { - final int hash; - final String key; - String value; - HeaderEntry next; - HeaderEntry before, after; - - HeaderEntry(int hash, String key, String value) { - this.hash = hash; - this.key = key; - this.value = value; - } - - void remove() { - before.after = after; - after.before = before; - } - - void addBefore(HeaderEntry e) { - after = e; - before = e.before; - before.after = this; - after.before = this; - } - - public String getKey() { - return key; - } - - public String getValue() { - return value; - } - - public String setValue(String value) { - if (value == null) { - throw new NullPointerException("value"); - } - SpdyCodecUtil.validateHeaderValue(value); - String oldValue = this.value; - this.value = value; - return oldValue; - } - - @Override - public String toString() { - return key + '=' + value; - } - } + /** + * Checks if no header exists. + */ + public abstract boolean isEmpty(); } diff --git a/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHeadersFrame.java b/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHeadersFrame.java index 4cd56e7f66..3650373c1e 100644 --- a/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHeadersFrame.java +++ b/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHeadersFrame.java @@ -47,65 +47,67 @@ public interface SpdyHeadersFrame extends SpdyStreamFrame { void setTruncated(); /** - * Returns the header value with the specified header name. If there is - * more than one header value for the specified header name, the first - * value is returned. - * - * @return the header value or {@code null} if there is no such header + * Returns the {@link SpdyHeaders}. */ + SpdyHeaders headers(); + + /** + * @deprecated Use {@link SpdyHeaders#headers()} instead. + */ + @Deprecated String getHeader(String name); /** - * Returns the header values with the specified header name. - * - * @return the {@link List} of header values. An empty list if there is no - * such header. + * @deprecated Use {@link SpdyHeaders#headers()} instead. */ + @Deprecated List getHeaders(String name); /** - * Returns all header names and values that this block contains. - * - * @return the {@link List} of the header name-value pairs. An empty list - * if there is no header in this message. + * @deprecated Use {@link SpdyHeaders#headers()} instead. */ + @Deprecated List> getHeaders(); /** - * Returns {@code true} if and only if there is a header with the specified - * header name. + * @deprecated Use {@link SpdyHeaders#headers()} instead. */ + @Deprecated boolean containsHeader(String name); /** - * Returns the {@link Set} of all header names that this block contains. + * @deprecated Use {@link SpdyHeaders#headers()} instead. */ + @Deprecated Set getHeaderNames(); /** - * Adds a new header with the specified name and value. + * @deprecated Use {@link SpdyHeaders#headers()} instead. */ + @Deprecated void addHeader(String name, Object value); /** - * Sets a new header with the specified name and value. If there is an - * existing header with the same name, the existing header is removed. + * @deprecated Use {@link SpdyHeaders#headers()} instead. */ + @Deprecated void setHeader(String name, Object value); /** - * Sets a new header with the specified name and values. If there is an - * existing header with the same name, the existing header is removed. + * @deprecated Use {@link SpdyHeaders#headers()} instead. */ + @Deprecated void setHeader(String name, Iterable values); /** - * Removes the header with the specified name. + * @deprecated Use {@link SpdyHeaders#headers()} instead. */ + @Deprecated void removeHeader(String name); /** - * Removes all headers from this block. + * @deprecated Use {@link SpdyHeaders#headers()} instead. */ + @Deprecated void clearHeaders(); } diff --git a/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHttpDecoder.java b/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHttpDecoder.java index 9ec3432340..c37fe3b248 100644 --- a/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHttpDecoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHttpDecoder.java @@ -240,8 +240,8 @@ public class SpdyHttpDecoder extends OneToOneDecoder { // Ignore trailers in a truncated HEADERS frame. if (!spdyHeadersFrame.isTruncated()) { - for (Map.Entry e : spdyHeadersFrame.getHeaders()) { - httpMessage.addHeader(e.getKey(), e.getValue()); + for (Map.Entry e : spdyHeadersFrame.headers()) { + httpMessage.headers().add(e.getKey(), e.getValue()); } } @@ -315,15 +315,15 @@ public class SpdyHttpDecoder extends OneToOneDecoder { HttpHeaders.setHost(httpRequest, host); } - for (Map.Entry e: requestFrame.getHeaders()) { - httpRequest.addHeader(e.getKey(), e.getValue()); + for (Map.Entry e: requestFrame.headers()) { + httpRequest.headers().add(e.getKey(), e.getValue()); } // The Connection and Keep-Alive headers are no longer valid HttpHeaders.setKeepAlive(httpRequest, true); // Transfer-Encoding header is not valid - httpRequest.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING); + httpRequest.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING); return httpRequest; } @@ -337,16 +337,16 @@ public class SpdyHttpDecoder extends OneToOneDecoder { SpdyHeaders.removeVersion(spdyVersion, responseFrame); HttpResponse httpResponse = new DefaultHttpResponse(version, status); - for (Map.Entry e: responseFrame.getHeaders()) { - httpResponse.addHeader(e.getKey(), e.getValue()); + for (Map.Entry e: responseFrame.headers()) { + httpResponse.headers().add(e.getKey(), e.getValue()); } // The Connection and Keep-Alive headers are no longer valid HttpHeaders.setKeepAlive(httpResponse, true); // Transfer-Encoding header is not valid - httpResponse.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING); - httpResponse.removeHeader(HttpHeaders.Names.TRAILER); + httpResponse.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING); + httpResponse.headers().remove(HttpHeaders.Names.TRAILER); return httpResponse; } diff --git a/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHttpEncoder.java b/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHttpEncoder.java index d837d34aed..e3fee1d743 100644 --- a/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHttpEncoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHttpEncoder.java @@ -164,7 +164,7 @@ public class SpdyHttpEncoder implements ChannelDownstreamHandler { } else if (msg instanceof HttpResponse) { HttpResponse httpResponse = (HttpResponse) msg; - if (httpResponse.containsHeader(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID)) { + if (httpResponse.headers().contains(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID)) { SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpResponse); currentStreamId = spdySynStreamFrame.getStreamId(); ChannelFuture future = getMessageFuture(ctx, e, currentStreamId, httpResponse); @@ -196,7 +196,7 @@ public class SpdyHttpEncoder implements ChannelDownstreamHandler { if (chunk.isLast()) { if (chunk instanceof HttpChunkTrailer) { HttpChunkTrailer trailer = (HttpChunkTrailer) chunk; - List> trailers = trailer.getHeaders(); + HttpHeaders trailers = trailer.trailingHeaders(); if (trailers.isEmpty()) { SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamId); spdyDataFrame.setLast(true); @@ -206,7 +206,7 @@ public class SpdyHttpEncoder implements ChannelDownstreamHandler { SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamId); spdyHeadersFrame.setLast(true); for (Map.Entry entry: trailers) { - spdyHeadersFrame.addHeader(entry.getKey(), entry.getValue()); + spdyHeadersFrame.headers().add(entry.getKey(), entry.getValue()); } Channels.write(ctx, future, spdyHeadersFrame, remoteAddress); } @@ -292,10 +292,10 @@ public class SpdyHttpEncoder implements ChannelDownstreamHandler { // The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding // headers are not valid and MUST not be sent. - httpMessage.removeHeader(HttpHeaders.Names.CONNECTION); - httpMessage.removeHeader("Keep-Alive"); - httpMessage.removeHeader("Proxy-Connection"); - httpMessage.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING); + httpMessage.headers().remove(HttpHeaders.Names.CONNECTION); + httpMessage.headers().remove("Keep-Alive"); + httpMessage.headers().remove("Proxy-Connection"); + httpMessage.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING); SpdySynStreamFrame spdySynStreamFrame = new DefaultSpdySynStreamFrame(streamId, associatedToStreamId, priority); @@ -317,11 +317,9 @@ public class SpdyHttpEncoder implements ChannelDownstreamHandler { } // Replace the HTTP host header with the SPDY host header - if (spdyVersion >= 3) { - String host = HttpHeaders.getHost(httpMessage); - httpMessage.removeHeader(HttpHeaders.Names.HOST); - SpdyHeaders.setHost(spdySynStreamFrame, host); - } + String host = HttpHeaders.getHost(httpMessage); + httpMessage.headers().remove(HttpHeaders.Names.HOST); + SpdyHeaders.setHost(spdySynStreamFrame, host); // Set the SPDY scheme header if (scheme == null) { @@ -330,8 +328,8 @@ public class SpdyHttpEncoder implements ChannelDownstreamHandler { SpdyHeaders.setScheme(spdyVersion, spdySynStreamFrame, scheme); // Transfer the remaining HTTP headers - for (Map.Entry entry: httpMessage.getHeaders()) { - spdySynStreamFrame.addHeader(entry.getKey(), entry.getValue()); + for (Map.Entry entry: httpMessage.headers()) { + spdySynStreamFrame.headers().add(entry.getKey(), entry.getValue()); } return spdySynStreamFrame; @@ -347,10 +345,10 @@ public class SpdyHttpEncoder implements ChannelDownstreamHandler { // The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding // headers are not valid and MUST not be sent. - httpResponse.removeHeader(HttpHeaders.Names.CONNECTION); - httpResponse.removeHeader("Keep-Alive"); - httpResponse.removeHeader("Proxy-Connection"); - httpResponse.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING); + httpResponse.headers().remove(HttpHeaders.Names.CONNECTION); + httpResponse.headers().remove("Keep-Alive"); + httpResponse.headers().remove("Proxy-Connection"); + httpResponse.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING); SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId); spdySynReplyFrame.setLast(!chunked && !httpResponse.getContent().readable()); @@ -360,8 +358,8 @@ public class SpdyHttpEncoder implements ChannelDownstreamHandler { SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, httpResponse.getProtocolVersion()); // Transfer the remaining HTTP headers - for (Map.Entry entry: httpResponse.getHeaders()) { - spdySynReplyFrame.addHeader(entry.getKey(), entry.getValue()); + for (Map.Entry entry: httpResponse.headers()) { + spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue()); } return spdySynReplyFrame; diff --git a/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHttpHeaders.java b/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHttpHeaders.java index 629a4dbc4e..57bc637b7b 100644 --- a/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHttpHeaders.java +++ b/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHttpHeaders.java @@ -62,7 +62,7 @@ public final class SpdyHttpHeaders { * Removes the {@code "X-SPDY-Stream-ID"} header. */ public static void removeStreamId(HttpMessage message) { - message.removeHeader(Names.STREAM_ID); + message.headers().remove(Names.STREAM_ID); } /** @@ -83,7 +83,7 @@ public final class SpdyHttpHeaders { * Removes the {@code "X-SPDY-Associated-To-Stream-ID"} header. */ public static void removeAssociatedToStreamId(HttpMessage message) { - message.removeHeader(Names.ASSOCIATED_TO_STREAM_ID); + message.headers().remove(Names.ASSOCIATED_TO_STREAM_ID); } /** @@ -107,7 +107,7 @@ public final class SpdyHttpHeaders { * Removes the {@code "X-SPDY-Priority"} header. */ public static void removePriority(HttpMessage message) { - message.removeHeader(Names.PRIORITY); + message.headers().remove(Names.PRIORITY); } /** @@ -131,41 +131,41 @@ public final class SpdyHttpHeaders { * Removes the {@code "X-SPDY-URL"} header. */ public static void removeUrl(HttpMessage message) { - message.removeHeader(Names.URL); + message.headers().remove(Names.URL); } /** * Returns the value of the {@code "X-SPDY-URL"} header. */ public static String getUrl(HttpMessage message) { - return message.getHeader(Names.URL); + return message.headers().get(Names.URL); } /** * Sets the {@code "X-SPDY-URL"} header. */ public static void setUrl(HttpMessage message, String url) { - message.setHeader(Names.URL, url); + message.headers().set(Names.URL, url); } /** * Removes the {@code "X-SPDY-Scheme"} header. */ public static void removeScheme(HttpMessage message) { - message.removeHeader(Names.SCHEME); + message.headers().remove(Names.SCHEME); } /** * Returns the value of the {@code "X-SPDY-Scheme"} header. */ public static String getScheme(HttpMessage message) { - return message.getHeader(Names.SCHEME); + return message.headers().get(Names.SCHEME); } /** * Sets the {@code "X-SPDY-Scheme"} header. */ public static void setScheme(HttpMessage message, String scheme) { - message.setHeader(Names.SCHEME, scheme); + message.headers().set(Names.SCHEME, scheme); } } diff --git a/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHttpResponseStreamIdHandler.java b/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHttpResponseStreamIdHandler.java index ea88f71355..b9b43a98df 100644 --- a/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHttpResponseStreamIdHandler.java +++ b/src/main/java/org/jboss/netty/handler/codec/spdy/SpdyHttpResponseStreamIdHandler.java @@ -36,7 +36,7 @@ public class SpdyHttpResponseStreamIdHandler extends SimpleChannelHandler { @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { if (e.getMessage() instanceof HttpMessage) { - boolean contains = ((HttpMessage) e.getMessage()).containsHeader(SpdyHttpHeaders.Names.STREAM_ID); + boolean contains = ((HttpMessage) e.getMessage()).headers().contains(SpdyHttpHeaders.Names.STREAM_ID); if (!contains) { ids.add(NO_ID); } else { @@ -54,7 +54,7 @@ public class SpdyHttpResponseStreamIdHandler extends SimpleChannelHandler { if (e.getMessage() instanceof HttpResponse) { HttpResponse response = (HttpResponse) e.getMessage(); Integer id = ids.poll(); - if (id != null && id.intValue() != NO_ID && !response.containsHeader(SpdyHttpHeaders.Names.STREAM_ID)) { + if (id != null && id.intValue() != NO_ID && !response.headers().contains(SpdyHttpHeaders.Names.STREAM_ID)) { SpdyHttpHeaders.setStreamId(response, id); } } diff --git a/src/test/java/org/jboss/netty/handler/codec/http/DefaultHttpMessageTest.java b/src/test/java/org/jboss/netty/handler/codec/http/DefaultHttpMessageTest.java index ffd8622ff3..2819c6ab1f 100644 --- a/src/test/java/org/jboss/netty/handler/codec/http/DefaultHttpMessageTest.java +++ b/src/test/java/org/jboss/netty/handler/codec/http/DefaultHttpMessageTest.java @@ -27,20 +27,20 @@ public class DefaultHttpMessageTest { // Insert sample keys. for (int i = 0; i < 1000; i ++) { - m.setHeader(String.valueOf(i), ""); + m.headers().set(String.valueOf(i), ""); } // Remove in reversed order. for (int i = 999; i >= 0; i --) { - m.removeHeader(String.valueOf(i)); + m.headers().remove(String.valueOf(i)); } // Check if random access returns nothing. for (int i = 0; i < 1000; i ++) { - Assert.assertNull(m.getHeader(String.valueOf(i))); + Assert.assertNull(m.headers().get(String.valueOf(i))); } // Check if sequential access returns nothing. - Assert.assertTrue(m.getHeaders().isEmpty()); + Assert.assertTrue(m.headers().entries().isEmpty()); } } diff --git a/src/test/java/org/jboss/netty/handler/codec/http/HttpChunkAggregatorTest.java b/src/test/java/org/jboss/netty/handler/codec/http/HttpChunkAggregatorTest.java index 94e4b16770..e07e018db5 100644 --- a/src/test/java/org/jboss/netty/handler/codec/http/HttpChunkAggregatorTest.java +++ b/src/test/java/org/jboss/netty/handler/codec/http/HttpChunkAggregatorTest.java @@ -37,7 +37,7 @@ public class HttpChunkAggregatorTest { HttpChunkAggregator aggr = new HttpChunkAggregator(1024 * 1024); DecoderEmbedder embedder = new DecoderEmbedder(aggr); HttpMessage message = new DefaultHttpMessage(HttpVersion.HTTP_1_1); - HttpHeaders.setHeader(message, "X-Test", true); + message.headers().set("X-Test", true); message.setChunked(true); HttpChunk chunk1 = new DefaultHttpChunk(ChannelBuffers.copiedBuffer("test", CharsetUtil.US_ASCII)); HttpChunk chunk2 = new DefaultHttpChunk(ChannelBuffers.copiedBuffer("test2", CharsetUtil.US_ASCII)); @@ -53,7 +53,7 @@ public class HttpChunkAggregatorTest { assertNotNull(aggratedMessage); assertEquals(chunk1.getContent().readableBytes() + chunk2.getContent().readableBytes(), HttpHeaders.getContentLength(aggratedMessage)); - assertEquals(aggratedMessage.getHeader("X-Test"), Boolean.TRUE.toString()); + assertEquals(aggratedMessage.headers().get("X-Test"), Boolean.TRUE.toString()); checkContentBuffer(aggratedMessage); assertNull(embedder.poll()); @@ -75,12 +75,12 @@ public class HttpChunkAggregatorTest { HttpChunkAggregator aggr = new HttpChunkAggregator(1024 * 1024); DecoderEmbedder embedder = new DecoderEmbedder(aggr); HttpMessage message = new DefaultHttpMessage(HttpVersion.HTTP_1_1); - HttpHeaders.setHeader(message, "X-Test", true); + message.headers().set("X-Test", true); message.setChunked(true); HttpChunk chunk1 = new DefaultHttpChunk(ChannelBuffers.copiedBuffer("test", CharsetUtil.US_ASCII)); HttpChunk chunk2 = new DefaultHttpChunk(ChannelBuffers.copiedBuffer("test2", CharsetUtil.US_ASCII)); HttpChunkTrailer trailer = new DefaultHttpChunkTrailer(); - trailer.setHeader("X-Trailer", true); + trailer.trailingHeaders().set("X-Trailer", true); assertFalse(embedder.offer(message)); assertFalse(embedder.offer(chunk1)); @@ -93,8 +93,8 @@ public class HttpChunkAggregatorTest { assertNotNull(aggratedMessage); assertEquals(chunk1.getContent().readableBytes() + chunk2.getContent().readableBytes(), HttpHeaders.getContentLength(aggratedMessage)); - assertEquals(aggratedMessage.getHeader("X-Test"), Boolean.TRUE.toString()); - assertEquals(aggratedMessage.getHeader("X-Trailer"), Boolean.TRUE.toString()); + assertEquals(aggratedMessage.headers().get("X-Test"), Boolean.TRUE.toString()); + assertEquals(aggratedMessage.headers().get("X-Trailer"), Boolean.TRUE.toString()); checkContentBuffer(aggratedMessage); assertNull(embedder.poll()); @@ -167,8 +167,8 @@ public class HttpChunkAggregatorTest { HttpChunkAggregator aggr = new HttpChunkAggregator(1024 * 1024); DecoderEmbedder embedder = new DecoderEmbedder(aggr); HttpMessage message = new DefaultHttpMessage(HttpVersion.HTTP_1_1); - HttpHeaders.setHeader(message, "X-Test", true); - HttpHeaders.setHeader(message, "Transfer-Encoding", "Chunked"); + message.headers().set("X-Test", true); + message.headers().set("Transfer-Encoding", "Chunked"); message.setChunked(true); HttpChunk chunk1 = new DefaultHttpChunk(ChannelBuffers.copiedBuffer("test", CharsetUtil.US_ASCII)); HttpChunk chunk2 = new DefaultHttpChunk(ChannelBuffers.copiedBuffer("test2", CharsetUtil.US_ASCII)); @@ -184,7 +184,7 @@ public class HttpChunkAggregatorTest { assertNotNull(aggratedMessage); assertEquals(chunk1.getContent().readableBytes() + chunk2.getContent().readableBytes(), HttpHeaders.getContentLength(aggratedMessage)); - assertEquals(aggratedMessage.getHeader("X-Test"), Boolean.TRUE.toString()); + assertEquals(aggratedMessage.headers().get("X-Test"), Boolean.TRUE.toString()); checkContentBuffer(aggratedMessage); assertNull(embedder.poll()); } diff --git a/src/test/java/org/jboss/netty/handler/codec/http/multipart/HttpPostRequestDecoderTest.java b/src/test/java/org/jboss/netty/handler/codec/http/multipart/HttpPostRequestDecoderTest.java index e9b9e7668d..b4df39119c 100644 --- a/src/test/java/org/jboss/netty/handler/codec/http/multipart/HttpPostRequestDecoderTest.java +++ b/src/test/java/org/jboss/netty/handler/codec/http/multipart/HttpPostRequestDecoderTest.java @@ -37,8 +37,8 @@ public class HttpPostRequestDecoderTest { final DefaultHttpRequest req = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "http://localhost"); req.setContent(ChannelBuffers.EMPTY_BUFFER); - req.setHeader(HttpHeaders.Names.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); - req.setHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED); + req.headers().set(HttpHeaders.Names.CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); + req.headers().set(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED); req.setChunked(true); // Force to use memory-based data. diff --git a/src/test/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketRequestBuilder.java b/src/test/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketRequestBuilder.java index bbb2678ef5..e51c5f0004 100644 --- a/src/test/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketRequestBuilder.java +++ b/src/test/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketRequestBuilder.java @@ -98,22 +98,22 @@ public class WebSocketRequestBuilder { public HttpRequest build() { HttpRequest req = new DefaultHttpRequest(httpVersion, method, uri); if (host != null) { - req.setHeader(Names.HOST, host); + req.headers().set(Names.HOST, host); } if (upgrade != null) { - req.setHeader(Names.UPGRADE, upgrade); + req.headers().set(Names.UPGRADE, upgrade); } if (connection != null) { - req.setHeader(Names.CONNECTION, connection); + req.headers().set(Names.CONNECTION, connection); } if (key != null) { - req.setHeader(Names.SEC_WEBSOCKET_KEY, key); + req.headers().set(Names.SEC_WEBSOCKET_KEY, key); } if (origin != null) { - req.setHeader(Names.SEC_WEBSOCKET_ORIGIN, origin); + req.headers().set(Names.SEC_WEBSOCKET_ORIGIN, origin); } if (version != null) { - req.setHeader(Names.SEC_WEBSOCKET_VERSION, version.toHttpHeaderValue()); + req.headers().set(Names.SEC_WEBSOCKET_VERSION, version.toHttpHeaderValue()); } return req; } diff --git a/src/test/java/org/jboss/netty/handler/codec/spdy/SpdyFrameDecoderTest.java b/src/test/java/org/jboss/netty/handler/codec/spdy/SpdyFrameDecoderTest.java index 93098ff680..bf31e51706 100644 --- a/src/test/java/org/jboss/netty/handler/codec/spdy/SpdyFrameDecoderTest.java +++ b/src/test/java/org/jboss/netty/handler/codec/spdy/SpdyFrameDecoderTest.java @@ -104,7 +104,7 @@ public class SpdyFrameDecoderTest { } private static void addHeader(SpdyHeadersFrame frame, int headerNameSize, int headerValueSize) { - frame.addHeader("k", "v"); + frame.headers().add("k", "v"); StringBuilder headerName = new StringBuilder(); for (int i = 0; i < headerNameSize; i++) { headerName.append('h'); @@ -113,7 +113,7 @@ public class SpdyFrameDecoderTest { for (int i = 0; i < headerValueSize; i++) { headerValue.append('a'); } - frame.addHeader(headerName.toString(), headerValue.toString()); + frame.headers().add(headerName.toString(), headerValue.toString()); } protected ChannelFactory newClientSocketChannelFactory(Executor executor) { diff --git a/src/test/java/org/jboss/netty/handler/codec/spdy/SpdySessionHandlerTest.java b/src/test/java/org/jboss/netty/handler/codec/spdy/SpdySessionHandlerTest.java index 168dd69ee1..dde48e89cc 100644 --- a/src/test/java/org/jboss/netty/handler/codec/spdy/SpdySessionHandlerTest.java +++ b/src/test/java/org/jboss/netty/handler/codec/spdy/SpdySessionHandlerTest.java @@ -46,7 +46,7 @@ public class SpdySessionHandlerTest { assertEquals(spdyDataFrame.isLast(), last); } - private static void assertSynReply(Object msg, int streamId, boolean last, SpdyHeadersFrame headers) { + private static void assertSynReply(Object msg, int streamId, boolean last, SpdyHeaders headers) { assertNotNull(msg); assertTrue(msg instanceof SpdySynReplyFrame); assertHeaders(msg, streamId, last, headers); @@ -74,21 +74,21 @@ public class SpdySessionHandlerTest { assertEquals(spdyGoAwayFrame.getLastGoodStreamId(), lastGoodStreamId); } - private static void assertHeaders(Object msg, int streamId, boolean last, SpdyHeadersFrame headers) { + private static void assertHeaders(Object msg, int streamId, boolean last, SpdyHeaders headers) { assertNotNull(msg); assertTrue(msg instanceof SpdyHeadersFrame); SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg; assertEquals(spdyHeadersFrame.getStreamId(), streamId); assertEquals(spdyHeadersFrame.isLast(), last); - for (String name: headers.getHeaderNames()) { - List expectedValues = headers.getHeaders(name); - List receivedValues = spdyHeadersFrame.getHeaders(name); + for (String name: headers.names()) { + List expectedValues = headers.getAll(name); + List receivedValues = spdyHeadersFrame.headers().getAll(name); assertTrue(receivedValues.containsAll(expectedValues)); receivedValues.removeAll(expectedValues); assertTrue(receivedValues.isEmpty()); - spdyHeadersFrame.removeHeader(name); + spdyHeadersFrame.headers().remove(name); } - assertTrue(spdyHeadersFrame.getHeaders().isEmpty()); + assertTrue(spdyHeadersFrame.headers().isEmpty()); } private static void testSpdySessionHandler(SpdyVersion spdyVersion, boolean server) { @@ -101,7 +101,7 @@ public class SpdySessionHandlerTest { int remoteStreamId = server ? 2 : 1; SpdySynStreamFrame spdySynStreamFrame = new DefaultSpdySynStreamFrame(localStreamId, 0, (byte) 0); - spdySynStreamFrame.setHeader("Compression", "test"); + spdySynStreamFrame.headers().set("Compression", "test"); SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(localStreamId); spdyDataFrame.setLast(true); @@ -130,13 +130,13 @@ public class SpdySessionHandlerTest { // Check if frame codec correctly compresses/uncompresses headers sessionHandler.offer(spdySynStreamFrame); - assertSynReply(sessionHandler.poll(), localStreamId, false, spdySynStreamFrame); + assertSynReply(sessionHandler.poll(), localStreamId, false, spdySynStreamFrame.headers()); assertNull(sessionHandler.peek()); SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(localStreamId); - spdyHeadersFrame.addHeader("HEADER","test1"); - spdyHeadersFrame.addHeader("HEADER","test2"); + spdyHeadersFrame.headers().add("HEADER","test1"); + spdyHeadersFrame.headers().add("HEADER","test2"); sessionHandler.offer(spdyHeadersFrame); - assertHeaders(sessionHandler.poll(), localStreamId, false, spdyHeadersFrame); + assertHeaders(sessionHandler.poll(), localStreamId, false, spdyHeadersFrame.headers()); assertNull(sessionHandler.peek()); localStreamId += 2; @@ -268,14 +268,14 @@ public class SpdySessionHandlerTest { int remoteStreamId = server ? 2 : 1; SpdySynStreamFrame spdySynStreamFrame = new DefaultSpdySynStreamFrame(localStreamId, 0, (byte) 0); - spdySynStreamFrame.setHeader("Compression", "test"); + spdySynStreamFrame.headers().set("Compression", "test"); SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(localStreamId); spdyDataFrame.setLast(true); // Send an initial request sessionHandler.offer(spdySynStreamFrame); - assertSynReply(sessionHandler.poll(), localStreamId, false, spdySynStreamFrame); + assertSynReply(sessionHandler.poll(), localStreamId, false, spdySynStreamFrame.headers()); assertNull(sessionHandler.peek()); sessionHandler.offer(spdyDataFrame); assertDataFrame(sessionHandler.poll(), localStreamId, true); @@ -346,8 +346,8 @@ public class SpdySessionHandlerTest { int streamId = spdySynStreamFrame.getStreamId(); SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId); spdySynReplyFrame.setLast(spdySynStreamFrame.isLast()); - for (Map.Entry entry: spdySynStreamFrame.getHeaders()) { - spdySynReplyFrame.addHeader(entry.getKey(), entry.getValue()); + for (Map.Entry entry: spdySynStreamFrame.headers()) { + spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue()); } Channels.write(e.getChannel(), spdySynReplyFrame, e.getRemoteAddress());