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 7f2cbe232b..b66bb46902 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 @@ -147,8 +147,8 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { res.headers().add(headers); } - res.headers().add(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET); - res.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE); + res.headers().set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) + .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE); // Fill in the headers and contents depending on handshake getMethod. if (isHixie76) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java index 54f8ff4fa7..6ab773fa0e 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java @@ -149,9 +149,9 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker { logger.debug("WebSocket version 07 server handshake key: {}, response: {}.", key, accept); } - res.headers().add(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET); - res.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE); - res.headers().add(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT, accept); + res.headers().set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) + .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) + .set(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT, accept); String subprotocols = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL); if (subprotocols != null) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java index 1e454f4339..c3bd4b8eb1 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java @@ -155,9 +155,9 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker { logger.debug("WebSocket version 08 server handshake key: {}, response: {}", key, accept); } - res.headers().add(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET); - res.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE); - res.headers().add(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT, accept); + res.headers().set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) + .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) + .set(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT, accept); String subprotocols = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL); if (subprotocols != null) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java index 8b445be922..ddb3f592f0 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java @@ -153,9 +153,9 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker { logger.debug("WebSocket version 13 server handshake key: {}, response: {}", key, accept); } - res.headers().add(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET); - res.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE); - res.headers().add(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT, accept); + res.headers().set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) + .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) + .set(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT, accept); String subprotocols = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL); if (subprotocols != null) { diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00Test.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00Test.java index 8783e0b5bb..f8f60c8d41 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00Test.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00Test.java @@ -36,7 +36,18 @@ import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; -public class WebSocketServerHandshaker00Test { +public class WebSocketServerHandshaker00Test extends WebSocketServerHandshakerTest { + + @Override + protected WebSocketServerHandshaker newHandshaker(String webSocketURL, String subprotocols, + WebSocketDecoderConfig decoderConfig) { + return new WebSocketServerHandshaker00(webSocketURL, subprotocols, decoderConfig); + } + + @Override + protected WebSocketVersion webSocketVersion() { + return WebSocketVersion.V00; + } @Test public void testPerformOpeningHandshake() { diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07Test.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07Test.java new file mode 100644 index 0000000000..650aacdd5e --- /dev/null +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07Test.java @@ -0,0 +1,30 @@ +/* + * Copyright 2020 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; + +public class WebSocketServerHandshaker07Test extends WebSocketServerHandshakerTest { + + @Override + protected WebSocketServerHandshaker newHandshaker(String webSocketURL, String subprotocols, + WebSocketDecoderConfig decoderConfig) { + return new WebSocketServerHandshaker07(webSocketURL, subprotocols, decoderConfig); + } + + @Override + protected WebSocketVersion webSocketVersion() { + return WebSocketVersion.V07; + } +} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08Test.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08Test.java index 2c8cc6fc25..e5b222529f 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08Test.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08Test.java @@ -33,7 +33,18 @@ import org.junit.Test; import static io.netty.handler.codec.http.HttpVersion.*; -public class WebSocketServerHandshaker08Test { +public class WebSocketServerHandshaker08Test extends WebSocketServerHandshakerTest { + + @Override + protected WebSocketServerHandshaker newHandshaker(String webSocketURL, String subprotocols, + WebSocketDecoderConfig decoderConfig) { + return new WebSocketServerHandshaker08(webSocketURL, subprotocols, decoderConfig); + } + + @Override + protected WebSocketVersion webSocketVersion() { + return WebSocketVersion.V08; + } @Test public void testPerformOpeningHandshake() { diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13Test.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13Test.java index 25340dfe31..e1ce15cae8 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13Test.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13Test.java @@ -40,7 +40,18 @@ import java.util.Iterator; import static io.netty.handler.codec.http.HttpVersion.*; -public class WebSocketServerHandshaker13Test { +public class WebSocketServerHandshaker13Test extends WebSocketServerHandshakerTest { + + @Override + protected WebSocketServerHandshaker newHandshaker(String webSocketURL, String subprotocols, + WebSocketDecoderConfig decoderConfig) { + return new WebSocketServerHandshaker13(webSocketURL, subprotocols, decoderConfig); + } + + @Override + protected WebSocketVersion webSocketVersion() { + return WebSocketVersion.V13; + } @Test public void testPerformOpeningHandshake() { diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerTest.java new file mode 100644 index 0000000000..e81a53de04 --- /dev/null +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerTest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2020 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; + +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaderValues; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpVersion; +import org.junit.Test; + +import static org.junit.Assert.*; + +public abstract class WebSocketServerHandshakerTest { + + protected abstract WebSocketServerHandshaker newHandshaker(String webSocketURL, String subprotocols, + WebSocketDecoderConfig decoderConfig); + + protected abstract WebSocketVersion webSocketVersion(); + + @Test + public void testDuplicateHandshakeResponseHeaders() { + WebSocketServerHandshaker serverHandshaker = newHandshaker("ws://example.com/chat", + "chat", WebSocketDecoderConfig.DEFAULT); + FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/chat"); + request.headers() + .set(HttpHeaderNames.HOST, "example.com") + .set(HttpHeaderNames.ORIGIN, "example.com") + .set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) + .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) + .set(HttpHeaderNames.SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==") + .set(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, "http://example.com") + .set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, "chat, superchat") + .set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, webSocketVersion().toAsciiString()); + HttpHeaders customResponseHeaders = new DefaultHttpHeaders(); + // set duplicate required headers and one custom + customResponseHeaders + .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE) + .set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET) + .set("custom", "header"); + + if (webSocketVersion() != WebSocketVersion.V00) { + customResponseHeaders.set(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT, "12345"); + } + + FullHttpResponse response = null; + try { + response = serverHandshaker.newHandshakeResponse(request, customResponseHeaders); + HttpHeaders responseHeaders = response.headers(); + + assertEquals(1, responseHeaders.getAll(HttpHeaderNames.CONNECTION).size()); + assertEquals(1, responseHeaders.getAll(HttpHeaderNames.UPGRADE).size()); + assertTrue(responseHeaders.containsValue("custom", "header", true)); + if (webSocketVersion() != WebSocketVersion.V00) { + assertFalse(responseHeaders.containsValue(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT, "12345", false)); + } + } finally { + request.release(); + if (response != null) { + response.release(); + } + } + } +}