diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java index ad5240f67b..254f3cda64 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java @@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.handler.codec.Headers; import io.netty.util.AsciiString; +import io.netty.util.internal.StringUtil; import java.text.ParseException; import java.util.Calendar; @@ -1565,6 +1566,48 @@ public abstract class HttpHeaders implements Iterable> return false; } + /** + * Returns {@code true} if a header with the {@code name} and {@code value} exists, {@code false} otherwise. + * This also handles multiple values that are seperated with a {@code ,}. + *

+ * If {@code ignoreCase} is {@code true} then a case insensitive compare is done on the value. + * @param name the name of the header to find + * @param value the value of the header to find + * @param ignoreCase {@code true} then a case insensitive compare is run to compare values. + * otherwise a case sensitive compare is run to compare values. + */ + public boolean containsValue(CharSequence name, CharSequence value, boolean ignoreCase) { + List values = getAll(name); + if (values.isEmpty()) { + return false; + } + + for (String v: values) { + if (contains(v, value, ignoreCase)) { + return true; + } + } + return false; + } + + private static boolean contains(String value, CharSequence expected, boolean ignoreCase) { + String[] parts = StringUtil.split(value, ','); + if (ignoreCase) { + for (String s: parts) { + if (AsciiString.contentEqualsIgnoreCase(expected, s.trim())) { + return true; + } + } + } else { + for (String s: parts) { + if (AsciiString.contentEquals(expected, s.trim())) { + return true; + } + } + } + return false; + } + /** * {@link Headers#get(Object)} and convert the result to a {@link String}. * @param name the name of the header to retrieve diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java index 322f7cfc22..b077f9f543 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java @@ -199,10 +199,9 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker { + upgrade); } - CharSequence connection = headers.get(HttpHeaderNames.CONNECTION); - if (!HttpHeaderValues.UPGRADE.contentEqualsIgnoreCase(connection)) { + if (!headers.containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true)) { throw new WebSocketHandshakeException("Invalid handshake response connection: " - + connection); + + headers.get(HttpHeaderNames.CONNECTION)); } ByteBuf challenge = response.content(); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java index 6b97ea34f9..c2618b2e47 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java @@ -203,9 +203,9 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker { throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); } - CharSequence connection = headers.get(HttpHeaderNames.CONNECTION); - if (!HttpHeaderValues.UPGRADE.contentEqualsIgnoreCase(connection)) { - throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection); + if (!headers.containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true)) { + throw new WebSocketHandshakeException("Invalid handshake response connection: " + + headers.get(HttpHeaderNames.CONNECTION)); } CharSequence accept = headers.get(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java index 4a41bae85a..d589b7729c 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java @@ -204,9 +204,9 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker { throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); } - CharSequence connection = headers.get(HttpHeaderNames.CONNECTION); - if (!HttpHeaderValues.UPGRADE.contentEqualsIgnoreCase(connection)) { - throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection); + if (!headers.containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true)) { + throw new WebSocketHandshakeException("Invalid handshake response connection: " + + headers.get(HttpHeaderNames.CONNECTION)); } CharSequence accept = headers.get(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java index 75de865f06..37a77c72aa 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java @@ -214,9 +214,9 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade); } - CharSequence connection = headers.get(HttpHeaderNames.CONNECTION); - if (!HttpHeaderValues.UPGRADE.contentEqualsIgnoreCase(connection)) { - throw new WebSocketHandshakeException("Invalid handshake response connection: " + connection); + if (!headers.containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true)) { + throw new WebSocketHandshakeException("Invalid handshake response connection: " + + headers.get(HttpHeaderNames.CONNECTION)); } CharSequence accept = headers.get(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java index d6a17c16ab..ff1797db64 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java @@ -107,7 +107,7 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { protected FullHttpResponse newHandshakeResponse(FullHttpRequest req, HttpHeaders headers) { // Serve the WebSocket handshake request. - if (!HttpHeaderValues.UPGRADE.contentEqualsIgnoreCase(req.headers().get(HttpHeaderNames.CONNECTION)) + if (!req.headers().containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true) || !HttpHeaderValues.WEBSOCKET.contentEqualsIgnoreCase(req.headers().get(HttpHeaderNames.UPGRADE))) { throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade"); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandler.java index bec6f10ee3..e860dac8eb 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketClientExtensionHandler.java @@ -61,7 +61,7 @@ public class WebSocketClientExtensionHandler extends ChannelDuplexHandler { @Override public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - if (msg instanceof HttpRequest && WebSocketExtensionUtil.isWebsocketUpgrade((HttpRequest) msg)) { + if (msg instanceof HttpRequest && WebSocketExtensionUtil.isWebsocketUpgrade(((HttpRequest) msg).headers())) { HttpRequest request = (HttpRequest) msg; String headerValue = request.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS); @@ -83,7 +83,7 @@ public class WebSocketClientExtensionHandler extends ChannelDuplexHandler { if (msg instanceof HttpResponse) { HttpResponse response = (HttpResponse) msg; - if (WebSocketExtensionUtil.isWebsocketUpgrade(response)) { + if (WebSocketExtensionUtil.isWebsocketUpgrade(response.headers())) { String extensionsHeader = response.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS); if (extensionsHeader != null) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionUtil.java index 2e8fffa37a..3b0af94739 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionUtil.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionUtil.java @@ -17,7 +17,7 @@ package io.netty.handler.codec.http.websocketx.extensions; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpMessage; +import io.netty.handler.codec.http.HttpHeaders; import io.netty.util.internal.StringUtil; import java.util.ArrayList; @@ -37,12 +37,9 @@ public final class WebSocketExtensionUtil { private static final Pattern PARAMETER = Pattern.compile("^([^=]+)(=[\\\"]?([^\\\"]+)[\\\"]?)?$"); - static boolean isWebsocketUpgrade(HttpMessage httpMessage) { - if (httpMessage == null) { - throw new NullPointerException("httpMessage"); - } - return httpMessage.headers().contains(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true) && - httpMessage.headers().contains(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET, true); + static boolean isWebsocketUpgrade(HttpHeaders headers) { + return headers.containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true) && + headers.contains(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET, true); } public static List extractExtensions(String extensionHeader) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandler.java index 862b3896a4..1b2e39189c 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketServerExtensionHandler.java @@ -68,7 +68,7 @@ public class WebSocketServerExtensionHandler extends ChannelDuplexHandler { if (msg instanceof HttpRequest) { HttpRequest request = (HttpRequest) msg; - if (WebSocketExtensionUtil.isWebsocketUpgrade(request)) { + if (WebSocketExtensionUtil.isWebsocketUpgrade(request.headers())) { String extensionsHeader = request.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS); if (extensionsHeader != null) { @@ -105,7 +105,7 @@ public class WebSocketServerExtensionHandler extends ChannelDuplexHandler { @Override public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { if (msg instanceof HttpResponse && - WebSocketExtensionUtil.isWebsocketUpgrade((HttpResponse) msg) && validExtensions != null) { + WebSocketExtensionUtil.isWebsocketUpgrade(((HttpResponse) msg).headers()) && validExtensions != null) { HttpResponse response = (HttpResponse) msg; String headerValue = response.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionUtilTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionUtilTest.java new file mode 100644 index 0000000000..889e916480 --- /dev/null +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/extensions/WebSocketExtensionUtilTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2016 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.http.websocketx.extensions; + +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaderValues; +import io.netty.handler.codec.http.HttpHeaders; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class WebSocketExtensionUtilTest { + + @Test + public void testIsWebsocketUpgrade() { + HttpHeaders headers = new DefaultHttpHeaders(); + assertFalse(WebSocketExtensionUtil.isWebsocketUpgrade(headers)); + + headers.add(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET); + assertFalse(WebSocketExtensionUtil.isWebsocketUpgrade(headers)); + + headers.add(HttpHeaderNames.CONNECTION, "Keep-Alive, Upgrade"); + assertTrue(WebSocketExtensionUtil.isWebsocketUpgrade(headers)); + } +}