diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java index 9969cb3842..7f8fceaac4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java @@ -34,7 +34,7 @@ public class WebSocket00FrameDecoder extends ReplayingDecoder maxFramePayloadLength) { + protocolViolation(ctx, "Max frame length of " + maxFramePayloadLength + " has been exceeded."); + return null; + } + if (logger.isDebugEnabled()) { logger.debug("Decoding WebSocket Frame length=" + framePayloadLength); } @@ -235,7 +245,7 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder customHeaders) { - super(webSocketURL, version, subprotocol, customHeaders); + boolean allowExtensions, Map customHeaders, long maxFramePayloadLength) { + super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength); this.allowExtensions = allowExtensions; } @@ -196,7 +198,7 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker { } channel.pipeline().replace(HttpResponseDecoder.class, "ws-decoder", - new WebSocket08FrameDecoder(false, allowExtensions)); + new WebSocket08FrameDecoder(false, allowExtensions, getMaxFramePayloadLength())); setHandshakeComplete(); } 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 9052815017..25e8d8cbb5 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 @@ -54,7 +54,7 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { private final boolean allowExtensions; /** - * Constructor specifying the destination web socket location and version to initiate + * Creates a new instance. * * @param webSocketURL * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be @@ -67,10 +67,12 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { * Allow extensions to be used in the reserved bits of the web socket frame * @param customHeaders * Map of custom headers to add to the client request + * @param maxFramePayloadLength + * Maximum length of a frame's payload */ public WebSocketClientHandshaker13(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, Map customHeaders) { - super(webSocketURL, version, subprotocol, customHeaders); + boolean allowExtensions, Map customHeaders, long maxFramePayloadLength) { + super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength); this.allowExtensions = allowExtensions; } @@ -196,7 +198,7 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { } channel.pipeline().replace(HttpResponseDecoder.class, "ws-decoder", - new WebSocket13FrameDecoder(false, allowExtensions)); + new WebSocket13FrameDecoder(false, allowExtensions, getMaxFramePayloadLength())); setHandshakeComplete(); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakerFactory.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakerFactory.java index a199299411..b4e499598e 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakerFactory.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshakerFactory.java @@ -24,8 +24,8 @@ import java.util.Map; public class WebSocketClientHandshakerFactory { /** - * Instances a new handshaker - * + * Creates a new handshaker. + * * @param webSocketURL * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be * sent to this URL. @@ -37,21 +37,44 @@ public class WebSocketClientHandshakerFactory { * Allow extensions to be used in the reserved bits of the web socket frame * @param customHeaders * Custom HTTP headers to send during the handshake - * @throws WebSocketHandshakeException */ - public WebSocketClientHandshaker newHandshaker(URI webSocketURL, WebSocketVersion version, String subprotocol, - boolean allowExtensions, Map customHeaders) throws WebSocketHandshakeException { + public WebSocketClientHandshaker newHandshaker( + URI webSocketURL, WebSocketVersion version, String subprotocol, + boolean allowExtensions, Map customHeaders) { + return newHandshaker(webSocketURL, version, subprotocol, allowExtensions, customHeaders, 65536); + } + + /** + * Creates a new handshaker. + * + * @param webSocketURL + * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be + * sent to this URL. + * @param version + * Version of web socket specification to use to connect to the server + * @param subprotocol + * Sub protocol request sent to the server. Null if no sub-protocol support is required. + * @param allowExtensions + * Allow extensions to be used in the reserved bits of the web socket frame + * @param customHeaders + * Custom HTTP headers to send during the handshake + * @param maxFramePayloadLength + * Maximum allowable frame payload length. Setting this value to your application's requirement may + * reduce denial of service attacks using long data frames. + */ + public WebSocketClientHandshaker newHandshaker( + URI webSocketURL, WebSocketVersion version, String subprotocol, + boolean allowExtensions, Map customHeaders, long maxFramePayloadLength) { if (version == WebSocketVersion.V13) { - return new WebSocketClientHandshaker13(webSocketURL, version, subprotocol, allowExtensions, customHeaders); + return new WebSocketClientHandshaker13(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength); } if (version == WebSocketVersion.V08) { - return new WebSocketClientHandshaker08(webSocketURL, version, subprotocol, allowExtensions, customHeaders); + return new WebSocketClientHandshaker08(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength); } if (version == WebSocketVersion.V00) { - return new WebSocketClientHandshaker00(webSocketURL, version, subprotocol, customHeaders); + return new WebSocketClientHandshaker00(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength); } throw new WebSocketHandshakeException("Protocol version " + version.toString() + " not supported."); - } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java index e22561bc88..2b0d981e85 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java @@ -15,13 +15,13 @@ */ package io.netty.handler.codec.http.websocketx; -import java.util.LinkedHashSet; -import java.util.Set; - import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.handler.codec.http.HttpRequest; +import java.util.LinkedHashSet; +import java.util.Set; + /** * Base class for server side web socket opening and closing handshakes */ @@ -33,6 +33,8 @@ public abstract class WebSocketServerHandshaker { private final WebSocketVersion version; + private final long maxFramePayloadLength; + /** * Constructor specifying the destination web socket location * @@ -43,9 +45,12 @@ public abstract class WebSocketServerHandshaker { * sent to this URL. * @param subprotocols * CSV of supported protocols. Null if sub protocols not supported. + * @param maxFramePayloadLength + * Maximum length of a frame's payload */ protected WebSocketServerHandshaker( - WebSocketVersion version, String webSocketUrl, String subprotocols) { + WebSocketVersion version, String webSocketUrl, String subprotocols, + long maxFramePayloadLength) { this.version = version; this.webSocketUrl = webSocketUrl; if (subprotocols != null) { @@ -57,6 +62,7 @@ public abstract class WebSocketServerHandshaker { } else { this.subprotocols = new String[0]; } + this.maxFramePayloadLength = maxFramePayloadLength; } /** @@ -71,7 +77,7 @@ public abstract class WebSocketServerHandshaker { */ public Set getSubprotocols() { Set ret = new LinkedHashSet(); - for (String p: this.subprotocols) { + for (String p: subprotocols) { ret.add(p); } return ret; @@ -84,6 +90,13 @@ public abstract class WebSocketServerHandshaker { return version; } + /** + * Returns the max length for any frame's payload. + */ + public long getMaxFramePayloadLength() { + return maxFramePayloadLength; + } + /** * Performs the opening handshake * 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 ff6c31cdd6..a6e1bae7f8 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 @@ -57,9 +57,12 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { * sent to this URL. * @param subprotocols * CSV of supported protocols + * @param maxFramePayloadLength + * Maximum allowable frame payload length. Setting this value to your application's requirement may + * reduce denial of service attacks using long data frames. */ - public WebSocketServerHandshaker00(String webSocketURL, String subprotocols) { - super(WebSocketVersion.V00, webSocketURL, subprotocols); + public WebSocketServerHandshaker00(String webSocketURL, String subprotocols, long maxFramePayloadLength) { + super(WebSocketVersion.V00, webSocketURL, subprotocols, maxFramePayloadLength); } /** @@ -166,7 +169,8 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { if (p.get(HttpChunkAggregator.class) != null) { p.remove(HttpChunkAggregator.class); } - p.replace(HttpRequestDecoder.class, "wsdecoder", new WebSocket00FrameDecoder()); + p.replace(HttpRequestDecoder.class, "wsdecoder", + new WebSocket00FrameDecoder(getMaxFramePayloadLength())); ChannelFuture future = channel.write(res); 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 2e5b328a4b..e453c506e8 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 @@ -58,9 +58,12 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker { * CSV of supported protocols * @param allowExtensions * Allow extensions to be used in the reserved bits of the web socket frame + * @param maxFramePayloadLength + * Maximum allowable frame payload length. Setting this value to your application's requirement may + * reduce denial of service attacks using long data frames. */ - public WebSocketServerHandshaker08(String webSocketURL, String subprotocols, boolean allowExtensions) { - super(WebSocketVersion.V08, webSocketURL, subprotocols); + public WebSocketServerHandshaker08(String webSocketURL, String subprotocols, boolean allowExtensions, long maxFramePayloadLength) { + super(WebSocketVersion.V08, webSocketURL, subprotocols, maxFramePayloadLength); this.allowExtensions = allowExtensions; } @@ -141,7 +144,8 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker { p.remove(HttpChunkAggregator.class); } - p.replace(HttpRequestDecoder.class, "wsdecoder", new WebSocket08FrameDecoder(true, allowExtensions)); + p.replace(HttpRequestDecoder.class, "wsdecoder", + new WebSocket08FrameDecoder(true, allowExtensions, getMaxFramePayloadLength())); p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket08FrameEncoder(false)); return future; 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 10ddb243f9..8cc434e021 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 @@ -59,9 +59,12 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker { * CSV of supported protocols * @param allowExtensions * Allow extensions to be used in the reserved bits of the web socket frame + * @param maxFramePayloadLength + * Maximum allowable frame payload length. Setting this value to your application's requirement may + * reduce denial of service attacks using long data frames. */ - public WebSocketServerHandshaker13(String webSocketURL, String subprotocols, boolean allowExtensions) { - super(WebSocketVersion.V13, webSocketURL, subprotocols); + public WebSocketServerHandshaker13(String webSocketURL, String subprotocols, boolean allowExtensions, long maxFramePayloadLength) { + super(WebSocketVersion.V13, webSocketURL, subprotocols, maxFramePayloadLength); this.allowExtensions = allowExtensions; } @@ -142,7 +145,8 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker { p.remove(HttpChunkAggregator.class); } - p.replace(HttpRequestDecoder.class, "wsdecoder", new WebSocket13FrameDecoder(true, allowExtensions)); + p.replace(HttpRequestDecoder.class, "wsdecoder", + new WebSocket13FrameDecoder(true, allowExtensions, getMaxFramePayloadLength())); p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket13FrameEncoder(false)); return future; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java index 4428bc5ea0..50f848c5cd 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java @@ -17,11 +17,11 @@ package io.netty.handler.codec.http.websocketx; import io.netty.channel.Channel; import io.netty.handler.codec.http.DefaultHttpResponse; +import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.HttpHeaders.Names; /** * Instances the appropriate handshake class to use for servers @@ -34,9 +34,11 @@ public class WebSocketServerHandshakerFactory { private final boolean allowExtensions; + private final long maxFramePayloadLength; + /** * Constructor specifying the destination web socket location - * + * * @param webSocketURL * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be * sent to this URL. @@ -45,15 +47,37 @@ public class WebSocketServerHandshakerFactory { * @param allowExtensions * Allow extensions to be used in the reserved bits of the web socket frame */ - public WebSocketServerHandshakerFactory(String webSocketURL, String subprotocols, boolean allowExtensions) { + public WebSocketServerHandshakerFactory( + String webSocketURL, String subprotocols, boolean allowExtensions) { + this(webSocketURL, subprotocols, allowExtensions, 65536); + } + + /** + * Constructor specifying the destination web socket location + * + * @param webSocketURL + * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be + * sent to this URL. + * @param subprotocols + * CSV of supported protocols. Null if sub protocols not supported. + * @param allowExtensions + * Allow extensions to be used in the reserved bits of the web socket frame + * @param maxFramePayloadLength + * Maximum allowable frame payload length. Setting this value to your application's requirement may + * reduce denial of service attacks using long data frames. + */ + public WebSocketServerHandshakerFactory( + String webSocketURL, String subprotocols, boolean allowExtensions, + long maxFramePayloadLength) { this.webSocketURL = webSocketURL; this.subprotocols = subprotocols; this.allowExtensions = allowExtensions; + this.maxFramePayloadLength = maxFramePayloadLength; } /** * Instances a new handshaker - * + * * @return A new WebSocketServerHandshaker for the requested web socket version. Null if web socket version is not * supported. */ @@ -63,22 +87,22 @@ public class WebSocketServerHandshakerFactory { if (version != null) { if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) { // Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification). - return new WebSocketServerHandshaker13(webSocketURL, subprotocols, allowExtensions); + return new WebSocketServerHandshaker13(webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength); } else if (version.equals(WebSocketVersion.V08.toHttpHeaderValue())) { // Version 8 of the wire protocol - version 10 of the draft hybi specification. - return new WebSocketServerHandshaker08(webSocketURL, subprotocols, allowExtensions); + return new WebSocketServerHandshaker08(webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength); } else { return null; } } else { // Assume version 00 where version header was not specified - return new WebSocketServerHandshaker00(webSocketURL, subprotocols); + return new WebSocketServerHandshaker00(webSocketURL, subprotocols, maxFramePayloadLength); } } /** * Return that we need cannot not support the web socket version - * + * * @param channel * Channel */ diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpClientCodecTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpClientCodecTest.java index 4f675ed5d2..994836b3a7 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpClientCodecTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpClientCodecTest.java @@ -18,6 +18,7 @@ package io.netty.handler.codec.http; import static org.junit.Assert.*; import io.netty.buffer.ChannelBuffer; import io.netty.buffer.ChannelBuffers; +import io.netty.handler.codec.CodecException; import io.netty.handler.codec.PrematureChannelClosureException; import io.netty.handler.codec.embedder.DecoderEmbedder; import io.netty.handler.codec.embedder.EncoderEmbedder; @@ -72,7 +73,7 @@ public class HttpClientCodecTest { try { encoder.finish(); fail(); - } catch (CodecEmbedderException e) { + } catch (CodecException e) { assertTrue(e.getCause() instanceof PrematureChannelClosureException); } @@ -92,7 +93,7 @@ public class HttpClientCodecTest { encoder.finish(); decoder.finish(); fail(); - } catch (CodecEmbedderException e) { + } catch (CodecException e) { assertTrue(e.getCause() instanceof PrematureChannelClosureException); } 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 0a9ae8cede..b9e9312c09 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 @@ -74,7 +74,7 @@ public class WebSocketServerHandshaker00Test { ChannelBuffer buffer = ChannelBuffers.copiedBuffer("^n:ds[4U", Charset.defaultCharset()); req.setContent(buffer); - WebSocketServerHandshaker00 handsaker = new WebSocketServerHandshaker00("ws://example.com/chat", "chat"); + WebSocketServerHandshaker00 handsaker = new WebSocketServerHandshaker00("ws://example.com/chat", "chat", Long.MAX_VALUE); handsaker.handshake(channelMock, req); Assert.assertEquals("ws://example.com/chat", res.getValue().getHeader(Names.SEC_WEBSOCKET_LOCATION)); 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 ae67d5f9d2..a015f3818c 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 @@ -67,7 +67,7 @@ public class WebSocketServerHandshaker08Test { req.setHeader(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat"); req.setHeader(Names.SEC_WEBSOCKET_VERSION, "8"); - WebSocketServerHandshaker08 handsaker = new WebSocketServerHandshaker08("ws://example.com/chat", "chat", false); + WebSocketServerHandshaker08 handsaker = new WebSocketServerHandshaker08("ws://example.com/chat", "chat", false, Long.MAX_VALUE); handsaker.handshake(channelMock, req); Assert.assertEquals("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.getValue().getHeader(Names.SEC_WEBSOCKET_ACCEPT)); 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 7c9739103f..a1a015b803 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 @@ -66,7 +66,7 @@ public class WebSocketServerHandshaker13Test { req.setHeader(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com"); req.setHeader(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat"); req.setHeader(Names.SEC_WEBSOCKET_VERSION, "13"); - WebSocketServerHandshaker13 handsaker = new WebSocketServerHandshaker13("ws://example.com/chat", "chat", false); + WebSocketServerHandshaker13 handsaker = new WebSocketServerHandshaker13("ws://example.com/chat", "chat", false, Long.MAX_VALUE); handsaker.handshake(channelMock, req); Assert.assertEquals("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.getValue().getHeader(Names.SEC_WEBSOCKET_ACCEPT));