Merge pull request #284 from veebs/WsMaxFrameLength
Issue #283 - (master) Support max frame length for web socket to limit chance of DOS attack
This commit is contained in:
commit
9f2c454ab7
@ -35,7 +35,7 @@ public class WebSocket00FrameDecoder extends ReplayingDecoder<VoidEnum> {
|
|||||||
|
|
||||||
private static final int DEFAULT_MAX_FRAME_SIZE = 16384;
|
private static final int DEFAULT_MAX_FRAME_SIZE = 16384;
|
||||||
|
|
||||||
private final int maxFrameSize;
|
private final long maxFrameSize;
|
||||||
private boolean receivedClosingHandshake;
|
private boolean receivedClosingHandshake;
|
||||||
|
|
||||||
public WebSocket00FrameDecoder() {
|
public WebSocket00FrameDecoder() {
|
||||||
@ -53,6 +53,17 @@ public class WebSocket00FrameDecoder extends ReplayingDecoder<VoidEnum> {
|
|||||||
this.maxFrameSize = 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
|
@Override
|
||||||
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, VoidEnum state)
|
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, VoidEnum state)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
@ -82,6 +82,7 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
|
|||||||
private UTF8Output fragmentedFramesText;
|
private UTF8Output fragmentedFramesText;
|
||||||
private int fragmentedFramesCount;
|
private int fragmentedFramesCount;
|
||||||
|
|
||||||
|
private long maxFramePayloadLength;
|
||||||
private boolean frameFinalFlag;
|
private boolean frameFinalFlag;
|
||||||
private int frameRsv;
|
private int frameRsv;
|
||||||
private int frameOpcode;
|
private int frameOpcode;
|
||||||
@ -99,7 +100,7 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor with default values
|
||||||
*
|
*
|
||||||
* @param maskedPayload
|
* @param maskedPayload
|
||||||
* Web socket servers must set this to true processed incoming masked payload. Client implementations
|
* Web socket servers must set this to true processed incoming masked payload. Client implementations
|
||||||
@ -108,9 +109,26 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
|
|||||||
* Flag to allow reserved extension bits to be used or not
|
* Flag to allow reserved extension bits to be used or not
|
||||||
*/
|
*/
|
||||||
public WebSocket08FrameDecoder(boolean maskedPayload, boolean allowExtensions) {
|
public WebSocket08FrameDecoder(boolean maskedPayload, boolean allowExtensions) {
|
||||||
|
this(maskedPayload, allowExtensions, Long.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param maskedPayload
|
||||||
|
* Web socket servers must set this to true processed incoming masked payload. Client implementations
|
||||||
|
* must set this to false.
|
||||||
|
* @param allowExtensions
|
||||||
|
* Flag to allow reserved extension bits to be used or not
|
||||||
|
* @param maxFramePayloadLength
|
||||||
|
* Maximum length of a frame's payload. Setting this to an appropriate value for you application
|
||||||
|
* helps check for denial of services attacks.
|
||||||
|
*/
|
||||||
|
public WebSocket08FrameDecoder(boolean maskedPayload, boolean allowExtensions, long maxFramePayloadLength) {
|
||||||
super(State.FRAME_START);
|
super(State.FRAME_START);
|
||||||
this.maskedPayload = maskedPayload;
|
this.maskedPayload = maskedPayload;
|
||||||
this.allowExtensions = allowExtensions;
|
this.allowExtensions = allowExtensions;
|
||||||
|
this.maxFramePayloadLength = maxFramePayloadLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -220,6 +238,10 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
|
|||||||
framePayloadLength = framePayloadLen1;
|
framePayloadLength = framePayloadLen1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (framePayloadLength > this.maxFramePayloadLength) {
|
||||||
|
protocolViolation(channel, "Max frame length of " + this.maxFramePayloadLength + " has been exceeded.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Decoding WebSocket Frame length=" + framePayloadLength);
|
logger.debug("Decoding WebSocket Frame length=" + framePayloadLength);
|
||||||
}
|
}
|
||||||
@ -236,10 +258,12 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
|
|||||||
int rbytes = actualReadableBytes();
|
int rbytes = actualReadableBytes();
|
||||||
ChannelBuffer payloadBuffer = null;
|
ChannelBuffer payloadBuffer = null;
|
||||||
|
|
||||||
int willHaveReadByteCount = framePayloadBytesRead + rbytes;
|
long willHaveReadByteCount = framePayloadBytesRead + rbytes;
|
||||||
|
|
||||||
// logger.debug("Frame rbytes=" + rbytes + " willHaveReadByteCount="
|
// logger.debug("Frame rbytes=" + rbytes + " willHaveReadByteCount="
|
||||||
// + willHaveReadByteCount + " framePayloadLength=" +
|
// + willHaveReadByteCount + " framePayloadLength=" +
|
||||||
// framePayloadLength);
|
// framePayloadLength);
|
||||||
|
|
||||||
if (willHaveReadByteCount == framePayloadLength) {
|
if (willHaveReadByteCount == framePayloadLength) {
|
||||||
// We have all our content so proceed to process
|
// We have all our content so proceed to process
|
||||||
payloadBuffer = buffer.readBytes(rbytes);
|
payloadBuffer = buffer.readBytes(rbytes);
|
||||||
|
@ -59,7 +59,7 @@ package io.netty.handler.codec.http.websocketx;
|
|||||||
public class WebSocket13FrameDecoder extends WebSocket08FrameDecoder {
|
public class WebSocket13FrameDecoder extends WebSocket08FrameDecoder {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor with default values
|
||||||
*
|
*
|
||||||
* @param maskedPayload
|
* @param maskedPayload
|
||||||
* Web socket servers must set this to true processed incoming masked payload. Client implementations
|
* Web socket servers must set this to true processed incoming masked payload. Client implementations
|
||||||
@ -68,6 +68,22 @@ public class WebSocket13FrameDecoder extends WebSocket08FrameDecoder {
|
|||||||
* Flag to allow reserved extension bits to be used or not
|
* Flag to allow reserved extension bits to be used or not
|
||||||
*/
|
*/
|
||||||
public WebSocket13FrameDecoder(boolean maskedPayload, boolean allowExtensions) {
|
public WebSocket13FrameDecoder(boolean maskedPayload, boolean allowExtensions) {
|
||||||
super(maskedPayload, allowExtensions);
|
this(maskedPayload, allowExtensions, Long.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param maskedPayload
|
||||||
|
* Web socket servers must set this to true processed incoming masked payload. Client implementations
|
||||||
|
* must set this to false.
|
||||||
|
* @param allowExtensions
|
||||||
|
* Flag to allow reserved extension bits to be used or not
|
||||||
|
* @param maxFramePayloadLength
|
||||||
|
* Maximum length of a frame's payload. Setting this to an appropriate value for you application
|
||||||
|
* helps check for denial of services attacks.
|
||||||
|
*/
|
||||||
|
public WebSocket13FrameDecoder(boolean maskedPayload, boolean allowExtensions, long maxFramePayloadLength) {
|
||||||
|
super(maskedPayload, allowExtensions, maxFramePayloadLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,8 +39,10 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
|
|
||||||
protected final Map<String, String> customHeaders;
|
protected final Map<String, String> customHeaders;
|
||||||
|
|
||||||
|
private final long maxFramePayloadLength;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base constructor
|
* Base constructor with default values
|
||||||
*
|
*
|
||||||
* @param webSocketUrl
|
* @param webSocketUrl
|
||||||
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
||||||
@ -54,10 +56,31 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
*/
|
*/
|
||||||
public WebSocketClientHandshaker(URI webSocketUrl, WebSocketVersion version, String subprotocol,
|
public WebSocketClientHandshaker(URI webSocketUrl, WebSocketVersion version, String subprotocol,
|
||||||
Map<String, String> customHeaders) {
|
Map<String, String> 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<String, String> customHeaders, long maxFramePayloadLength) {
|
||||||
this.webSocketUrl = webSocketUrl;
|
this.webSocketUrl = webSocketUrl;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
expectedSubprotocol = subprotocol;
|
expectedSubprotocol = subprotocol;
|
||||||
this.customHeaders = customHeaders;
|
this.customHeaders = customHeaders;
|
||||||
|
this.maxFramePayloadLength = maxFramePayloadLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,6 +97,13 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
return version;
|
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
|
* Flag to indicate if the opening handshake is complete
|
||||||
*/
|
*/
|
||||||
|
@ -49,7 +49,7 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
|||||||
private byte[] expectedChallengeResponseBytes;
|
private byte[] expectedChallengeResponseBytes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor specifying the destination web socket location and version to initiate
|
* Constructor with default values
|
||||||
*
|
*
|
||||||
* @param webSocketURL
|
* @param webSocketURL
|
||||||
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
||||||
@ -63,8 +63,27 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
|||||||
*/
|
*/
|
||||||
public WebSocketClientHandshaker00(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
public WebSocketClientHandshaker00(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
||||||
Map<String, String> customHeaders) {
|
Map<String, String> customHeaders) {
|
||||||
super(webSocketURL, version, subprotocol, customHeaders);
|
this(webSocketURL, version, subprotocol, customHeaders, Long.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor specifying the destination web socket location and version to initiate
|
||||||
|
*
|
||||||
|
* @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<String, String> customHeaders, long maxFramePayloadLength) {
|
||||||
|
super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -148,7 +167,6 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
|||||||
}
|
}
|
||||||
request.addHeader(Names.ORIGIN, originValue);
|
request.addHeader(Names.ORIGIN, originValue);
|
||||||
|
|
||||||
|
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_KEY1, key1);
|
request.addHeader(Names.SEC_WEBSOCKET_KEY1, key1);
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_KEY2, key2);
|
request.addHeader(Names.SEC_WEBSOCKET_KEY2, key2);
|
||||||
if (getExpectedSubprotocol() != null && !getExpectedSubprotocol().equals("")) {
|
if (getExpectedSubprotocol() != null && !getExpectedSubprotocol().equals("")) {
|
||||||
@ -220,7 +238,8 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
|||||||
String protocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
String protocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||||
setActualSubprotocol(protocol);
|
setActualSubprotocol(protocol);
|
||||||
|
|
||||||
channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder", new WebSocket00FrameDecoder());
|
channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder",
|
||||||
|
new WebSocket00FrameDecoder(this.getMaxFramePayloadLength()));
|
||||||
|
|
||||||
setHandshakeComplete();
|
setHandshakeComplete();
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
|
|||||||
private final boolean allowExtensions;
|
private final boolean allowExtensions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor specifying the destination web socket location and version to initiate
|
* Constructor with default values
|
||||||
*
|
*
|
||||||
* @param webSocketURL
|
* @param webSocketURL
|
||||||
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
||||||
@ -70,7 +70,29 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
|
|||||||
*/
|
*/
|
||||||
public WebSocketClientHandshaker08(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
public WebSocketClientHandshaker08(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
||||||
boolean allowExtensions, Map<String, String> customHeaders) {
|
boolean allowExtensions, Map<String, String> 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<String, String> customHeaders, long maxFramePayloadLength) {
|
||||||
|
super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength);
|
||||||
this.allowExtensions = allowExtensions;
|
this.allowExtensions = allowExtensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +225,7 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder",
|
channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder",
|
||||||
new WebSocket08FrameDecoder(false, allowExtensions));
|
new WebSocket08FrameDecoder(false, allowExtensions, this.getMaxFramePayloadLength()));
|
||||||
|
|
||||||
setHandshakeComplete();
|
setHandshakeComplete();
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
|
|||||||
private final boolean allowExtensions;
|
private final boolean allowExtensions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor specifying the destination web socket location and version to initiate
|
* Constructor with default values
|
||||||
*
|
*
|
||||||
* @param webSocketURL
|
* @param webSocketURL
|
||||||
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
* 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,
|
public WebSocketClientHandshaker13(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
||||||
boolean allowExtensions, Map<String, String> customHeaders) {
|
boolean allowExtensions, Map<String, String> 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<String, String> customHeaders, long maxFramePayloadLength) {
|
||||||
|
super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength);
|
||||||
this.allowExtensions = allowExtensions;
|
this.allowExtensions = allowExtensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,7 +222,7 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder",
|
channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder",
|
||||||
new WebSocket13FrameDecoder(false, allowExtensions));
|
new WebSocket13FrameDecoder(false, allowExtensions, this.getMaxFramePayloadLength()));
|
||||||
|
|
||||||
setHandshakeComplete();
|
setHandshakeComplete();
|
||||||
}
|
}
|
||||||
|
@ -41,17 +41,40 @@ public class WebSocketClientHandshakerFactory {
|
|||||||
*/
|
*/
|
||||||
public WebSocketClientHandshaker newHandshaker(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
public WebSocketClientHandshaker newHandshaker(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
||||||
boolean allowExtensions, Map<String, String> customHeaders) throws WebSocketHandshakeException {
|
boolean allowExtensions, Map<String, String> 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<String, String> customHeaders, long maxFramePayloadLength) throws WebSocketHandshakeException {
|
||||||
if (version == WebSocketVersion.V13) {
|
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) {
|
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) {
|
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.");
|
throw new WebSocketHandshakeException("Protocol version " + version.toString() + " not supported.");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,23 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
|
|
||||||
private final WebSocketVersion version;
|
private final WebSocketVersion version;
|
||||||
|
|
||||||
|
private final long maxFramePayloadLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Constructor specifying the destination web socket location
|
||||||
*
|
*
|
||||||
@ -43,9 +60,11 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
* sent to this URL.
|
* sent to this URL.
|
||||||
* @param subprotocols
|
* @param subprotocols
|
||||||
* CSV of supported protocols. Null if sub protocols not supported.
|
* CSV of supported protocols. Null if sub protocols not supported.
|
||||||
|
* @param maxFramePayloadLength
|
||||||
|
* Maximum length of a frame's payload
|
||||||
*/
|
*/
|
||||||
protected WebSocketServerHandshaker(
|
protected WebSocketServerHandshaker(WebSocketVersion version, String webSocketUrl, String subprotocols,
|
||||||
WebSocketVersion version, String webSocketUrl, String subprotocols) {
|
long maxFramePayloadLength) {
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.webSocketUrl = webSocketUrl;
|
this.webSocketUrl = webSocketUrl;
|
||||||
if (subprotocols != null) {
|
if (subprotocols != null) {
|
||||||
@ -57,6 +76,7 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
} else {
|
} else {
|
||||||
this.subprotocols = new String[0];
|
this.subprotocols = new String[0];
|
||||||
}
|
}
|
||||||
|
this.maxFramePayloadLength = maxFramePayloadLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,6 +104,13 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the max length for any frame's payload
|
||||||
|
*/
|
||||||
|
public long getMaxFramePayloadLength() {
|
||||||
|
return maxFramePayloadLength;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the opening handshake
|
* Performs the opening handshake
|
||||||
*
|
*
|
||||||
|
@ -51,7 +51,7 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
|
|||||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketServerHandshaker00.class);
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketServerHandshaker00.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor specifying the destination web socket location
|
* Constructor with default values
|
||||||
*
|
*
|
||||||
* @param webSocketURL
|
* @param webSocketURL
|
||||||
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
||||||
@ -60,7 +60,23 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
|
|||||||
* CSV of supported protocols
|
* CSV of supported protocols
|
||||||
*/
|
*/
|
||||||
public WebSocketServerHandshaker00(String webSocketURL, String subprotocols) {
|
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) {
|
if (p.get(HttpChunkAggregator.class) != null) {
|
||||||
p.remove(HttpChunkAggregator.class);
|
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);
|
ChannelFuture future = channel.write(res);
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
|
|||||||
private final boolean allowExtensions;
|
private final boolean allowExtensions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor specifying the destination web socket location
|
* Constructor using defaults
|
||||||
*
|
*
|
||||||
* @param webSocketURL
|
* @param webSocketURL
|
||||||
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
||||||
@ -61,7 +61,26 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
|
|||||||
* Allow extensions to be used in the reserved bits of the web socket frame
|
* Allow extensions to be used in the reserved bits of the web socket frame
|
||||||
*/
|
*/
|
||||||
public WebSocketServerHandshaker08(String webSocketURL, String subprotocols, boolean allowExtensions) {
|
public WebSocketServerHandshaker08(String webSocketURL, String subprotocols, boolean allowExtensions) {
|
||||||
super(WebSocketVersion.V08, 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 WebSocketServerHandshaker08(String webSocketURL, String subprotocols, boolean allowExtensions,
|
||||||
|
long maxFramePayloadLength) {
|
||||||
|
super(WebSocketVersion.V08, webSocketURL, subprotocols, maxFramePayloadLength);
|
||||||
this.allowExtensions = allowExtensions;
|
this.allowExtensions = allowExtensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +161,8 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
|
|||||||
p.remove(HttpChunkAggregator.class);
|
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));
|
p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket08FrameEncoder(false));
|
||||||
|
|
||||||
return future;
|
return future;
|
||||||
|
@ -51,7 +51,7 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
|
|||||||
private final boolean allowExtensions;
|
private final boolean allowExtensions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor specifying the destination web socket location
|
* Constructor using defaults
|
||||||
*
|
*
|
||||||
* @param webSocketURL
|
* @param webSocketURL
|
||||||
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
||||||
@ -62,7 +62,26 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
|
|||||||
* Allow extensions to be used in the reserved bits of the web socket frame
|
* Allow extensions to be used in the reserved bits of the web socket frame
|
||||||
*/
|
*/
|
||||||
public WebSocketServerHandshaker13(String webSocketURL, String subprotocols, boolean allowExtensions) {
|
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;
|
this.allowExtensions = allowExtensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +162,8 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
|
|||||||
p.remove(HttpChunkAggregator.class);
|
p.remove(HttpChunkAggregator.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
p.replace(HttpRequestDecoder.class, "wsdecoder", new WebSocket13FrameDecoder(true, allowExtensions));
|
p.replace(HttpRequestDecoder.class, "wsdecoder",
|
||||||
|
new WebSocket13FrameDecoder(true, allowExtensions, this.getMaxFramePayloadLength()));
|
||||||
p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket13FrameEncoder(false));
|
p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket13FrameEncoder(false));
|
||||||
|
|
||||||
return future;
|
return future;
|
||||||
|
@ -34,6 +34,8 @@ public class WebSocketServerHandshakerFactory {
|
|||||||
|
|
||||||
private final boolean allowExtensions;
|
private final boolean allowExtensions;
|
||||||
|
|
||||||
|
private final long maxFramePayloadLength;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor specifying the destination web socket location
|
* Constructor specifying the destination web socket location
|
||||||
*
|
*
|
||||||
@ -46,11 +48,32 @@ public class WebSocketServerHandshakerFactory {
|
|||||||
* Allow extensions to be used in the reserved bits of the web socket frame
|
* 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, 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. 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.webSocketURL = webSocketURL;
|
||||||
this.subprotocols = subprotocols;
|
this.subprotocols = subprotocols;
|
||||||
this.allowExtensions = allowExtensions;
|
this.allowExtensions = allowExtensions;
|
||||||
|
this.maxFramePayloadLength = maxFramePayloadLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instances a new handshaker
|
* Instances a new handshaker
|
||||||
*
|
*
|
||||||
@ -63,16 +86,16 @@ public class WebSocketServerHandshakerFactory {
|
|||||||
if (version != null) {
|
if (version != null) {
|
||||||
if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) {
|
if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) {
|
||||||
// Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification).
|
// 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())) {
|
} else if (version.equals(WebSocketVersion.V08.toHttpHeaderValue())) {
|
||||||
// Version 8 of the wire protocol - version 10 of the draft hybi specification.
|
// 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 {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Assume version 00 where version header was not specified
|
// Assume version 00 where version header was not specified
|
||||||
return new WebSocketServerHandshaker00(webSocketURL, subprotocols);
|
return new WebSocketServerHandshaker00(webSocketURL, subprotocols, maxFramePayloadLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ public class WebSocketServerHandshaker00Test {
|
|||||||
ChannelBuffer buffer = ChannelBuffers.copiedBuffer("^n:ds[4U", Charset.defaultCharset());
|
ChannelBuffer buffer = ChannelBuffers.copiedBuffer("^n:ds[4U", Charset.defaultCharset());
|
||||||
req.setContent(buffer);
|
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);
|
handsaker.handshake(channelMock, req);
|
||||||
|
|
||||||
Assert.assertEquals("ws://example.com/chat", res.getValue().getHeader(Names.SEC_WEBSOCKET_LOCATION));
|
Assert.assertEquals("ws://example.com/chat", res.getValue().getHeader(Names.SEC_WEBSOCKET_LOCATION));
|
||||||
|
@ -68,7 +68,7 @@ public class WebSocketServerHandshaker08Test {
|
|||||||
req.setHeader(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
|
req.setHeader(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_VERSION, "8");
|
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);
|
handsaker.handshake(channelMock, req);
|
||||||
|
|
||||||
Assert.assertEquals("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.getValue().getHeader(Names.SEC_WEBSOCKET_ACCEPT));
|
Assert.assertEquals("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.getValue().getHeader(Names.SEC_WEBSOCKET_ACCEPT));
|
||||||
|
@ -67,7 +67,7 @@ public class WebSocketServerHandshaker13Test {
|
|||||||
req.setHeader(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com");
|
req.setHeader(Names.SEC_WEBSOCKET_ORIGIN, "http://example.com");
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
|
req.setHeader(Names.SEC_WEBSOCKET_PROTOCOL, "chat, superchat");
|
||||||
req.setHeader(Names.SEC_WEBSOCKET_VERSION, "13");
|
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);
|
handsaker.handshake(channelMock, req);
|
||||||
|
|
||||||
Assert.assertEquals("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.getValue().getHeader(Names.SEC_WEBSOCKET_ACCEPT));
|
Assert.assertEquals("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", res.getValue().getHeader(Names.SEC_WEBSOCKET_ACCEPT));
|
||||||
|
@ -117,7 +117,7 @@ public class WebSocketClient {
|
|||||||
|
|
||||||
// Send 10 messages and wait for responses
|
// Send 10 messages and wait for responses
|
||||||
logger.info("WebSocket Client sending message");
|
logger.info("WebSocket Client sending message");
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 1000; i++) {
|
||||||
ch.write(new TextWebSocketFrame("Message #" + i));
|
ch.write(new TextWebSocketFrame("Message #" + i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user