diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/client/WebSocketClient.java b/src/main/java/org/jboss/netty/example/http/websocketx/client/WebSocketClient.java index 6bc4b67cb3..3bc03f693a 100644 --- a/src/main/java/org/jboss/netty/example/http/websocketx/client/WebSocketClient.java +++ b/src/main/java/org/jboss/netty/example/http/websocketx/client/WebSocketClient.java @@ -114,7 +114,7 @@ public class WebSocketClient { // Send 10 messages and wait for responses System.out.println("WebSocket Client sending message"); - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 1000; i++) { ch.write(new TextWebSocketFrame("Message #" + i)); } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java index 30157993e5..805a54dced 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java @@ -35,7 +35,7 @@ public class WebSocket00FrameDecoder extends ReplayingDecoder { private static final int DEFAULT_MAX_FRAME_SIZE = 16384; - private final int maxFrameSize; + private final long maxFrameSize; private boolean receivedClosingHandshake; public WebSocket00FrameDecoder() { @@ -52,6 +52,17 @@ public class WebSocket00FrameDecoder extends ReplayingDecoder { public WebSocket00FrameDecoder(int maxFrameSize) { this.maxFrameSize = maxFrameSize; } + + /** + * Creates a new instance of {@code WebSocketFrameDecoder} with the specified {@code maxFrameSize}. If the client + * sends a frame size larger than {@code maxFrameSize}, the channel will be closed. + * + * @param maxFrameSize + * the maximum frame size to decode + */ + public WebSocket00FrameDecoder(long maxFrameSize) { + this.maxFrameSize = maxFrameSize; + } @Override protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, VoidEnum state) diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java index cd9cf5c9d7..f53e9b043a 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java @@ -82,6 +82,7 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder this.maxFramePayloadLength) { + protocolViolation(channel, "Max frame length of " + this.maxFramePayloadLength + " has been exceeded."); + return null; + } if (logger.isDebugEnabled()) { logger.debug("Decoding WebSocket Frame length=" + framePayloadLength); } @@ -236,7 +258,7 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder customHeaders) { + this(webSocketUrl, version, subprotocol, customHeaders, Long.MAX_VALUE); + } + + /** + * Base constructor + * + * @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. + * @param customHeaders + * Map of custom headers to add to the client request + * @param maxFramePayloadLength + * Maximum length of a frame's payload + */ + public WebSocketClientHandshaker(URI webSocketUrl, WebSocketVersion version, String subprotocol, + Map customHeaders, long maxFramePayloadLength) { this.webSocketUrl = webSocketUrl; this.version = version; expectedSubprotocol = subprotocol; this.customHeaders = customHeaders; + this.maxFramePayloadLength = maxFramePayloadLength; } /** @@ -74,6 +97,13 @@ public abstract class WebSocketClientHandshaker { return version; } + /** + * Returns the max length for any frame's payload + */ + public long getMaxFramePayloadLength() { + return maxFramePayloadLength; + } + /** * Flag to indicate if the opening handshake is complete */ 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 878a67cf64..9def0d3c73 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 @@ -49,7 +49,7 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker { private byte[] expectedChallengeResponseBytes; /** - * Constructor specifying the destination web socket location and version to initiate + * Constructor with default values * * @param webSocketURL * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be @@ -63,9 +63,29 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker { */ public WebSocketClientHandshaker00(URI webSocketURL, WebSocketVersion version, String subprotocol, Map customHeaders) { - super(webSocketURL, version, subprotocol, customHeaders); - + this(webSocketURL, version, subprotocol, customHeaders, Long.MAX_VALUE); } + + /** + * Constructor + * + * @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. + * @param customHeaders + * Map of custom headers to add to the client request + * @param maxFramePayloadLength + * Maximum length of a frame's payload + */ + public WebSocketClientHandshaker00(URI webSocketURL, WebSocketVersion version, String subprotocol, + Map customHeaders, long maxFramePayloadLength) { + super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength); + } + /** *

@@ -219,7 +239,8 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker { String protocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); setActualSubprotocol(protocol); - channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder", new WebSocket00FrameDecoder()); + channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder", + new WebSocket00FrameDecoder(this.getMaxFramePayloadLength())); 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 a95920a2e9..1c45ca85de 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 @@ -54,7 +54,7 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker { private final boolean allowExtensions; /** - * Constructor specifying the destination web socket location and version to initiate + * Constructor with default values * * @param webSocketURL * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be @@ -70,10 +70,33 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker { */ public WebSocketClientHandshaker08(URI webSocketURL, WebSocketVersion version, String subprotocol, boolean allowExtensions, Map customHeaders) { - super(webSocketURL, version, subprotocol, customHeaders); + this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, Long.MAX_VALUE); + } + + /** + * Constructor + * + * @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. + * @param allowExtensions + * 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 WebSocketClientHandshaker08(URI webSocketURL, WebSocketVersion version, String subprotocol, + boolean allowExtensions, Map customHeaders, long maxFramePayloadLength) { + super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength); this.allowExtensions = allowExtensions; } + /** * /** *

@@ -204,7 +227,7 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker { } channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder", - new WebSocket08FrameDecoder(false, allowExtensions)); + new WebSocket08FrameDecoder(false, allowExtensions, this.getMaxFramePayloadLength())); 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 235446b320..4e24920a68 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 @@ -54,7 +54,7 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { private final boolean allowExtensions; /** - * Constructor specifying the destination web socket location and version to initiate + * Constructor with default values * * @param webSocketURL * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be @@ -70,7 +70,29 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { */ public WebSocketClientHandshaker13(URI webSocketURL, WebSocketVersion version, String subprotocol, boolean allowExtensions, Map customHeaders) { - super(webSocketURL, version, subprotocol, customHeaders); + this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, Long.MAX_VALUE); + } + + /** + * Constructor + * + * @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. + * @param allowExtensions + * 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, long maxFramePayloadLength) { + super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength); this.allowExtensions = allowExtensions; } @@ -200,7 +222,7 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { } channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder", - new WebSocket13FrameDecoder(false, allowExtensions)); + new WebSocket13FrameDecoder(false, allowExtensions, this.getMaxFramePayloadLength())); setHandshakeComplete(); } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshakerFactory.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshakerFactory.java index 860c296935..bdb962938b 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshakerFactory.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshakerFactory.java @@ -41,17 +41,42 @@ public class WebSocketClientHandshakerFactory { */ public WebSocketClientHandshaker newHandshaker(URI webSocketURL, WebSocketVersion version, String subprotocol, boolean allowExtensions, Map customHeaders) throws WebSocketHandshakeException { + return newHandshaker(webSocketURL, version, subprotocol, allowExtensions, customHeaders, Long.MAX_VALUE); + } + + /** + * Instances 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. + * @throws WebSocketHandshakeException + */ + public WebSocketClientHandshaker newHandshaker(URI webSocketURL, WebSocketVersion version, String subprotocol, + boolean allowExtensions, Map customHeaders, long maxFramePayloadLength) throws WebSocketHandshakeException { 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/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java index c617b15ca9..403b530e30 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java @@ -35,9 +35,11 @@ public abstract class WebSocketServerHandshaker { private final WebSocketVersion version; + private final long maxFramePayloadLength; + /** * {@link ChannelFutureListener} which will call {@link Channels#fireExceptionCaught(org.jboss.netty.channel.ChannelHandlerContext, Throwable)} - * if the {@link ChannelFuture} was not sucessful. + * if the {@link ChannelFuture} was not successful. */ public static final ChannelFutureListener HANDSHAKE_LISTENER = new ChannelFutureListener() { public void operationComplete(ChannelFuture future) throws Exception { @@ -47,6 +49,21 @@ public abstract class WebSocketServerHandshaker { } }; + /** + * Constructor using default values + * + * @param version + * the protocol version + * @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. + */ + protected WebSocketServerHandshaker(WebSocketVersion version, String webSocketUrl, String subprotocols) { + this(version, webSocketUrl, subprotocols, Long.MAX_VALUE); + } + /** * Constructor specifying the destination web socket location * @@ -57,9 +74,11 @@ 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) { + protected WebSocketServerHandshaker(WebSocketVersion version, String webSocketUrl, String subprotocols, + long maxFramePayloadLength) { this.version = version; this.webSocketUrl = webSocketUrl; if (subprotocols != null) { @@ -71,8 +90,10 @@ public abstract class WebSocketServerHandshaker { } else { this.subprotocols = new String[0]; } + this.maxFramePayloadLength = maxFramePayloadLength; } + /** * Returns the URL of the web socket */ @@ -98,6 +119,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/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 22110c9266..97d2cba757 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 @@ -51,7 +51,7 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketServerHandshaker00.class); /** - * Constructor specifying the destination web socket location + * Constructor with default values * * @param webSocketURL * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be @@ -60,8 +60,24 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { * CSV of supported protocols */ public WebSocketServerHandshaker00(String webSocketURL, String subprotocols) { - super(WebSocketVersion.V00, webSocketURL, subprotocols); + this(webSocketURL, subprotocols, Long.MAX_VALUE); } + + /** + * 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 + * @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, long maxFramePayloadLength) { + super(WebSocketVersion.V00, webSocketURL, subprotocols, maxFramePayloadLength); + } /** *

@@ -167,7 +183,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(this.getMaxFramePayloadLength())); ChannelFuture future = channel.write(res); 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 c7b93965b7..2f8dbde774 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 @@ -53,7 +53,7 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker { private final boolean allowExtensions; /** - * Constructor specifying the destination web socket location + * Constructor using defaults * * @param webSocketURL * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be @@ -64,7 +64,26 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker { * Allow extensions to be used in the reserved bits of the web socket frame */ public WebSocketServerHandshaker08(String webSocketURL, String subprotocols, boolean allowExtensions) { - super(WebSocketVersion.V08, webSocketURL, subprotocols); + this(webSocketURL, subprotocols, allowExtensions, Long.MAX_VALUE); + } + + /** + * Constructor + * + * @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 + * @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, + long maxFramePayloadLength) { + super(WebSocketVersion.V08, webSocketURL, subprotocols, maxFramePayloadLength); this.allowExtensions = allowExtensions; } @@ -150,7 +169,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, this.getMaxFramePayloadLength())); p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket08FrameEncoder(false)); return future; 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 abd6372269..bdb62623e7 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 @@ -54,7 +54,7 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker { private final boolean allowExtensions; /** - * Constructor specifying the destination web socket location + * Constructor using defaults * * @param webSocketURL * URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be @@ -65,10 +65,30 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker { * Allow extensions to be used in the reserved bits of the web socket frame */ public WebSocketServerHandshaker13(String webSocketURL, String subprotocols, boolean allowExtensions) { - super(WebSocketVersion.V13, webSocketURL, subprotocols); + this(webSocketURL, subprotocols, allowExtensions, Long.MAX_VALUE); + } + + /** + * 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 + * @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, + long maxFramePayloadLength) { + super(WebSocketVersion.V13, webSocketURL, subprotocols, maxFramePayloadLength); this.allowExtensions = allowExtensions; } + /** *

* Handle the web socket handshake for the web socket specification