Added an option to use websockets without masking

Motivation:

The requirement for the masking of frames and for checks of correct
masking in the websocket specifiation have a large impact on performance.
While it is mandatory for browsers to use masking there are other
applications (like IPC protocols) that want to user websocket framing and proxy-traversing
characteristics without the overhead of masking. The websocket standard
also mentions that the requirement for mask verification on server side
might be dropped in future.

Modifications:

Added an optional parameter allowMaskMismatch for the websocket decoder
that allows a server to also accept unmasked frames (and clients to accept
masked frames).
Allowed to set this option through the websocket handshaker
constructors as well as the websocket client and server handlers.
The public API for existing components doesn't change, it will be
forwarded to functions which implicetly set masking as required in the
specification.
For websocket clients an additional parameter is added that allows to
disable the masking of frames that are sent by the client.

Result:

This update gives netty users the ability to create and use completely
unmasked websocket connections in addition to the normal masked channels
that the standard describes.
This commit is contained in:
Matthias Einwag 2014-10-22 21:59:45 +02:00 committed by Trustin Lee
parent e30839a1be
commit 82461f0511
18 changed files with 396 additions and 46 deletions

View File

@ -61,7 +61,7 @@ public class WebSocket07FrameDecoder extends WebSocket08FrameDecoder {
/** /**
* Constructor * Constructor
* *
* @param maskedPayload * @param expectMaskedFrames
* 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
* must set this to false. * must set this to false.
* @param allowExtensions * @param allowExtensions
@ -70,7 +70,27 @@ public class WebSocket07FrameDecoder extends WebSocket08FrameDecoder {
* Maximum length of a frame's payload. Setting this to an appropriate value for you application * Maximum length of a frame's payload. Setting this to an appropriate value for you application
* helps check for denial of services attacks. * helps check for denial of services attacks.
*/ */
public WebSocket07FrameDecoder(boolean maskedPayload, boolean allowExtensions, int maxFramePayloadLength) { public WebSocket07FrameDecoder(boolean expectMaskedFrames, boolean allowExtensions, int maxFramePayloadLength) {
super(maskedPayload, allowExtensions, maxFramePayloadLength); this(expectMaskedFrames, allowExtensions, maxFramePayloadLength, false);
}
/**
* Constructor
*
* @param expectMaskedFrames
* 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.
* @param allowMaskMismatch
* Allows to loosen the masking requirement on received frames. When this is set to false then also
* frames which are not masked properly according to the standard will still be accepted.
*/
public WebSocket07FrameDecoder(boolean expectMaskedFrames, boolean allowExtensions, int maxFramePayloadLength,
boolean allowMaskMismatch) {
super(expectMaskedFrames, allowExtensions, maxFramePayloadLength, allowMaskMismatch);
} }
} }

View File

@ -95,10 +95,12 @@ public class WebSocket08FrameDecoder extends ByteToMessageDecoder
private final long maxFramePayloadLength; private final long maxFramePayloadLength;
private final boolean allowExtensions; private final boolean allowExtensions;
private final boolean maskedPayload; private final boolean expectMaskedFrames;
private final boolean allowMaskMismatch;
private int fragmentedFramesCount; private int fragmentedFramesCount;
private boolean frameFinalFlag; private boolean frameFinalFlag;
private boolean frameMasked;
private int frameRsv; private int frameRsv;
private int frameOpcode; private int frameOpcode;
private long framePayloadLength; private long framePayloadLength;
@ -110,7 +112,7 @@ public class WebSocket08FrameDecoder extends ByteToMessageDecoder
/** /**
* Constructor * Constructor
* *
* @param maskedPayload * @param expectMaskedFrames
* 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
* must set this to false. * must set this to false.
* @param allowExtensions * @param allowExtensions
@ -119,8 +121,29 @@ public class WebSocket08FrameDecoder extends ByteToMessageDecoder
* Maximum length of a frame's payload. Setting this to an appropriate value for you application * Maximum length of a frame's payload. Setting this to an appropriate value for you application
* helps check for denial of services attacks. * helps check for denial of services attacks.
*/ */
public WebSocket08FrameDecoder(boolean maskedPayload, boolean allowExtensions, int maxFramePayloadLength) { public WebSocket08FrameDecoder(boolean expectMaskedFrames, boolean allowExtensions, int maxFramePayloadLength) {
this.maskedPayload = maskedPayload; this(expectMaskedFrames, allowExtensions, maxFramePayloadLength, false);
}
/**
* Constructor
*
* @param expectMaskedFrames
* 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.
* @param allowMaskMismatch
* Allows to loosen the masking requirement on received frames. When this is set to false then also
* frames which are not masked properly according to the standard will still be accepted.
*/
public WebSocket08FrameDecoder(boolean expectMaskedFrames, boolean allowExtensions, int maxFramePayloadLength,
boolean allowMaskMismatch) {
this.expectMaskedFrames = expectMaskedFrames;
this.allowMaskMismatch = allowMaskMismatch;
this.allowExtensions = allowExtensions; this.allowExtensions = allowExtensions;
this.maxFramePayloadLength = maxFramePayloadLength; this.maxFramePayloadLength = maxFramePayloadLength;
} }
@ -158,7 +181,7 @@ public class WebSocket08FrameDecoder extends ByteToMessageDecoder
} }
// MASK, PAYLOAD LEN 1 // MASK, PAYLOAD LEN 1
b = in.readByte(); b = in.readByte();
boolean frameMasked = (b & 0x80) != 0; frameMasked = (b & 0x80) != 0;
framePayloadLen1 = b & 0x7F; framePayloadLen1 = b & 0x7F;
if (frameRsv != 0 && !allowExtensions) { if (frameRsv != 0 && !allowExtensions) {
@ -166,10 +189,11 @@ public class WebSocket08FrameDecoder extends ByteToMessageDecoder
return; return;
} }
if (maskedPayload && !frameMasked) { if (!allowMaskMismatch && expectMaskedFrames != frameMasked) {
protocolViolation(ctx, "unmasked client to server frame"); protocolViolation(ctx, "received a frame that is not masked as expected");
return; return;
} }
if (frameOpcode > 7) { // control frame (have MSB in opcode set) if (frameOpcode > 7) { // control frame (have MSB in opcode set)
// control frames MUST NOT be fragmented // control frames MUST NOT be fragmented
@ -260,7 +284,7 @@ public class WebSocket08FrameDecoder extends ByteToMessageDecoder
state = State.MASKING_KEY; state = State.MASKING_KEY;
case MASKING_KEY: case MASKING_KEY:
if (maskedPayload) { if (frameMasked) {
if (in.readableBytes() < 4) { if (in.readableBytes() < 4) {
return; return;
} }
@ -284,7 +308,7 @@ public class WebSocket08FrameDecoder extends ByteToMessageDecoder
state = State.READING_FIRST; state = State.READING_FIRST;
// Unmask data if needed // Unmask data if needed
if (maskedPayload) { if (frameMasked) {
unmask(payloadBuffer); unmask(payloadBuffer);
} }

View File

@ -61,7 +61,7 @@ public class WebSocket13FrameDecoder extends WebSocket08FrameDecoder {
/** /**
* Constructor * Constructor
* *
* @param maskedPayload * @param expectMaskedFrames
* 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
* must set this to false. * must set this to false.
* @param allowExtensions * @param allowExtensions
@ -70,7 +70,27 @@ public class WebSocket13FrameDecoder extends WebSocket08FrameDecoder {
* Maximum length of a frame's payload. Setting this to an appropriate value for you application * Maximum length of a frame's payload. Setting this to an appropriate value for you application
* helps check for denial of services attacks. * helps check for denial of services attacks.
*/ */
public WebSocket13FrameDecoder(boolean maskedPayload, boolean allowExtensions, int maxFramePayloadLength) { public WebSocket13FrameDecoder(boolean expectMaskedFrames, boolean allowExtensions, int maxFramePayloadLength) {
super(maskedPayload, allowExtensions, maxFramePayloadLength); this(expectMaskedFrames, allowExtensions, maxFramePayloadLength, false);
}
/**
* Constructor
*
* @param expectMaskedFrames
* 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.
* @param allowMaskMismatch
* Allows to loosen the masking requirement on received frames. When this is set to false then also
* frames which are not masked properly according to the standard will still be accepted.
*/
public WebSocket13FrameDecoder(boolean expectMaskedFrames, boolean allowExtensions, int maxFramePayloadLength,
boolean allowMaskMismatch) {
super(expectMaskedFrames, allowExtensions, maxFramePayloadLength, allowMaskMismatch);
} }
} }

View File

@ -47,6 +47,8 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
private String expectedChallengeResponseString; private String expectedChallengeResponseString;
private final boolean allowExtensions; private final boolean allowExtensions;
private final boolean performMasking;
private final boolean allowMaskMismatch;
/** /**
* Creates a new instance. * Creates a new instance.
@ -66,9 +68,41 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
* Maximum length of a frame's payload * Maximum length of a frame's payload
*/ */
public WebSocketClientHandshaker07(URI webSocketURL, WebSocketVersion version, String subprotocol, public WebSocketClientHandshaker07(URI webSocketURL, WebSocketVersion version, String subprotocol,
boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength) { boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength) {
this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, true, false);
}
/**
* Creates a new instance.
*
* @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
* @param performMasking
* Whether to mask all written websocket frames. This must be set to true in order to be fully compatible
* with the websocket specifications. Client applications that communicate with a non-standard server
* which doesn't require masking might set this to false to achieve a higher performance.
* @param allowMaskMismatch
* Allows to loosen the masking requirement on received frames. When this is set to false then also
* frames which are not masked properly according to the standard will still be accepted.
*/
public WebSocketClientHandshaker07(URI webSocketURL, WebSocketVersion version, String subprotocol,
boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength,
boolean performMasking, boolean allowMaskMismatch) {
super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength); super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength);
this.allowExtensions = allowExtensions; this.allowExtensions = allowExtensions;
this.performMasking = performMasking;
this.allowMaskMismatch = allowMaskMismatch;
} }
/** /**
@ -192,11 +226,11 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
@Override @Override
protected WebSocketFrameDecoder newWebsocketDecoder() { protected WebSocketFrameDecoder newWebsocketDecoder() {
return new WebSocket07FrameDecoder(false, allowExtensions, maxFramePayloadLength()); return new WebSocket07FrameDecoder(false, allowExtensions, maxFramePayloadLength(), allowMaskMismatch);
} }
@Override @Override
protected WebSocketFrameEncoder newWebSocketEncoder() { protected WebSocketFrameEncoder newWebSocketEncoder() {
return new WebSocket07FrameEncoder(true); return new WebSocket07FrameEncoder(performMasking);
} }
} }

View File

@ -48,6 +48,8 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
private String expectedChallengeResponseString; private String expectedChallengeResponseString;
private final boolean allowExtensions; private final boolean allowExtensions;
private final boolean performMasking;
private final boolean allowMaskMismatch;
/** /**
* Creates a new instance. * Creates a new instance.
@ -67,9 +69,41 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
* Maximum length of a frame's payload * Maximum length of a frame's payload
*/ */
public WebSocketClientHandshaker08(URI webSocketURL, WebSocketVersion version, String subprotocol, public WebSocketClientHandshaker08(URI webSocketURL, WebSocketVersion version, String subprotocol,
boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength) { boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength) {
this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, true, false);
}
/**
* Creates a new instance.
*
* @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
* @param performMasking
* Whether to mask all written websocket frames. This must be set to true in order to be fully compatible
* with the websocket specifications. Client applications that communicate with a non-standard server
* which doesn't require masking might set this to false to achieve a higher performance.
* @param allowMaskMismatch
* Allows to loosen the masking requirement on received frames. When this is set to false then also
* frames which are not masked properly according to the standard will still be accepted.
*/
public WebSocketClientHandshaker08(URI webSocketURL, WebSocketVersion version, String subprotocol,
boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength,
boolean performMasking, boolean allowMaskMismatch) {
super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength); super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength);
this.allowExtensions = allowExtensions; this.allowExtensions = allowExtensions;
this.performMasking = performMasking;
this.allowMaskMismatch = allowMaskMismatch;
} }
/** /**
@ -193,11 +227,11 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
@Override @Override
protected WebSocketFrameDecoder newWebsocketDecoder() { protected WebSocketFrameDecoder newWebsocketDecoder() {
return new WebSocket08FrameDecoder(false, allowExtensions, maxFramePayloadLength()); return new WebSocket08FrameDecoder(false, allowExtensions, maxFramePayloadLength(), allowMaskMismatch);
} }
@Override @Override
protected WebSocketFrameEncoder newWebSocketEncoder() { protected WebSocketFrameEncoder newWebSocketEncoder() {
return new WebSocket08FrameEncoder(true); return new WebSocket08FrameEncoder(performMasking);
} }
} }

View File

@ -48,6 +48,8 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
private String expectedChallengeResponseString; private String expectedChallengeResponseString;
private final boolean allowExtensions; private final boolean allowExtensions;
private final boolean performMasking;
private final boolean allowMaskMismatch;
/** /**
* Creates a new instance. * Creates a new instance.
@ -67,9 +69,41 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
* Maximum length of a frame's payload * Maximum length of a frame's payload
*/ */
public WebSocketClientHandshaker13(URI webSocketURL, WebSocketVersion version, String subprotocol, public WebSocketClientHandshaker13(URI webSocketURL, WebSocketVersion version, String subprotocol,
boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength) { boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength) {
this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, true, false);
}
/**
* Creates a new instance.
*
* @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
* @param performMasking
* Whether to mask all written websocket frames. This must be set to true in order to be fully compatible
* with the websocket specifications. Client applications that communicate with a non-standard server
* which doesn't require masking might set this to false to achieve a higher performance.
* @param allowMaskMismatch
* Allows to loosen the masking requirement on received frames. When this is set to false then also
* frames which are not masked properly according to the standard will still be accepted.
*/
public WebSocketClientHandshaker13(URI webSocketURL, WebSocketVersion version, String subprotocol,
boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength,
boolean performMasking, boolean allowMaskMismatch) {
super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength); super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength);
this.allowExtensions = allowExtensions; this.allowExtensions = allowExtensions;
this.performMasking = performMasking;
this.allowMaskMismatch = allowMaskMismatch;
} }
/** /**
@ -203,11 +237,11 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
@Override @Override
protected WebSocketFrameDecoder newWebsocketDecoder() { protected WebSocketFrameDecoder newWebsocketDecoder() {
return new WebSocket13FrameDecoder(false, allowExtensions, maxFramePayloadLength()); return new WebSocket13FrameDecoder(false, allowExtensions, maxFramePayloadLength(), allowMaskMismatch);
} }
@Override @Override
protected WebSocketFrameEncoder newWebSocketEncoder() { protected WebSocketFrameEncoder newWebSocketEncoder() {
return new WebSocket13FrameEncoder(true); return new WebSocket13FrameEncoder(performMasking);
} }
} }

View File

@ -74,17 +74,53 @@ public final class WebSocketClientHandshakerFactory {
public static WebSocketClientHandshaker newHandshaker( public static WebSocketClientHandshaker newHandshaker(
URI webSocketURL, WebSocketVersion version, String subprotocol, URI webSocketURL, WebSocketVersion version, String subprotocol,
boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength) { boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength) {
return newHandshaker(webSocketURL, version, subprotocol, allowExtensions, customHeaders,
maxFramePayloadLength, true, false);
}
/**
* 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.
* @param performMasking
* Whether to mask all written websocket frames. This must be set to true in order to be fully compatible
* with the websocket specifications. Client applications that communicate with a non-standard server
* which doesn't require masking might set this to false to achieve a higher performance.
* @param allowMaskMismatch
* Allows to loosen the masking requirement on received frames. When this is set to false then also
* frames which are not masked properly according to the standard will still be accepted.
*/
public static WebSocketClientHandshaker newHandshaker(
URI webSocketURL, WebSocketVersion version, String subprotocol,
boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength,
boolean performMasking, boolean allowMaskMismatch) {
if (version == V13) { if (version == V13) {
return new WebSocketClientHandshaker13( return new WebSocketClientHandshaker13(
webSocketURL, V13, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength); webSocketURL, V13, subprotocol, allowExtensions, customHeaders,
maxFramePayloadLength, performMasking, allowMaskMismatch);
} }
if (version == V08) { if (version == V08) {
return new WebSocketClientHandshaker08( return new WebSocketClientHandshaker08(
webSocketURL, V08, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength); webSocketURL, V08, subprotocol, allowExtensions, customHeaders,
maxFramePayloadLength, performMasking, allowMaskMismatch);
} }
if (version == V07) { if (version == V07) {
return new WebSocketClientHandshaker07( return new WebSocketClientHandshaker07(
webSocketURL, V07, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength); webSocketURL, V07, subprotocol, allowExtensions, customHeaders,
maxFramePayloadLength, performMasking, allowMaskMismatch);
} }
if (version == V00) { if (version == V00) {
return new WebSocketClientHandshaker00( return new WebSocketClientHandshaker00(

View File

@ -62,6 +62,39 @@ public class WebSocketClientProtocolHandler extends WebSocketProtocolHandler {
HANDSHAKE_COMPLETE HANDSHAKE_COMPLETE
} }
/**
* 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
* @param handleCloseFrames
* {@code true} if close frames should not be forwarded and just close the channel
* @param performMasking
* Whether to mask all written websocket frames. This must be set to true in order to be fully compatible
* with the websocket specifications. Client applications that communicate with a non-standard server
* which doesn't require masking might set this to false to achieve a higher performance.
* @param allowMaskMismatch
* Allows to loosen the masking requirement on received frames. When this is set to false then also
* frames which are not masked properly according to the standard will still be accepted.
*/
public WebSocketClientProtocolHandler(URI webSocketURL, WebSocketVersion version, String subprotocol,
boolean allowExtensions, HttpHeaders customHeaders,
int maxFramePayloadLength, boolean handleCloseFrames,
boolean performMasking, boolean allowMaskMismatch) {
this(WebSocketClientHandshakerFactory.newHandshaker(webSocketURL, version, subprotocol,
allowExtensions, customHeaders, maxFramePayloadLength,
performMasking, allowMaskMismatch), handleCloseFrames);
}
/** /**
* Base constructor * Base constructor
* *
@ -82,8 +115,8 @@ public class WebSocketClientProtocolHandler extends WebSocketProtocolHandler {
public WebSocketClientProtocolHandler(URI webSocketURL, WebSocketVersion version, String subprotocol, public WebSocketClientProtocolHandler(URI webSocketURL, WebSocketVersion version, String subprotocol,
boolean allowExtensions, HttpHeaders customHeaders, boolean allowExtensions, HttpHeaders customHeaders,
int maxFramePayloadLength, boolean handleCloseFrames) { int maxFramePayloadLength, boolean handleCloseFrames) {
this(WebSocketClientHandshakerFactory.newHandshaker(webSocketURL, version, subprotocol, this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength,
allowExtensions, customHeaders, maxFramePayloadLength), handleCloseFrames); handleCloseFrames, true, false);
} }
/** /**

View File

@ -41,6 +41,7 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker {
public static final String WEBSOCKET_07_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; public static final String WEBSOCKET_07_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
private final boolean allowExtensions; private final boolean allowExtensions;
private final boolean allowMaskMismatch;
/** /**
* Constructor specifying the destination web socket location * Constructor specifying the destination web socket location
@ -58,8 +59,32 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker {
*/ */
public WebSocketServerHandshaker07( public WebSocketServerHandshaker07(
String webSocketURL, String subprotocols, boolean allowExtensions, int maxFramePayloadLength) { String webSocketURL, String subprotocols, boolean allowExtensions, int maxFramePayloadLength) {
this(webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength, false);
}
/**
* 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.
* @param allowMaskMismatch
* Allows to loosen the masking requirement on received frames. When this is set to false then also
* frames which are not masked properly according to the standard will still be accepted.
*/
public WebSocketServerHandshaker07(
String webSocketURL, String subprotocols, boolean allowExtensions, int maxFramePayloadLength,
boolean allowMaskMismatch) {
super(WebSocketVersion.V07, webSocketURL, subprotocols, maxFramePayloadLength); super(WebSocketVersion.V07, webSocketURL, subprotocols, maxFramePayloadLength);
this.allowExtensions = allowExtensions; this.allowExtensions = allowExtensions;
this.allowMaskMismatch = allowMaskMismatch;
} }
/** /**
@ -136,7 +161,7 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker {
@Override @Override
protected WebSocketFrameDecoder newWebsocketDecoder() { protected WebSocketFrameDecoder newWebsocketDecoder() {
return new WebSocket07FrameDecoder(true, allowExtensions, maxFramePayloadLength()); return new WebSocket07FrameDecoder(true, allowExtensions, maxFramePayloadLength(), allowMaskMismatch);
} }
@Override @Override

View File

@ -41,6 +41,7 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
public static final String WEBSOCKET_08_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; public static final String WEBSOCKET_08_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
private final boolean allowExtensions; private final boolean allowExtensions;
private final boolean allowMaskMismatch;
/** /**
* Constructor specifying the destination web socket location * Constructor specifying the destination web socket location
@ -58,8 +59,32 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
*/ */
public WebSocketServerHandshaker08( public WebSocketServerHandshaker08(
String webSocketURL, String subprotocols, boolean allowExtensions, int maxFramePayloadLength) { String webSocketURL, String subprotocols, boolean allowExtensions, int maxFramePayloadLength) {
this(webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength, false);
}
/**
* 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.
* @param allowMaskMismatch
* Allows to loosen the masking requirement on received frames. When this is set to false then also
* frames which are not masked properly according to the standard will still be accepted.
*/
public WebSocketServerHandshaker08(
String webSocketURL, String subprotocols, boolean allowExtensions, int maxFramePayloadLength,
boolean allowMaskMismatch) {
super(WebSocketVersion.V08, webSocketURL, subprotocols, maxFramePayloadLength); super(WebSocketVersion.V08, webSocketURL, subprotocols, maxFramePayloadLength);
this.allowExtensions = allowExtensions; this.allowExtensions = allowExtensions;
this.allowMaskMismatch = allowMaskMismatch;
} }
/** /**
@ -135,7 +160,7 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
@Override @Override
protected WebSocketFrameDecoder newWebsocketDecoder() { protected WebSocketFrameDecoder newWebsocketDecoder() {
return new WebSocket08FrameDecoder(true, allowExtensions, maxFramePayloadLength()); return new WebSocket08FrameDecoder(true, allowExtensions, maxFramePayloadLength(), allowMaskMismatch);
} }
@Override @Override

View File

@ -40,6 +40,7 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
public static final String WEBSOCKET_13_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; public static final String WEBSOCKET_13_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
private final boolean allowExtensions; private final boolean allowExtensions;
private final boolean allowMaskMismatch;
/** /**
* Constructor specifying the destination web socket location * Constructor specifying the destination web socket location
@ -57,8 +58,32 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
*/ */
public WebSocketServerHandshaker13( public WebSocketServerHandshaker13(
String webSocketURL, String subprotocols, boolean allowExtensions, int maxFramePayloadLength) { String webSocketURL, String subprotocols, boolean allowExtensions, int maxFramePayloadLength) {
this(webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength, false);
}
/**
* 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.
* @param allowMaskMismatch
* Allows to loosen the masking requirement on received frames. When this is set to false then also
* frames which are not masked properly according to the standard will still be accepted.
*/
public WebSocketServerHandshaker13(
String webSocketURL, String subprotocols, boolean allowExtensions, int maxFramePayloadLength,
boolean allowMaskMismatch) {
super(WebSocketVersion.V13, webSocketURL, subprotocols, maxFramePayloadLength); super(WebSocketVersion.V13, webSocketURL, subprotocols, maxFramePayloadLength);
this.allowExtensions = allowExtensions; this.allowExtensions = allowExtensions;
this.allowMaskMismatch = allowMaskMismatch;
} }
/** /**
@ -133,7 +158,7 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
@Override @Override
protected WebSocketFrameDecoder newWebsocketDecoder() { protected WebSocketFrameDecoder newWebsocketDecoder() {
return new WebSocket13FrameDecoder(true, allowExtensions, maxFramePayloadLength()); return new WebSocket13FrameDecoder(true, allowExtensions, maxFramePayloadLength(), allowMaskMismatch);
} }
@Override @Override

View File

@ -39,6 +39,8 @@ public class WebSocketServerHandshakerFactory {
private final int maxFramePayloadLength; private final int maxFramePayloadLength;
private final boolean allowMaskMismatch;
/** /**
* Constructor specifying the destination web socket location * Constructor specifying the destination web socket location
* *
@ -72,10 +74,34 @@ public class WebSocketServerHandshakerFactory {
public WebSocketServerHandshakerFactory( public WebSocketServerHandshakerFactory(
String webSocketURL, String subprotocols, boolean allowExtensions, String webSocketURL, String subprotocols, boolean allowExtensions,
int maxFramePayloadLength) { int maxFramePayloadLength) {
this(webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength, false);
}
/**
* 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.
* @param allowMaskMismatch
* Allows to loosen the masking requirement on received frames. When this is set to false then also
* frames which are not masked properly according to the standard will still be accepted.
*/
public WebSocketServerHandshakerFactory(
String webSocketURL, String subprotocols, boolean allowExtensions,
int maxFramePayloadLength, boolean allowMaskMismatch) {
this.webSocketURL = webSocketURL; this.webSocketURL = webSocketURL;
this.subprotocols = subprotocols; this.subprotocols = subprotocols;
this.allowExtensions = allowExtensions; this.allowExtensions = allowExtensions;
this.maxFramePayloadLength = maxFramePayloadLength; this.maxFramePayloadLength = maxFramePayloadLength;
this.allowMaskMismatch = allowMaskMismatch;
} }
/** /**
@ -91,15 +117,15 @@ public class WebSocketServerHandshakerFactory {
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( return new WebSocketServerHandshaker13(
webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength); webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength, allowMaskMismatch);
} 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( return new WebSocketServerHandshaker08(
webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength); webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength, allowMaskMismatch);
} else if (version.equals(WebSocketVersion.V07.toHttpHeaderValue())) { } else if (version.equals(WebSocketVersion.V07.toHttpHeaderValue())) {
// Version 8 of the wire protocol - version 07 of the draft hybi specification. // Version 8 of the wire protocol - version 07 of the draft hybi specification.
return new WebSocketServerHandshaker07( return new WebSocketServerHandshaker07(
webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength); webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength, allowMaskMismatch);
} else { } else {
return null; return null;
} }

View File

@ -66,6 +66,7 @@ public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler {
private final String subprotocols; private final String subprotocols;
private final boolean allowExtensions; private final boolean allowExtensions;
private final int maxFramePayloadLength; private final int maxFramePayloadLength;
private final boolean allowMaskMismatch;
public WebSocketServerProtocolHandler(String websocketPath) { public WebSocketServerProtocolHandler(String websocketPath) {
this(websocketPath, null, false); this(websocketPath, null, false);
@ -80,11 +81,17 @@ public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler {
} }
public WebSocketServerProtocolHandler(String websocketPath, String subprotocols, public WebSocketServerProtocolHandler(String websocketPath, String subprotocols,
boolean allowExtensions, int maxFrameSize) { boolean allowExtensions, int maxFrameSize) {
this(websocketPath, subprotocols, allowExtensions, maxFrameSize, false);
}
public WebSocketServerProtocolHandler(String websocketPath, String subprotocols,
boolean allowExtensions, int maxFrameSize, boolean allowMaskMismatch) {
this.websocketPath = websocketPath; this.websocketPath = websocketPath;
this.subprotocols = subprotocols; this.subprotocols = subprotocols;
this.allowExtensions = allowExtensions; this.allowExtensions = allowExtensions;
maxFramePayloadLength = maxFrameSize; maxFramePayloadLength = maxFrameSize;
this.allowMaskMismatch = allowMaskMismatch;
} }
@Override @Override
@ -94,7 +101,7 @@ public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler {
// Add the WebSocketHandshakeHandler before this one. // Add the WebSocketHandshakeHandler before this one.
ctx.pipeline().addBefore(ctx.name(), WebSocketServerProtocolHandshakeHandler.class.getName(), ctx.pipeline().addBefore(ctx.name(), WebSocketServerProtocolHandshakeHandler.class.getName(),
new WebSocketServerProtocolHandshakeHandler(websocketPath, subprotocols, new WebSocketServerProtocolHandshakeHandler(websocketPath, subprotocols,
allowExtensions, maxFramePayloadLength)); allowExtensions, maxFramePayloadLength, allowMaskMismatch));
} }
if (cp.get(Utf8FrameValidator.class) == null) { if (cp.get(Utf8FrameValidator.class) == null) {
// Add the UFT8 checking before this one. // Add the UFT8 checking before this one.

View File

@ -41,13 +41,15 @@ class WebSocketServerProtocolHandshakeHandler extends ChannelHandlerAdapter {
private final String subprotocols; private final String subprotocols;
private final boolean allowExtensions; private final boolean allowExtensions;
private final int maxFramePayloadSize; private final int maxFramePayloadSize;
private final boolean allowMaskMismatch;
WebSocketServerProtocolHandshakeHandler(String websocketPath, String subprotocols, WebSocketServerProtocolHandshakeHandler(String websocketPath, String subprotocols,
boolean allowExtensions, int maxFrameSize) { boolean allowExtensions, int maxFrameSize, boolean allowMaskMismatch) {
this.websocketPath = websocketPath; this.websocketPath = websocketPath;
this.subprotocols = subprotocols; this.subprotocols = subprotocols;
this.allowExtensions = allowExtensions; this.allowExtensions = allowExtensions;
maxFramePayloadSize = maxFrameSize; maxFramePayloadSize = maxFrameSize;
this.allowMaskMismatch = allowMaskMismatch;
} }
@Override @Override
@ -61,7 +63,7 @@ class WebSocketServerProtocolHandshakeHandler extends ChannelHandlerAdapter {
final WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( final WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
getWebSocketLocation(ctx.pipeline(), req, websocketPath), subprotocols, getWebSocketLocation(ctx.pipeline(), req, websocketPath), subprotocols,
allowExtensions, maxFramePayloadSize); allowExtensions, maxFramePayloadSize, allowMaskMismatch);
final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req); final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req);
if (handshaker == null) { if (handshaker == null) {
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());

View File

@ -59,12 +59,17 @@ public class WebSocket08EncoderDecoderTest {
// Test without masking // Test without masking
EmbeddedChannel outChannel = new EmbeddedChannel(new WebSocket08FrameEncoder(false)); EmbeddedChannel outChannel = new EmbeddedChannel(new WebSocket08FrameEncoder(false));
EmbeddedChannel inChannel = new EmbeddedChannel(new WebSocket08FrameDecoder(false, false, 1024 * 1024)); EmbeddedChannel inChannel = new EmbeddedChannel(new WebSocket08FrameDecoder(false, false, 1024 * 1024, false));
executeTests(outChannel, inChannel); executeTests(outChannel, inChannel);
// Test with activated masking // Test with activated masking
outChannel = new EmbeddedChannel(new WebSocket08FrameEncoder(true)); outChannel = new EmbeddedChannel(new WebSocket08FrameEncoder(true));
inChannel = new EmbeddedChannel(new WebSocket08FrameDecoder(true, false, 1024 * 1024)); inChannel = new EmbeddedChannel(new WebSocket08FrameDecoder(true, false, 1024 * 1024, false));
executeTests(outChannel, inChannel);
// Test with activated masking and an unmasked expecting but forgiving decoder
outChannel = new EmbeddedChannel(new WebSocket08FrameEncoder(true));
inChannel = new EmbeddedChannel(new WebSocket08FrameDecoder(false, false, 1024 * 1024, true));
executeTests(outChannel, inChannel); executeTests(outChannel, inChannel);
// Release test data // Release test data

View File

@ -21,7 +21,7 @@ public class WebSocket08FrameDecoderTest {
@Test @Test
public void channelInactive() throws Exception { public void channelInactive() throws Exception {
final WebSocket08FrameDecoder decoder = new WebSocket08FrameDecoder(true, true, 65535); final WebSocket08FrameDecoder decoder = new WebSocket08FrameDecoder(true, true, 65535, false);
final ChannelHandlerContext ctx = EasyMock.createMock(ChannelHandlerContext.class); final ChannelHandlerContext ctx = EasyMock.createMock(ChannelHandlerContext.class);
decoder.channelInactive(ctx); decoder.channelInactive(ctx);
} }

View File

@ -61,10 +61,10 @@ public class WebSocketServerHandshaker08Test {
if (subProtocol) { if (subProtocol) {
new WebSocketServerHandshaker08( new WebSocketServerHandshaker08(
"ws://example.com/chat", "chat", false, Integer.MAX_VALUE).handshake(ch, req); "ws://example.com/chat", "chat", false, Integer.MAX_VALUE, false).handshake(ch, req);
} else { } else {
new WebSocketServerHandshaker08( new WebSocketServerHandshaker08(
"ws://example.com/chat", null, false, Integer.MAX_VALUE).handshake(ch, req); "ws://example.com/chat", null, false, Integer.MAX_VALUE, false).handshake(ch, req);
} }
ByteBuf resBuf = ch.readOutbound(); ByteBuf resBuf = ch.readOutbound();

View File

@ -61,10 +61,10 @@ public class WebSocketServerHandshaker13Test {
if (subProtocol) { if (subProtocol) {
new WebSocketServerHandshaker13( new WebSocketServerHandshaker13(
"ws://example.com/chat", "chat", false, Integer.MAX_VALUE).handshake(ch, req); "ws://example.com/chat", "chat", false, Integer.MAX_VALUE, false).handshake(ch, req);
} else { } else {
new WebSocketServerHandshaker13( new WebSocketServerHandshaker13(
"ws://example.com/chat", null, false, Integer.MAX_VALUE).handshake(ch, req); "ws://example.com/chat", null, false, Integer.MAX_VALUE, false).handshake(ch, req);
} }
ByteBuf resBuf = ch.readOutbound(); ByteBuf resBuf = ch.readOutbound();