From 8efe131eb0881e8c39eca1e537006224be1ef6da Mon Sep 17 00:00:00 2001 From: Veebs Date: Mon, 28 Nov 2011 22:27:58 +1100 Subject: [PATCH 1/4] Fix closing handshake and run against Autobahn V0.4.3 --- .../websocketx/autobahn/package-info.java | 13 ++++++++----- .../websocketx/WebSocket08FrameDecoder.java | 19 +++++++++++-------- .../WebSocketServerHandshaker10.java | 7 +++++-- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/autobahn/package-info.java b/src/main/java/org/jboss/netty/example/http/websocketx/autobahn/package-info.java index 46de920b06..3d010b0da2 100644 --- a/src/main/java/org/jboss/netty/example/http/websocketx/autobahn/package-info.java +++ b/src/main/java/org/jboss/netty/example/http/websocketx/autobahn/package-info.java @@ -32,20 +32,23 @@ * *

06. Go to AutoBahn directory: cd Autobahn * - *

07. Checkout stable version: git checkout v0.4.2 + *

07. Checkout stable version: git checkout v0.4.3 * *

08. Go to test suite directory: cd testsuite/websockets * - *

09. Edit fuzzing_clinet_spec.json and set the version to 10. + *

09. Edit fuzzing_clinet_spec.json and set the version to 10 or 17. * * { - * "servers": [{"agent": "Netty", "hostname": "localhost", "port": 9000, "version": 10}], - * "cases": ["*"] + * "options": {"failByDrop": false}, + * "servers": [{"agent": "Netty", "url": "ws://localhost:9000", "options": {"version": 17}}], + * "cases": ["*"], + * "exclude-cases": [], + * "exclude-agent-cases": {"FoobarServer*": ["4.*", "1.1.3"]} * } * * *

10. Run the test python fuzzing_client.py. Note that the actual test case python code is - * located in /usr/local/lib/python2.6/dist-packages/autobahn-0.4.2-py2.6.egg/autobahn/cases + * located in /usr/local/lib/python2.6/dist-packages/autobahn-0.4.3-py2.6.egg/autobahn/cases * and not in the checked out git repository. * *

11. See the results in reports/servers/index.html 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 146768db62..9ac57aa992 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 @@ -260,7 +260,17 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder Date: Tue, 29 Nov 2011 09:45:13 +1100 Subject: [PATCH 2/4] Added support for Hybi V17 and run against Autobahn V0.4.3 --- .../websocketx/WebSocket08FrameDecoder.java | 556 +++++++++--------- .../websocketx/WebSocket08FrameEncoder.java | 6 +- .../websocketx/WebSocket13FrameDecoder.java | 61 ++ .../websocketx/WebSocket13FrameEncoder.java | 62 ++ .../WebSocketClientHandshaker17.java | 186 ++++++ .../WebSocketClientHandshakerFactory.java | 3 + .../WebSocketServerHandshaker17.java | 168 ++++++ .../WebSocketServerHandshakerFactory.java | 115 ++-- .../WebSocketSpecificationVersion.java | 10 +- 9 files changed, 835 insertions(+), 332 deletions(-) create mode 100644 src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket13FrameDecoder.java create mode 100644 src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket13FrameEncoder.java create mode 100644 src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker17.java create mode 100644 src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker17.java 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 9ac57aa992..4a346f1a25 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 @@ -59,317 +59,325 @@ import org.jboss.netty.logging.InternalLoggerFactory; */ public class WebSocket08FrameDecoder extends ReplayingDecoder { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocket08FrameDecoder.class); + private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocket08FrameDecoder.class); - private static final byte OPCODE_CONT = 0x0; - private static final byte OPCODE_TEXT = 0x1; - private static final byte OPCODE_BINARY = 0x2; - private static final byte OPCODE_CLOSE = 0x8; - private static final byte OPCODE_PING = 0x9; - private static final byte OPCODE_PONG = 0xA; + private static final byte OPCODE_CONT = 0x0; + private static final byte OPCODE_TEXT = 0x1; + private static final byte OPCODE_BINARY = 0x2; + private static final byte OPCODE_CLOSE = 0x8; + private static final byte OPCODE_PING = 0x9; + private static final byte OPCODE_PONG = 0xA; - private UTF8Output fragmentedFramesText = null; - private int fragmentedFramesCount = 0; + private UTF8Output fragmentedFramesText = null; + private int fragmentedFramesCount = 0; - private boolean frameFinalFlag; - private int frameRsv; - private int frameOpcode; - private long framePayloadLength; - private ChannelBuffer framePayload = null; - private int framePayloadBytesRead = 0; - private ChannelBuffer maskingKey; + private boolean frameFinalFlag; + private int frameRsv; + private int frameOpcode; + private long framePayloadLength; + private ChannelBuffer framePayload = null; + private int framePayloadBytesRead = 0; + private ChannelBuffer maskingKey; - private boolean allowExtensions = false; - private boolean maskedPayload = false; - private boolean receivedClosingHandshake = false; + private boolean allowExtensions = false; + private boolean maskedPayload = false; + private boolean receivedClosingHandshake = false; - public enum State { - FRAME_START, MASKING_KEY, PAYLOAD, CORRUPT - } + public enum State { + FRAME_START, MASKING_KEY, PAYLOAD, CORRUPT + } - /** - * 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 - */ - public WebSocket08FrameDecoder(boolean maskedPayload, boolean allowExtensions) { - super(State.FRAME_START); - this.maskedPayload = maskedPayload; - this.allowExtensions = allowExtensions; - } + /** + * 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 + */ + public WebSocket08FrameDecoder(boolean maskedPayload, boolean allowExtensions) { + super(State.FRAME_START); + this.maskedPayload = maskedPayload; + this.allowExtensions = allowExtensions; + } - @Override - protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, State state) throws Exception { + @Override + protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, State state) + throws Exception { - // Discard all data received if closing handshake was received before. - if (receivedClosingHandshake) { - buffer.skipBytes(actualReadableBytes()); - return null; - } + // Discard all data received if closing handshake was received before. + if (receivedClosingHandshake) { + buffer.skipBytes(actualReadableBytes()); + return null; + } - switch (state) { - case FRAME_START: - framePayloadBytesRead = 0; - framePayloadLength = -1; - framePayload = null; + switch (state) { + case FRAME_START: + framePayloadBytesRead = 0; + framePayloadLength = -1; + framePayload = null; - // FIN, RSV, OPCODE - byte b = buffer.readByte(); - frameFinalFlag = (b & 0x80) != 0; - frameRsv = (b & 0x70) >> 4; - frameOpcode = (b & 0x0F); + // FIN, RSV, OPCODE + byte b = buffer.readByte(); + frameFinalFlag = (b & 0x80) != 0; + frameRsv = (b & 0x70) >> 4; + frameOpcode = (b & 0x0F); - logger.debug("Decoding WebSocket Frame opCode=" + frameOpcode); + if (logger.isDebugEnabled()) { + logger.debug("Decoding WebSocket Frame opCode=" + frameOpcode); + } - // MASK, PAYLOAD LEN 1 - b = buffer.readByte(); - boolean frameMasked = (b & 0x80) != 0; - int framePayloadLen1 = (b & 0x7F); + // MASK, PAYLOAD LEN 1 + b = buffer.readByte(); + boolean frameMasked = (b & 0x80) != 0; + int framePayloadLen1 = (b & 0x7F); - if (frameRsv != 0 && !this.allowExtensions) { - protocolViolation(channel, "RSV != 0 and no extension negotiated, RSV:" + frameRsv); - return null; - } + if (frameRsv != 0 && !this.allowExtensions) { + protocolViolation(channel, "RSV != 0 and no extension negotiated, RSV:" + frameRsv); + return null; + } - if (this.maskedPayload && !frameMasked) { - protocolViolation(channel, "unmasked client to server frame"); - return null; - } - if (frameOpcode > 7) { // control frame (have MSB in opcode set) + if (this.maskedPayload && !frameMasked) { + protocolViolation(channel, "unmasked client to server frame"); + return null; + } + if (frameOpcode > 7) { // control frame (have MSB in opcode set) - // control frames MUST NOT be fragmented - if (!frameFinalFlag) { - protocolViolation(channel, "fragmented control frame"); - return null; - } + // control frames MUST NOT be fragmented + if (!frameFinalFlag) { + protocolViolation(channel, "fragmented control frame"); + return null; + } - // control frames MUST have payload 125 octets or less - if (framePayloadLen1 > 125) { - protocolViolation(channel, "control frame with payload length > 125 octets"); - return null; - } + // control frames MUST have payload 125 octets or less + if (framePayloadLen1 > 125) { + protocolViolation(channel, "control frame with payload length > 125 octets"); + return null; + } - // check for reserved control frame opcodes - if (!(frameOpcode == OPCODE_CLOSE || frameOpcode == OPCODE_PING || frameOpcode == OPCODE_PONG)) { - protocolViolation(channel, "control frame using reserved opcode " + frameOpcode); - return null; - } + // check for reserved control frame opcodes + if (!(frameOpcode == OPCODE_CLOSE || frameOpcode == OPCODE_PING || frameOpcode == OPCODE_PONG)) { + protocolViolation(channel, "control frame using reserved opcode " + frameOpcode); + return null; + } - // close frame : if there is a body, the first two bytes of the - // body MUST be a 2-byte - // unsigned integer (in network byte order) representing a - // status code - if (frameOpcode == 8 && framePayloadLen1 == 1) { - protocolViolation(channel, "received close control frame with payload len 1"); - return null; - } - } else { // data frame - // check for reserved data frame opcodes - if (!(frameOpcode == OPCODE_CONT || frameOpcode == OPCODE_TEXT || frameOpcode == OPCODE_BINARY)) { - protocolViolation(channel, "data frame using reserved opcode " + frameOpcode); - return null; - } + // close frame : if there is a body, the first two bytes of the + // body MUST be a 2-byte unsigned integer (in network byte + // order) representing a status code + if (frameOpcode == 8 && framePayloadLen1 == 1) { + protocolViolation(channel, "received close control frame with payload len 1"); + return null; + } + } else { // data frame + // check for reserved data frame opcodes + if (!(frameOpcode == OPCODE_CONT || frameOpcode == OPCODE_TEXT || frameOpcode == OPCODE_BINARY)) { + protocolViolation(channel, "data frame using reserved opcode " + frameOpcode); + return null; + } - // check opcode vs message fragmentation state 1/2 - if (fragmentedFramesCount == 0 && frameOpcode == OPCODE_CONT) { - protocolViolation(channel, "received continuation data frame outside fragmented message"); - return null; - } + // check opcode vs message fragmentation state 1/2 + if (fragmentedFramesCount == 0 && frameOpcode == OPCODE_CONT) { + protocolViolation(channel, "received continuation data frame outside fragmented message"); + return null; + } - // check opcode vs message fragmentation state 2/2 - if (fragmentedFramesCount != 0 && frameOpcode != OPCODE_CONT && frameOpcode != OPCODE_PING) { - protocolViolation(channel, "received non-continuation data frame while inside fragmented message"); - return null; - } - } + // check opcode vs message fragmentation state 2/2 + if (fragmentedFramesCount != 0 && frameOpcode != OPCODE_CONT && frameOpcode != OPCODE_PING) { + protocolViolation(channel, "received non-continuation data frame while inside fragmented message"); + return null; + } + } - if (framePayloadLen1 == 126) { - framePayloadLength = buffer.readUnsignedShort(); - if (framePayloadLength < 126) { - protocolViolation(channel, "invalid data frame length (not using minimal length encoding)"); - return null; - } - } else if (framePayloadLen1 == 127) { - framePayloadLength = buffer.readLong(); - // TODO: check if it's bigger than 0x7FFFFFFFFFFFFFFF, Maybe - // just check if it's negative? + // Read frame payload length + if (framePayloadLen1 == 126) { + framePayloadLength = buffer.readUnsignedShort(); + if (framePayloadLength < 126) { + protocolViolation(channel, "invalid data frame length (not using minimal length encoding)"); + return null; + } + } else if (framePayloadLen1 == 127) { + framePayloadLength = buffer.readLong(); + // TODO: check if it's bigger than 0x7FFFFFFFFFFFFFFF, Maybe + // just check if it's negative? - if (framePayloadLength < 65536) { - protocolViolation(channel, "invalid data frame length (not using minimal length encoding)"); - return null; - } - } else { - framePayloadLength = framePayloadLen1; - } + if (framePayloadLength < 65536) { + protocolViolation(channel, "invalid data frame length (not using minimal length encoding)"); + return null; + } + } else { + framePayloadLength = framePayloadLen1; + } - // logger.debug("Frame length=" + framePayloadLength); - checkpoint(State.MASKING_KEY); - case MASKING_KEY: - if (this.maskedPayload) { - maskingKey = buffer.readBytes(4); - } - checkpoint(State.PAYLOAD); - case PAYLOAD: - // Some times, the payload may not be delivered in 1 nice packet - // We need to accumulate the data until we have it all - int rbytes = actualReadableBytes(); - ChannelBuffer payloadBuffer = null; + if (logger.isDebugEnabled()) { + logger.debug("Decoding WebSocket Frame length=" + framePayloadLength); + } - int willHaveReadByteCount = framePayloadBytesRead + rbytes; - // logger.debug("Frame rbytes=" + rbytes + " willHaveReadByteCount=" - // + willHaveReadByteCount + " framePayloadLength=" + - // framePayloadLength); - if (willHaveReadByteCount == framePayloadLength) { - // We have all our content so proceed to process - payloadBuffer = buffer.readBytes(rbytes); - } else if (willHaveReadByteCount < framePayloadLength) { - // We don't have all our content so accumulate payload. - // Returning null means we will get called back - payloadBuffer = buffer.readBytes(rbytes); - if (framePayload == null) { - framePayload = channel.getConfig().getBufferFactory().getBuffer(toFrameLength(framePayloadLength)); - } - framePayload.writeBytes(payloadBuffer); - framePayloadBytesRead = framePayloadBytesRead + rbytes; + checkpoint(State.MASKING_KEY); + case MASKING_KEY: + if (this.maskedPayload) { + maskingKey = buffer.readBytes(4); + } + checkpoint(State.PAYLOAD); + case PAYLOAD: + // Sometimes, the payload may not be delivered in 1 nice packet + // We need to accumulate the data until we have it all + int rbytes = actualReadableBytes(); + ChannelBuffer payloadBuffer = null; - // Return null to wait for more bytes to arrive - return null; - } else if (willHaveReadByteCount > framePayloadLength) { - // We have more than what we need so read up to the end of frame - // Leave the remainder in the buffer for next frame - payloadBuffer = buffer.readBytes(toFrameLength(framePayloadLength - framePayloadBytesRead)); - } + int willHaveReadByteCount = framePayloadBytesRead + rbytes; + // logger.debug("Frame rbytes=" + rbytes + " willHaveReadByteCount=" + // + willHaveReadByteCount + " framePayloadLength=" + + // framePayloadLength); + if (willHaveReadByteCount == framePayloadLength) { + // We have all our content so proceed to process + payloadBuffer = buffer.readBytes(rbytes); + } else if (willHaveReadByteCount < framePayloadLength) { + // We don't have all our content so accumulate payload. + // Returning null means we will get called back + payloadBuffer = buffer.readBytes(rbytes); + if (framePayload == null) { + framePayload = channel.getConfig().getBufferFactory().getBuffer(toFrameLength(framePayloadLength)); + } + framePayload.writeBytes(payloadBuffer); + framePayloadBytesRead = framePayloadBytesRead + rbytes; - // Now we have all the data, the next checkpoint must be the next - // frame - checkpoint(State.FRAME_START); + // Return null to wait for more bytes to arrive + return null; + } else if (willHaveReadByteCount > framePayloadLength) { + // We have more than what we need so read up to the end of frame + // Leave the remainder in the buffer for next frame + payloadBuffer = buffer.readBytes(toFrameLength(framePayloadLength - framePayloadBytesRead)); + } - // Take the data that we have in this packet - if (framePayload == null) { - framePayload = payloadBuffer; - } else { - framePayload.writeBytes(payloadBuffer); - } + // Now we have all the data, the next checkpoint must be the next + // frame + checkpoint(State.FRAME_START); - // Unmask data if needed - if (this.maskedPayload) { - unmask(framePayload); - } + // Take the data that we have in this packet + if (framePayload == null) { + framePayload = payloadBuffer; + } else { + framePayload.writeBytes(payloadBuffer); + } - // Processing ping/pong/close frames because they cannot be fragmented - if (frameOpcode == OPCODE_PING) { - return new PingWebSocketFrame(frameFinalFlag, frameRsv, framePayload); - } else if (frameOpcode == OPCODE_PONG) { - return new PongWebSocketFrame(frameFinalFlag, frameRsv, framePayload); - } else if (frameOpcode == OPCODE_CLOSE) { - this.receivedClosingHandshake = true; - return new CloseWebSocketFrame(frameFinalFlag, frameRsv); - } - - // Processing for possible fragmented messages for text and binary frames - String aggregatedText = null; - if (frameFinalFlag) { - // Final frame of the sequence. Apparently ping frames are - // allowed in the middle of a fragmented message - if (frameOpcode != OPCODE_PING) { - fragmentedFramesCount = 0; + // Unmask data if needed + if (this.maskedPayload) { + unmask(framePayload); + } - // Check text for UTF8 correctness - if (frameOpcode == OPCODE_TEXT || fragmentedFramesText != null) { - // Check UTF-8 correctness for this payload - checkUTF8String(channel, framePayload.array()); + // Processing ping/pong/close frames because they cannot be + // fragmented + if (frameOpcode == OPCODE_PING) { + return new PingWebSocketFrame(frameFinalFlag, frameRsv, framePayload); + } else if (frameOpcode == OPCODE_PONG) { + return new PongWebSocketFrame(frameFinalFlag, frameRsv, framePayload); + } else if (frameOpcode == OPCODE_CLOSE) { + this.receivedClosingHandshake = true; + return new CloseWebSocketFrame(frameFinalFlag, frameRsv); + } - // This does a second check to make sure UTF-8 - // correctness for entire text message - aggregatedText = fragmentedFramesText.toString(); + // Processing for possible fragmented messages for text and binary + // frames + String aggregatedText = null; + if (frameFinalFlag) { + // Final frame of the sequence. Apparently ping frames are + // allowed in the middle of a fragmented message + if (frameOpcode != OPCODE_PING) { + fragmentedFramesCount = 0; - fragmentedFramesText = null; - } - } - } else { - // Not final frame so we can expect more frames in the - // fragmented sequence - if (fragmentedFramesCount == 0) { - // First text or binary frame for a fragmented set - fragmentedFramesText = null; - if (frameOpcode == OPCODE_TEXT) { - checkUTF8String(channel, framePayload.array()); - } - } else { - // Subsequent frames - only check if init frame is text - if (fragmentedFramesText != null) { - checkUTF8String(channel, framePayload.array()); - } - } + // Check text for UTF8 correctness + if (frameOpcode == OPCODE_TEXT || fragmentedFramesText != null) { + // Check UTF-8 correctness for this payload + checkUTF8String(channel, framePayload.array()); - // Increment counter - fragmentedFramesCount++; - } + // This does a second check to make sure UTF-8 + // correctness for entire text message + aggregatedText = fragmentedFramesText.toString(); - // Return the frame - if (frameOpcode == OPCODE_TEXT) { - return new TextWebSocketFrame(frameFinalFlag, frameRsv, framePayload); - } else if (frameOpcode == OPCODE_BINARY) { - return new BinaryWebSocketFrame(frameFinalFlag, frameRsv, framePayload); - } else if (frameOpcode == OPCODE_CONT) { - return new ContinuationWebSocketFrame(frameFinalFlag, frameRsv, framePayload, aggregatedText); - } else { - throw new UnsupportedOperationException("Cannot decode web socket frame with opcode: " + frameOpcode); - } - case CORRUPT: - // If we don't keep reading Netty will throw an exception saying - // we can't return null if no bytes read and state not changed. - buffer.readByte(); - return null; - default: - throw new Error("Shouldn't reach here."); - } - } + fragmentedFramesText = null; + } + } + } else { + // Not final frame so we can expect more frames in the + // fragmented sequence + if (fragmentedFramesCount == 0) { + // First text or binary frame for a fragmented set + fragmentedFramesText = null; + if (frameOpcode == OPCODE_TEXT) { + checkUTF8String(channel, framePayload.array()); + } + } else { + // Subsequent frames - only check if init frame is text + if (fragmentedFramesText != null) { + checkUTF8String(channel, framePayload.array()); + } + } - private void unmask(ChannelBuffer frame) { - byte[] bytes = frame.array(); - for (int i = 0; i < bytes.length; i++) { - frame.setByte(i, frame.getByte(i) ^ maskingKey.getByte(i % 4)); - } - } + // Increment counter + fragmentedFramesCount++; + } - private void protocolViolation(Channel channel, String reason) throws CorruptedFrameException { - checkpoint(State.CORRUPT); - if (channel.isConnected()) { - channel.write(ChannelBuffers.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); - channel.close().awaitUninterruptibly(); - } - throw new CorruptedFrameException(reason); - } + // Return the frame + if (frameOpcode == OPCODE_TEXT) { + return new TextWebSocketFrame(frameFinalFlag, frameRsv, framePayload); + } else if (frameOpcode == OPCODE_BINARY) { + return new BinaryWebSocketFrame(frameFinalFlag, frameRsv, framePayload); + } else if (frameOpcode == OPCODE_CONT) { + return new ContinuationWebSocketFrame(frameFinalFlag, frameRsv, framePayload, aggregatedText); + } else { + throw new UnsupportedOperationException("Cannot decode web socket frame with opcode: " + frameOpcode); + } + case CORRUPT: + // If we don't keep reading Netty will throw an exception saying + // we can't return null if no bytes read and state not changed. + buffer.readByte(); + return null; + default: + throw new Error("Shouldn't reach here."); + } + } - private int toFrameLength(long l) throws TooLongFrameException { - if (l > Integer.MAX_VALUE) { - throw new TooLongFrameException("Length:" + l); - } else { - return (int) l; - } - } + private void unmask(ChannelBuffer frame) { + byte[] bytes = frame.array(); + for (int i = 0; i < bytes.length; i++) { + frame.setByte(i, frame.getByte(i) ^ maskingKey.getByte(i % 4)); + } + } - private void checkUTF8String(Channel channel, byte[] bytes) throws CorruptedFrameException { - try { - // StringBuilder sb = new StringBuilder("UTF8 " + bytes.length + - // " bytes: "); - // for (byte b : bytes) { - // sb.append(Integer.toHexString(b)).append(" "); - // } - // logger.debug(sb.toString()); + private void protocolViolation(Channel channel, String reason) throws CorruptedFrameException { + checkpoint(State.CORRUPT); + if (channel.isConnected()) { + channel.write(ChannelBuffers.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); + channel.close().awaitUninterruptibly(); + } + throw new CorruptedFrameException(reason); + } - if (fragmentedFramesText == null) { - fragmentedFramesText = new UTF8Output(bytes); - } else { - fragmentedFramesText.write(bytes); - } - } catch (UTF8Exception ex) { - protocolViolation(channel, "invalid UTF-8 bytes"); - } - } + private int toFrameLength(long l) throws TooLongFrameException { + if (l > Integer.MAX_VALUE) { + throw new TooLongFrameException("Length:" + l); + } else { + return (int) l; + } + } + + private void checkUTF8String(Channel channel, byte[] bytes) throws CorruptedFrameException { + try { + // StringBuilder sb = new StringBuilder("UTF8 " + bytes.length + + // " bytes: "); + // for (byte b : bytes) { + // sb.append(Integer.toHexString(b)).append(" "); + // } + // logger.debug(sb.toString()); + + if (fragmentedFramesText == null) { + fragmentedFramesText = new UTF8Output(bytes); + } else { + fragmentedFramesText.write(bytes); + } + } catch (UTF8Exception ex) { + protocolViolation(channel, "invalid UTF-8 bytes"); + } + } } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket08FrameEncoder.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket08FrameEncoder.java index 35538e3ddf..9270a2f7c7 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket08FrameEncoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket08FrameEncoder.java @@ -114,8 +114,10 @@ public class WebSocket08FrameEncoder extends OneToOneEncoder { int length = data.readableBytes(); - logger.debug("Encoding WebSocket Frame opCode=" + opcode + " length=" + length); - + if (logger.isDebugEnabled()) { + logger.debug("Encoding WebSocket Frame opCode=" + opcode + " length=" + length); + } + int b0 = 0; if (frame.isFinalFragment()) { b0 |= (1 << 7); diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket13FrameDecoder.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket13FrameDecoder.java new file mode 100644 index 0000000000..5cf746952a --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket13FrameDecoder.java @@ -0,0 +1,61 @@ +// (BSD License: http://www.opensource.org/licenses/bsd-license) +// +// Copyright (c) 2011, Joe Walnes and contributors +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or +// without modification, are permitted provided that the +// following conditions are met: +// +// * Redistributions of source code must retain the above +// copyright notice, this list of conditions and the +// following disclaimer. +// +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// * Neither the name of the Webbit nor the names of +// its contributors may be used to endorse or promote products +// derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package org.jboss.netty.handler.codec.http.websocketx; + +/** + * Decodes a web socket frame from wire protocol version 13 format. + * V13 is essentially the same as V8. + * + * @author Vibul Imtarnasan + */ +public class WebSocket13FrameDecoder extends WebSocket08FrameDecoder { + + /** + * 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 + */ + public WebSocket13FrameDecoder(boolean maskedPayload, boolean allowExtensions) { + super(maskedPayload, allowExtensions); + } +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket13FrameEncoder.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket13FrameEncoder.java new file mode 100644 index 0000000000..fb63448a77 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocket13FrameEncoder.java @@ -0,0 +1,62 @@ +// (BSD License: http://www.opensource.org/licenses/bsd-license) +// +// Copyright (c) 2011, Joe Walnes and contributors +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or +// without modification, are permitted provided that the +// following conditions are met: +// +// * Redistributions of source code must retain the above +// copyright notice, this list of conditions and the +// following disclaimer. +// +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// * Neither the name of the Webbit nor the names of +// its contributors may be used to endorse or promote products +// derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +package org.jboss.netty.handler.codec.http.websocketx; + + +/** + *

+ * Encodes a web socket frame into wire protocol version 13 format. V13 is essentially the same + * as V8. + *

+ * + * @author Vibul Imtarnasan + */ +public class WebSocket13FrameEncoder extends WebSocket08FrameEncoder { + + /** + * Constructor + * + * @param maskPayload + * Web socket clients must set this to true to mask payload. + * Server implementations must set this to false. + */ + public WebSocket13FrameEncoder(boolean maskPayload) { + super(maskPayload); + } +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker17.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker17.java new file mode 100644 index 0000000000..8d7b2caf37 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker17.java @@ -0,0 +1,186 @@ +/* + * Copyright 2010 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.handler.codec.http.websocketx; + +import java.net.URI; + +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.handler.codec.http.DefaultHttpRequest; +import org.jboss.netty.handler.codec.http.HttpHeaders.Names; +import org.jboss.netty.handler.codec.http.HttpHeaders.Values; +import org.jboss.netty.handler.codec.http.HttpMethod; +import org.jboss.netty.handler.codec.http.HttpRequest; +import org.jboss.netty.handler.codec.http.HttpResponse; +import org.jboss.netty.handler.codec.http.HttpResponseStatus; +import org.jboss.netty.handler.codec.http.HttpVersion; +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; +import org.jboss.netty.util.CharsetUtil; + +/** + *

+ * Performs client side opening and closing handshakes for web socket + * specification version draft-ietf-hybi-thewebsocketprotocol- 17 + *

+ * + * @author The Netty Project + */ +public class WebSocketClientHandshaker17 extends WebSocketClientHandshaker { + + private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker17.class); + + public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + private String expectedChallengeResponseString = null; + + private static final String protocol = null; + + private boolean allowExtensions = false; + + /** + * 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 allowExtensions + * Allow extensions to be used in the reserved bits of the web + * socket frame + */ + public WebSocketClientHandshaker17(URI webSocketURL, WebSocketSpecificationVersion version, String subProtocol, boolean allowExtensions) { + super(webSocketURL, version, subProtocol); + this.allowExtensions = allowExtensions; + } + + /** + * /** + *

+ * Sends the opening request to the server: + *

+ * + *
+     * GET /chat HTTP/1.1
+     * Host: server.example.com
+     * Upgrade: websocket
+     * Connection: Upgrade
+     * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+     * Sec-WebSocket-Origin: http://example.com
+     * Sec-WebSocket-Protocol: chat, superchat
+     * Sec-WebSocket-Version: 13
+     * 
+ * + * @param ctx + * Channel context + * @param channel + * Channel into which we can write our request + */ + @Override + public void beginOpeningHandshake(ChannelHandlerContext ctx, Channel channel) { + // Get path + URI wsURL = this.getWebSocketURL(); + String path = wsURL.getPath(); + if (wsURL.getQuery() != null && wsURL.getQuery().length() > 0) { + path = wsURL.getPath() + "?" + wsURL.getQuery(); + } + + // Get 16 bit nonce and base 64 encode it + byte[] nonce = createRandomBytes(16); + String key = base64Encode(nonce); + + String acceptSeed = key + MAGIC_GUID; + byte[] sha1 = sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); + this.expectedChallengeResponseString = base64Encode(sha1); + + if (logger.isDebugEnabled()) { + logger.debug(String.format("HyBi17 Client Handshake key: %s. Expected response: %s.", key, this.expectedChallengeResponseString)); + } + + // Format request + HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); + request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase()); + request.addHeader(Names.CONNECTION, Values.UPGRADE); + request.addHeader(Names.SEC_WEBSOCKET_KEY, key); + request.addHeader(Names.HOST, wsURL.getHost()); + request.addHeader(Names.ORIGIN, "http://" + wsURL.getHost()); + if (protocol != null && !protocol.equals("")) { + request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, protocol); + } + request.addHeader(Names.SEC_WEBSOCKET_VERSION, "13"); + + channel.write(request); + + ctx.getPipeline().replace("encoder", "ws-encoder", new WebSocket13FrameEncoder(true)); + } + + /** + *

+ * Process server response: + *

+ * + *
+     * HTTP/1.1 101 Switching Protocols
+     * Upgrade: websocket
+     * Connection: Upgrade
+     * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
+     * Sec-WebSocket-Protocol: chat
+     * 
+ * + * @param ctx + * Channel context + * @param response + * HTTP response returned from the server for the request sent by + * beginOpeningHandshake00(). + * @throws WebSocketHandshakeException + */ + @Override + public void endOpeningHandshake(ChannelHandlerContext ctx, HttpResponse response) throws WebSocketHandshakeException { + final HttpResponseStatus status = new HttpResponseStatus(101, "Switching Protocols"); + + if (!response.getStatus().equals(status)) { + throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus()); + } + + String upgrade = response.getHeader(Names.UPGRADE); + if (upgrade == null || !upgrade.equals(Values.WEBSOCKET.toLowerCase())) { + throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + response.getHeader(Names.UPGRADE)); + } + + String connection = response.getHeader(Names.CONNECTION); + if (connection == null || !connection.equals(Values.UPGRADE)) { + throw new WebSocketHandshakeException("Invalid handshake response connection: " + response.getHeader(Names.CONNECTION)); + } + + String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT); + if (accept == null || !accept.equals(this.expectedChallengeResponseString)) { + throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept, this.expectedChallengeResponseString)); + } + + ctx.getPipeline().replace("decoder", "ws-decoder", new WebSocket13FrameDecoder(false, this.allowExtensions)); + + this.setOpenningHandshakeCompleted(true); + } + +} 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 a8c13e4d5f..c908b13f6e 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 @@ -43,6 +43,9 @@ public class WebSocketClientHandshakerFactory { * @throws WebSocketHandshakeException */ public WebSocketClientHandshaker newHandshaker(URI webSocketURL, WebSocketSpecificationVersion version, String subProtocol, boolean allowExtensions) throws WebSocketHandshakeException { + if (version == WebSocketSpecificationVersion.V17) { + return new WebSocketClientHandshaker17(webSocketURL, version, subProtocol, allowExtensions); + } if (version == WebSocketSpecificationVersion.V10) { return new WebSocketClientHandshaker10(webSocketURL, version, subProtocol, allowExtensions); } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker17.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker17.java new file mode 100644 index 0000000000..8a9ac4aeb5 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker17.java @@ -0,0 +1,168 @@ +/* + * Copyright 2010 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.handler.codec.http.websocketx; + +import static org.jboss.netty.handler.codec.http.HttpHeaders.Values.WEBSOCKET; +import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1; + +import java.security.NoSuchAlgorithmException; + +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelFutureListener; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.handler.codec.http.DefaultHttpResponse; +import org.jboss.netty.handler.codec.http.HttpRequest; +import org.jboss.netty.handler.codec.http.HttpResponse; +import org.jboss.netty.handler.codec.http.HttpResponseStatus; +import org.jboss.netty.handler.codec.http.HttpHeaders.Names; +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; +import org.jboss.netty.util.CharsetUtil; + +/** + *

+ * Performs server side opening and closing handshakes for web socket + * specification version draft-ietf-hybi-thewebsocketprotocol- 17 + *

+ * + * @author The Netty Project + */ +public class WebSocketServerHandshaker17 extends WebSocketServerHandshaker { + + private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketServerHandshaker17.class); + + public static final String WEBSOCKET_17_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + private boolean allowExtensions = 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 + */ + public WebSocketServerHandshaker17(String webSocketURL, String subProtocols, boolean allowExtensions) { + super(webSocketURL, subProtocols); + this.allowExtensions = allowExtensions; + } + + /** + *

+ * Handle the web socket handshake for the web socket specification HyBi + * versions 13-17. Versions 13-17 share the same wire protocol. + *

+ * + *

+ * Browser request to the server: + *

+ * + *
+     * GET /chat HTTP/1.1
+     * Host: server.example.com
+     * Upgrade: websocket
+     * Connection: Upgrade
+     * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+     * Sec-WebSocket-Origin: http://example.com
+     * Sec-WebSocket-Protocol: chat, superchat
+     * Sec-WebSocket-Version: 13
+     * 
+ * + *

+ * Server response: + *

+ * + *
+     * HTTP/1.1 101 Switching Protocols
+     * Upgrade: websocket
+     * Connection: Upgrade
+     * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
+     * Sec-WebSocket-Protocol: chat
+     * 
+ * + * @param ctx + * Channel context + * @param req + * HTTP request + * @throws NoSuchAlgorithmException + */ + @Override + public void executeOpeningHandshake(ChannelHandlerContext ctx, HttpRequest req) { + + if (logger.isDebugEnabled()) { + logger.debug(String.format("Channel %s web socket spec version 17 handshake", ctx.getChannel().getId())); + } + + HttpResponse res = new DefaultHttpResponse(HTTP_1_1, new HttpResponseStatus(101, "Switching Protocols")); + this.setVersion(WebSocketSpecificationVersion.V17); + + String key = req.getHeader(Names.SEC_WEBSOCKET_KEY); + if (key == null) { + res.setStatus(HttpResponseStatus.BAD_REQUEST); + return; + } + String acceptSeed = key + WEBSOCKET_17_ACCEPT_GUID; + byte[] sha1 = sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); + String accept = base64Encode(sha1); + + if (logger.isDebugEnabled()) { + logger.debug(String.format("HyBi17 Server Handshake key: %s. Response: %s.", key, accept)); + } + + res.setStatus(new HttpResponseStatus(101, "Switching Protocols")); + res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase()); + res.addHeader(Names.CONNECTION, Names.UPGRADE); + res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept); + String protocol = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); + if (protocol != null) { + res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, this.selectSubProtocol(protocol)); + } + + ctx.getChannel().write(res); + + // Upgrade the connection and send the handshake response. + ChannelPipeline p = ctx.getChannel().getPipeline(); + p.remove("aggregator"); + p.replace("decoder", "wsdecoder", new WebSocket13FrameDecoder(true, this.allowExtensions)); + p.replace("encoder", "wsencoder", new WebSocket13FrameEncoder(false)); + + } + + /** + * Echo back the closing frame and close the connection + * + * @param ctx + * Channel context + * @param frame + * Web Socket frame that was received + */ + @Override + public void executeClosingHandshake(ChannelHandlerContext ctx, CloseWebSocketFrame frame) { + ChannelFuture f = ctx.getChannel().write(frame); + f.addListener(ChannelFutureListener.CLOSE); + } + +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java index 5c33b5989c..f1306b572e 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java @@ -30,66 +30,71 @@ import org.jboss.netty.handler.codec.http.HttpHeaders.Names; */ public class WebSocketServerHandshakerFactory { - private final String webSocketURL; + private final String webSocketURL; - private final String subProtocols; + private final String subProtocols; - private boolean allowExtensions = false; + private boolean allowExtensions = 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 - */ - public WebSocketServerHandshakerFactory(String webSocketURL, String subProtocols, boolean allowExtensions) { - this.webSocketURL = webSocketURL; - this.subProtocols = subProtocols; - this.allowExtensions = allowExtensions; - } + /** + * 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 + */ + public WebSocketServerHandshakerFactory(String webSocketURL, String subProtocols, boolean allowExtensions) { + this.webSocketURL = webSocketURL; + this.subProtocols = subProtocols; + this.allowExtensions = allowExtensions; + } - /** - * Instances a new handshaker - * - * @return A new WebSocketServerHandshaker for the requested web socket - * version. Null if web socket version is not supported. - */ - public WebSocketServerHandshaker newHandshaker(ChannelHandlerContext ctx, HttpRequest req) { + /** + * Instances a new handshaker + * + * @return A new WebSocketServerHandshaker for the requested web socket + * version. Null if web socket version is not supported. + */ + public WebSocketServerHandshaker newHandshaker(ChannelHandlerContext ctx, HttpRequest req) { - String version = req.getHeader(Names.SEC_WEBSOCKET_VERSION); - if (version != null) { - if (version.equals("8")) { - // Version 8 of the wire protocol - assume version 10 of the - // specification. - return new WebSocketServerHandshaker10(webSocketURL, subProtocols, this.allowExtensions); - } else { - return null; - } - } else { - // Assume version 00 where version header was not specified - return new WebSocketServerHandshaker00(webSocketURL, subProtocols); - } - } + String version = req.getHeader(Names.SEC_WEBSOCKET_VERSION); + if (version != null) { + if (version.equals("13")) { + // Version 13 of the wire protocol - assume version 17 of the + // specification. + return new WebSocketServerHandshaker17(webSocketURL, subProtocols, this.allowExtensions); + } else if (version.equals("8")) { + // Version 8 of the wire protocol - assume version 10 of the + // specification. + return new WebSocketServerHandshaker10(webSocketURL, subProtocols, this.allowExtensions); + } else { + return null; + } + } else { + // Assume version 00 where version header was not specified + return new WebSocketServerHandshaker00(webSocketURL, subProtocols); + } + } - /** - * Return that we need cannot not support the web socket version - * - * @param ctx - * Context - */ - public void sendUnsupportedWebSocketVersionResponse(ChannelHandlerContext ctx) { - HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, new HttpResponseStatus(101, "Switching Protocols")); - res.setStatus(HttpResponseStatus.UPGRADE_REQUIRED); - res.setHeader(Names.SEC_WEBSOCKET_VERSION, "8"); - ctx.getChannel().write(res); - } + /** + * Return that we need cannot not support the web socket version + * + * @param ctx + * Context + */ + public void sendUnsupportedWebSocketVersionResponse(ChannelHandlerContext ctx) { + HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, new HttpResponseStatus(101, + "Switching Protocols")); + res.setStatus(HttpResponseStatus.UPGRADE_REQUIRED); + res.setHeader(Names.SEC_WEBSOCKET_VERSION, "13"); + ctx.getChannel().write(res); + } } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketSpecificationVersion.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketSpecificationVersion.java index 8103296b4d..58a160a998 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketSpecificationVersion.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketSpecificationVersion.java @@ -41,5 +41,13 @@ public enum WebSocketSpecificationVersion { * "http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10" * >draft-ietf-hybi-thewebsocketprotocol- 10 */ - V10 + V10, + + /** + * draft-ietf-hybi-thewebsocketprotocol- 17 + */ + V17 + } From 181355665be56f8c52e871ff0561de770abaa9e8 Mon Sep 17 00:00:00 2001 From: Veebs Date: Tue, 29 Nov 2011 10:12:42 +1100 Subject: [PATCH 3/4] Fixed formatting. --- .../websocketx/WebSocket08FrameDecoder.java | 560 +++++++++--------- 1 file changed, 280 insertions(+), 280 deletions(-) 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 4a346f1a25..76d0988b8d 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 @@ -59,325 +59,325 @@ import org.jboss.netty.logging.InternalLoggerFactory; */ public class WebSocket08FrameDecoder extends ReplayingDecoder { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocket08FrameDecoder.class); + private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocket08FrameDecoder.class); - private static final byte OPCODE_CONT = 0x0; - private static final byte OPCODE_TEXT = 0x1; - private static final byte OPCODE_BINARY = 0x2; - private static final byte OPCODE_CLOSE = 0x8; - private static final byte OPCODE_PING = 0x9; - private static final byte OPCODE_PONG = 0xA; + private static final byte OPCODE_CONT = 0x0; + private static final byte OPCODE_TEXT = 0x1; + private static final byte OPCODE_BINARY = 0x2; + private static final byte OPCODE_CLOSE = 0x8; + private static final byte OPCODE_PING = 0x9; + private static final byte OPCODE_PONG = 0xA; - private UTF8Output fragmentedFramesText = null; - private int fragmentedFramesCount = 0; + private UTF8Output fragmentedFramesText = null; + private int fragmentedFramesCount = 0; - private boolean frameFinalFlag; - private int frameRsv; - private int frameOpcode; - private long framePayloadLength; - private ChannelBuffer framePayload = null; - private int framePayloadBytesRead = 0; - private ChannelBuffer maskingKey; + private boolean frameFinalFlag; + private int frameRsv; + private int frameOpcode; + private long framePayloadLength; + private ChannelBuffer framePayload = null; + private int framePayloadBytesRead = 0; + private ChannelBuffer maskingKey; - private boolean allowExtensions = false; - private boolean maskedPayload = false; - private boolean receivedClosingHandshake = false; + private boolean allowExtensions = false; + private boolean maskedPayload = false; + private boolean receivedClosingHandshake = false; - public enum State { - FRAME_START, MASKING_KEY, PAYLOAD, CORRUPT - } + public enum State { + FRAME_START, MASKING_KEY, PAYLOAD, CORRUPT + } - /** - * 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 - */ - public WebSocket08FrameDecoder(boolean maskedPayload, boolean allowExtensions) { - super(State.FRAME_START); - this.maskedPayload = maskedPayload; - this.allowExtensions = allowExtensions; - } + /** + * 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 + */ + public WebSocket08FrameDecoder(boolean maskedPayload, boolean allowExtensions) { + super(State.FRAME_START); + this.maskedPayload = maskedPayload; + this.allowExtensions = allowExtensions; + } - @Override - protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, State state) - throws Exception { + @Override + protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, State state) + throws Exception { - // Discard all data received if closing handshake was received before. - if (receivedClosingHandshake) { - buffer.skipBytes(actualReadableBytes()); - return null; - } + // Discard all data received if closing handshake was received before. + if (receivedClosingHandshake) { + buffer.skipBytes(actualReadableBytes()); + return null; + } - switch (state) { - case FRAME_START: - framePayloadBytesRead = 0; - framePayloadLength = -1; - framePayload = null; + switch (state) { + case FRAME_START: + framePayloadBytesRead = 0; + framePayloadLength = -1; + framePayload = null; - // FIN, RSV, OPCODE - byte b = buffer.readByte(); - frameFinalFlag = (b & 0x80) != 0; - frameRsv = (b & 0x70) >> 4; - frameOpcode = (b & 0x0F); + // FIN, RSV, OPCODE + byte b = buffer.readByte(); + frameFinalFlag = (b & 0x80) != 0; + frameRsv = (b & 0x70) >> 4; + frameOpcode = (b & 0x0F); - if (logger.isDebugEnabled()) { - logger.debug("Decoding WebSocket Frame opCode=" + frameOpcode); - } + if (logger.isDebugEnabled()) { + logger.debug("Decoding WebSocket Frame opCode=" + frameOpcode); + } - // MASK, PAYLOAD LEN 1 - b = buffer.readByte(); - boolean frameMasked = (b & 0x80) != 0; - int framePayloadLen1 = (b & 0x7F); + // MASK, PAYLOAD LEN 1 + b = buffer.readByte(); + boolean frameMasked = (b & 0x80) != 0; + int framePayloadLen1 = (b & 0x7F); - if (frameRsv != 0 && !this.allowExtensions) { - protocolViolation(channel, "RSV != 0 and no extension negotiated, RSV:" + frameRsv); - return null; - } + if (frameRsv != 0 && !this.allowExtensions) { + protocolViolation(channel, "RSV != 0 and no extension negotiated, RSV:" + frameRsv); + return null; + } - if (this.maskedPayload && !frameMasked) { - protocolViolation(channel, "unmasked client to server frame"); - return null; - } - if (frameOpcode > 7) { // control frame (have MSB in opcode set) + if (this.maskedPayload && !frameMasked) { + protocolViolation(channel, "unmasked client to server frame"); + return null; + } + if (frameOpcode > 7) { // control frame (have MSB in opcode set) - // control frames MUST NOT be fragmented - if (!frameFinalFlag) { - protocolViolation(channel, "fragmented control frame"); - return null; - } + // control frames MUST NOT be fragmented + if (!frameFinalFlag) { + protocolViolation(channel, "fragmented control frame"); + return null; + } - // control frames MUST have payload 125 octets or less - if (framePayloadLen1 > 125) { - protocolViolation(channel, "control frame with payload length > 125 octets"); - return null; - } + // control frames MUST have payload 125 octets or less + if (framePayloadLen1 > 125) { + protocolViolation(channel, "control frame with payload length > 125 octets"); + return null; + } - // check for reserved control frame opcodes - if (!(frameOpcode == OPCODE_CLOSE || frameOpcode == OPCODE_PING || frameOpcode == OPCODE_PONG)) { - protocolViolation(channel, "control frame using reserved opcode " + frameOpcode); - return null; - } + // check for reserved control frame opcodes + if (!(frameOpcode == OPCODE_CLOSE || frameOpcode == OPCODE_PING || frameOpcode == OPCODE_PONG)) { + protocolViolation(channel, "control frame using reserved opcode " + frameOpcode); + return null; + } - // close frame : if there is a body, the first two bytes of the - // body MUST be a 2-byte unsigned integer (in network byte - // order) representing a status code - if (frameOpcode == 8 && framePayloadLen1 == 1) { - protocolViolation(channel, "received close control frame with payload len 1"); - return null; - } - } else { // data frame - // check for reserved data frame opcodes - if (!(frameOpcode == OPCODE_CONT || frameOpcode == OPCODE_TEXT || frameOpcode == OPCODE_BINARY)) { - protocolViolation(channel, "data frame using reserved opcode " + frameOpcode); - return null; - } + // close frame : if there is a body, the first two bytes of the + // body MUST be a 2-byte unsigned integer (in network byte + // order) representing a status code + if (frameOpcode == 8 && framePayloadLen1 == 1) { + protocolViolation(channel, "received close control frame with payload len 1"); + return null; + } + } else { // data frame + // check for reserved data frame opcodes + if (!(frameOpcode == OPCODE_CONT || frameOpcode == OPCODE_TEXT || frameOpcode == OPCODE_BINARY)) { + protocolViolation(channel, "data frame using reserved opcode " + frameOpcode); + return null; + } - // check opcode vs message fragmentation state 1/2 - if (fragmentedFramesCount == 0 && frameOpcode == OPCODE_CONT) { - protocolViolation(channel, "received continuation data frame outside fragmented message"); - return null; - } + // check opcode vs message fragmentation state 1/2 + if (fragmentedFramesCount == 0 && frameOpcode == OPCODE_CONT) { + protocolViolation(channel, "received continuation data frame outside fragmented message"); + return null; + } - // check opcode vs message fragmentation state 2/2 - if (fragmentedFramesCount != 0 && frameOpcode != OPCODE_CONT && frameOpcode != OPCODE_PING) { - protocolViolation(channel, "received non-continuation data frame while inside fragmented message"); - return null; - } - } + // check opcode vs message fragmentation state 2/2 + if (fragmentedFramesCount != 0 && frameOpcode != OPCODE_CONT && frameOpcode != OPCODE_PING) { + protocolViolation(channel, "received non-continuation data frame while inside fragmented message"); + return null; + } + } - // Read frame payload length - if (framePayloadLen1 == 126) { - framePayloadLength = buffer.readUnsignedShort(); - if (framePayloadLength < 126) { - protocolViolation(channel, "invalid data frame length (not using minimal length encoding)"); - return null; - } - } else if (framePayloadLen1 == 127) { - framePayloadLength = buffer.readLong(); - // TODO: check if it's bigger than 0x7FFFFFFFFFFFFFFF, Maybe - // just check if it's negative? + // Read frame payload length + if (framePayloadLen1 == 126) { + framePayloadLength = buffer.readUnsignedShort(); + if (framePayloadLength < 126) { + protocolViolation(channel, "invalid data frame length (not using minimal length encoding)"); + return null; + } + } else if (framePayloadLen1 == 127) { + framePayloadLength = buffer.readLong(); + // TODO: check if it's bigger than 0x7FFFFFFFFFFFFFFF, Maybe + // just check if it's negative? - if (framePayloadLength < 65536) { - protocolViolation(channel, "invalid data frame length (not using minimal length encoding)"); - return null; - } - } else { - framePayloadLength = framePayloadLen1; - } + if (framePayloadLength < 65536) { + protocolViolation(channel, "invalid data frame length (not using minimal length encoding)"); + return null; + } + } else { + framePayloadLength = framePayloadLen1; + } - if (logger.isDebugEnabled()) { - logger.debug("Decoding WebSocket Frame length=" + framePayloadLength); - } + if (logger.isDebugEnabled()) { + logger.debug("Decoding WebSocket Frame length=" + framePayloadLength); + } - checkpoint(State.MASKING_KEY); - case MASKING_KEY: - if (this.maskedPayload) { - maskingKey = buffer.readBytes(4); - } - checkpoint(State.PAYLOAD); - case PAYLOAD: - // Sometimes, the payload may not be delivered in 1 nice packet - // We need to accumulate the data until we have it all - int rbytes = actualReadableBytes(); - ChannelBuffer payloadBuffer = null; + checkpoint(State.MASKING_KEY); + case MASKING_KEY: + if (this.maskedPayload) { + maskingKey = buffer.readBytes(4); + } + checkpoint(State.PAYLOAD); + case PAYLOAD: + // Sometimes, the payload may not be delivered in 1 nice packet + // We need to accumulate the data until we have it all + int rbytes = actualReadableBytes(); + ChannelBuffer payloadBuffer = null; - int willHaveReadByteCount = framePayloadBytesRead + rbytes; - // logger.debug("Frame rbytes=" + rbytes + " willHaveReadByteCount=" - // + willHaveReadByteCount + " framePayloadLength=" + - // framePayloadLength); - if (willHaveReadByteCount == framePayloadLength) { - // We have all our content so proceed to process - payloadBuffer = buffer.readBytes(rbytes); - } else if (willHaveReadByteCount < framePayloadLength) { - // We don't have all our content so accumulate payload. - // Returning null means we will get called back - payloadBuffer = buffer.readBytes(rbytes); - if (framePayload == null) { - framePayload = channel.getConfig().getBufferFactory().getBuffer(toFrameLength(framePayloadLength)); - } - framePayload.writeBytes(payloadBuffer); - framePayloadBytesRead = framePayloadBytesRead + rbytes; + int willHaveReadByteCount = framePayloadBytesRead + rbytes; + // logger.debug("Frame rbytes=" + rbytes + " willHaveReadByteCount=" + // + willHaveReadByteCount + " framePayloadLength=" + + // framePayloadLength); + if (willHaveReadByteCount == framePayloadLength) { + // We have all our content so proceed to process + payloadBuffer = buffer.readBytes(rbytes); + } else if (willHaveReadByteCount < framePayloadLength) { + // We don't have all our content so accumulate payload. + // Returning null means we will get called back + payloadBuffer = buffer.readBytes(rbytes); + if (framePayload == null) { + framePayload = channel.getConfig().getBufferFactory().getBuffer(toFrameLength(framePayloadLength)); + } + framePayload.writeBytes(payloadBuffer); + framePayloadBytesRead = framePayloadBytesRead + rbytes; - // Return null to wait for more bytes to arrive - return null; - } else if (willHaveReadByteCount > framePayloadLength) { - // We have more than what we need so read up to the end of frame - // Leave the remainder in the buffer for next frame - payloadBuffer = buffer.readBytes(toFrameLength(framePayloadLength - framePayloadBytesRead)); - } + // Return null to wait for more bytes to arrive + return null; + } else if (willHaveReadByteCount > framePayloadLength) { + // We have more than what we need so read up to the end of frame + // Leave the remainder in the buffer for next frame + payloadBuffer = buffer.readBytes(toFrameLength(framePayloadLength - framePayloadBytesRead)); + } - // Now we have all the data, the next checkpoint must be the next - // frame - checkpoint(State.FRAME_START); + // Now we have all the data, the next checkpoint must be the next + // frame + checkpoint(State.FRAME_START); - // Take the data that we have in this packet - if (framePayload == null) { - framePayload = payloadBuffer; - } else { - framePayload.writeBytes(payloadBuffer); - } + // Take the data that we have in this packet + if (framePayload == null) { + framePayload = payloadBuffer; + } else { + framePayload.writeBytes(payloadBuffer); + } - // Unmask data if needed - if (this.maskedPayload) { - unmask(framePayload); - } + // Unmask data if needed + if (this.maskedPayload) { + unmask(framePayload); + } - // Processing ping/pong/close frames because they cannot be - // fragmented - if (frameOpcode == OPCODE_PING) { - return new PingWebSocketFrame(frameFinalFlag, frameRsv, framePayload); - } else if (frameOpcode == OPCODE_PONG) { - return new PongWebSocketFrame(frameFinalFlag, frameRsv, framePayload); - } else if (frameOpcode == OPCODE_CLOSE) { - this.receivedClosingHandshake = true; - return new CloseWebSocketFrame(frameFinalFlag, frameRsv); - } + // Processing ping/pong/close frames because they cannot be + // fragmented + if (frameOpcode == OPCODE_PING) { + return new PingWebSocketFrame(frameFinalFlag, frameRsv, framePayload); + } else if (frameOpcode == OPCODE_PONG) { + return new PongWebSocketFrame(frameFinalFlag, frameRsv, framePayload); + } else if (frameOpcode == OPCODE_CLOSE) { + this.receivedClosingHandshake = true; + return new CloseWebSocketFrame(frameFinalFlag, frameRsv); + } - // Processing for possible fragmented messages for text and binary - // frames - String aggregatedText = null; - if (frameFinalFlag) { - // Final frame of the sequence. Apparently ping frames are - // allowed in the middle of a fragmented message - if (frameOpcode != OPCODE_PING) { - fragmentedFramesCount = 0; + // Processing for possible fragmented messages for text and binary + // frames + String aggregatedText = null; + if (frameFinalFlag) { + // Final frame of the sequence. Apparently ping frames are + // allowed in the middle of a fragmented message + if (frameOpcode != OPCODE_PING) { + fragmentedFramesCount = 0; - // Check text for UTF8 correctness - if (frameOpcode == OPCODE_TEXT || fragmentedFramesText != null) { - // Check UTF-8 correctness for this payload - checkUTF8String(channel, framePayload.array()); + // Check text for UTF8 correctness + if (frameOpcode == OPCODE_TEXT || fragmentedFramesText != null) { + // Check UTF-8 correctness for this payload + checkUTF8String(channel, framePayload.array()); - // This does a second check to make sure UTF-8 - // correctness for entire text message - aggregatedText = fragmentedFramesText.toString(); + // This does a second check to make sure UTF-8 + // correctness for entire text message + aggregatedText = fragmentedFramesText.toString(); - fragmentedFramesText = null; - } - } - } else { - // Not final frame so we can expect more frames in the - // fragmented sequence - if (fragmentedFramesCount == 0) { - // First text or binary frame for a fragmented set - fragmentedFramesText = null; - if (frameOpcode == OPCODE_TEXT) { - checkUTF8String(channel, framePayload.array()); - } - } else { - // Subsequent frames - only check if init frame is text - if (fragmentedFramesText != null) { - checkUTF8String(channel, framePayload.array()); - } - } + fragmentedFramesText = null; + } + } + } else { + // Not final frame so we can expect more frames in the + // fragmented sequence + if (fragmentedFramesCount == 0) { + // First text or binary frame for a fragmented set + fragmentedFramesText = null; + if (frameOpcode == OPCODE_TEXT) { + checkUTF8String(channel, framePayload.array()); + } + } else { + // Subsequent frames - only check if init frame is text + if (fragmentedFramesText != null) { + checkUTF8String(channel, framePayload.array()); + } + } - // Increment counter - fragmentedFramesCount++; - } + // Increment counter + fragmentedFramesCount++; + } - // Return the frame - if (frameOpcode == OPCODE_TEXT) { - return new TextWebSocketFrame(frameFinalFlag, frameRsv, framePayload); - } else if (frameOpcode == OPCODE_BINARY) { - return new BinaryWebSocketFrame(frameFinalFlag, frameRsv, framePayload); - } else if (frameOpcode == OPCODE_CONT) { - return new ContinuationWebSocketFrame(frameFinalFlag, frameRsv, framePayload, aggregatedText); - } else { - throw new UnsupportedOperationException("Cannot decode web socket frame with opcode: " + frameOpcode); - } - case CORRUPT: - // If we don't keep reading Netty will throw an exception saying - // we can't return null if no bytes read and state not changed. - buffer.readByte(); - return null; - default: - throw new Error("Shouldn't reach here."); - } - } + // Return the frame + if (frameOpcode == OPCODE_TEXT) { + return new TextWebSocketFrame(frameFinalFlag, frameRsv, framePayload); + } else if (frameOpcode == OPCODE_BINARY) { + return new BinaryWebSocketFrame(frameFinalFlag, frameRsv, framePayload); + } else if (frameOpcode == OPCODE_CONT) { + return new ContinuationWebSocketFrame(frameFinalFlag, frameRsv, framePayload, aggregatedText); + } else { + throw new UnsupportedOperationException("Cannot decode web socket frame with opcode: " + frameOpcode); + } + case CORRUPT: + // If we don't keep reading Netty will throw an exception saying + // we can't return null if no bytes read and state not changed. + buffer.readByte(); + return null; + default: + throw new Error("Shouldn't reach here."); + } + } - private void unmask(ChannelBuffer frame) { - byte[] bytes = frame.array(); - for (int i = 0; i < bytes.length; i++) { - frame.setByte(i, frame.getByte(i) ^ maskingKey.getByte(i % 4)); - } - } + private void unmask(ChannelBuffer frame) { + byte[] bytes = frame.array(); + for (int i = 0; i < bytes.length; i++) { + frame.setByte(i, frame.getByte(i) ^ maskingKey.getByte(i % 4)); + } + } - private void protocolViolation(Channel channel, String reason) throws CorruptedFrameException { - checkpoint(State.CORRUPT); - if (channel.isConnected()) { - channel.write(ChannelBuffers.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); - channel.close().awaitUninterruptibly(); - } - throw new CorruptedFrameException(reason); - } + private void protocolViolation(Channel channel, String reason) throws CorruptedFrameException { + checkpoint(State.CORRUPT); + if (channel.isConnected()) { + channel.write(ChannelBuffers.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); + channel.close().awaitUninterruptibly(); + } + throw new CorruptedFrameException(reason); + } - private int toFrameLength(long l) throws TooLongFrameException { - if (l > Integer.MAX_VALUE) { - throw new TooLongFrameException("Length:" + l); - } else { - return (int) l; - } - } + private int toFrameLength(long l) throws TooLongFrameException { + if (l > Integer.MAX_VALUE) { + throw new TooLongFrameException("Length:" + l); + } else { + return (int) l; + } + } - private void checkUTF8String(Channel channel, byte[] bytes) throws CorruptedFrameException { - try { - // StringBuilder sb = new StringBuilder("UTF8 " + bytes.length + - // " bytes: "); - // for (byte b : bytes) { - // sb.append(Integer.toHexString(b)).append(" "); - // } - // logger.debug(sb.toString()); + private void checkUTF8String(Channel channel, byte[] bytes) throws CorruptedFrameException { + try { + // StringBuilder sb = new StringBuilder("UTF8 " + bytes.length + + // " bytes: "); + // for (byte b : bytes) { + // sb.append(Integer.toHexString(b)).append(" "); + // } + // logger.debug(sb.toString()); - if (fragmentedFramesText == null) { - fragmentedFramesText = new UTF8Output(bytes); - } else { - fragmentedFramesText.write(bytes); - } - } catch (UTF8Exception ex) { - protocolViolation(channel, "invalid UTF-8 bytes"); - } - } + if (fragmentedFramesText == null) { + fragmentedFramesText = new UTF8Output(bytes); + } else { + fragmentedFramesText.write(bytes); + } + } catch (UTF8Exception ex) { + protocolViolation(channel, "invalid UTF-8 bytes"); + } + } } From 6fbc168cbf0f682c722b728ef9d1d091506c909f Mon Sep 17 00:00:00 2001 From: Veebs Date: Tue, 29 Nov 2011 10:19:47 +1100 Subject: [PATCH 4/4] Update example websocket client to use V17 --- .../org/jboss/netty/example/http/websocketx/client/App.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/client/App.java b/src/main/java/org/jboss/netty/example/http/websocketx/client/App.java index 3d1c3ab8bf..a140705def 100644 --- a/src/main/java/org/jboss/netty/example/http/websocketx/client/App.java +++ b/src/main/java/org/jboss/netty/example/http/websocketx/client/App.java @@ -59,10 +59,10 @@ public class App { MyCallbackHandler callbackHandler = new MyCallbackHandler(); WebSocketClientFactory factory = new WebSocketClientFactory(); - // Connect with spec version 10 (try changing it to V00 and it will + // Connect with spec version 17 (try changing it to V10 or V00 and it will // still work ... fingers crossed ;-) WebSocketClient client = factory.newClient(new URI("ws://localhost:8080/websocket"), - WebSocketSpecificationVersion.V10, callbackHandler); + WebSocketSpecificationVersion.V17, callbackHandler); // Connect System.out.println("WebSocket Client connecting");