def setUp(self): self.channel = DummyChannel() request = Request(self.channel, False) transport = WebSocketTransport(request) handler = TestHandler(transport) transport._attachHandler(handler) self.decoder = WebSocketFrameDecoder(request, handler) self.decoder.MAX_LENGTH = 100
def finishHandshake(nonce): """ Receive nonce value from request body, and calculate repsonse. """ protocolHeaders = self.requestHeaders.getRawHeaders( "WebSocket-Protocol", []) if len(protocolHeaders) not in (0, 1): return finish() if protocolHeaders: if protocolHeaders[0] not in self.site.supportedProtocols: return finish() protocolHeader = protocolHeaders[0] else: protocolHeader = None originHeader = originHeaders[0] hostHeader = hostHeaders[0] self.startedWriting = True handshake = [ "HTTP/1.1 101 Web Socket Protocol Handshake", "Upgrade: WebSocket", "Connection: Upgrade" ] handshake.append("Sec-WebSocket-Origin: %s" % (originHeader)) if self.isSecure(): scheme = "wss" else: scheme = "ws" handshake.append("Sec-WebSocket-Location: %s://%s%s" % (scheme, hostHeader, self.uri)) if protocolHeader is not None: handshake.append("Sec-WebSocket-Protocol: %s" % protocolHeader) for header in handshake: self.write("%s\r\n" % header) self.write("\r\n") # concatenate num1 (32 bit in), num2 (32 bit int), nonce, and take md5 of result res = struct.pack('>II8s', num1, num2, nonce) server_response = md5(res).digest() self.write(server_response) # XXX we probably don't want to set _transferDecoder self.channel._transferDecoder = WebSocketFrameDecoder( self, handler) transport._connectionMade()
def renderWebSocket(self): """ Render a WebSocket request. If the request is not identified with a proper WebSocket handshake, the connection will be closed. Otherwise, the response to the handshake is sent and a C{WebSocketHandler} is created to handle the request. """ # check for post-75 handshake requests isSecHandshake = self.requestHeaders.getRawHeaders( "Sec-WebSocket-Key1", []) if isSecHandshake: self._clientHandshake76() else: check = self._checkClientHandshake() if check is None: return originHeader, hostHeader, protocolHeader, handler = check self.startedWriting = True handshake = [ "HTTP/1.1 101 Web Socket Protocol Handshake", "Upgrade: WebSocket", "Connection: Upgrade" ] handshake.append("WebSocket-Origin: %s" % (originHeader)) if self.isSecure(): scheme = "wss" else: scheme = "ws" handshake.append("WebSocket-Location: %s://%s%s" % (scheme, hostHeader, self.uri)) if protocolHeader is not None: handshake.append("WebSocket-Protocol: %s" % protocolHeader) for header in handshake: self.write("%s\r\n" % header) self.write("\r\n") self.channel.setRawMode() # XXX we probably don't want to set _transferDecoder self.channel._transferDecoder = WebSocketFrameDecoder( self, handler) handler.transport._connectionMade() return
class WebSocketFrameDecoderTestCase(TestCase): """ Test for C{WebSocketFrameDecoder}. """ def setUp(self): self.channel = DummyChannel() request = Request(self.channel, False) transport = WebSocketTransport(request) handler = TestHandler(transport) transport._attachHandler(handler) self.decoder = WebSocketFrameDecoder(request, handler) self.decoder.MAX_LENGTH = 100 def test_oneFrame(self): """ We can send one frame handled with one C{dataReceived} call. """ self.decoder.dataReceived("\x00frame\xff") self.assertEquals(self.decoder.handler.frames, ["frame"]) def test_oneFrameSplitted(self): """ A frame can be split into several C{dataReceived} calls, and will be combined again when sent to the C{WebSocketHandler}. """ self.decoder.dataReceived("\x00fra") self.decoder.dataReceived("me\xff") self.assertEquals(self.decoder.handler.frames, ["frame"]) def test_multipleFrames(self): """ Several frames can be received in a single C{dataReceived} call. """ self.decoder.dataReceived("\x00frame1\xff\x00frame2\xff") self.assertEquals(self.decoder.handler.frames, ["frame1", "frame2"]) def test_missingNull(self): """ If a frame not starting with C{\\x00} is received, the connection is dropped. """ self.decoder.dataReceived("frame\xff") self.assertTrue(self.channel.transport.disconnected) def test_missingNullAfterGoodFrame(self): """ If a frame not starting with C{\\x00} is received after a correct frame, the connection is dropped. """ self.decoder.dataReceived("\x00frame\xfffoo") self.assertTrue(self.channel.transport.disconnected) self.assertEquals(self.decoder.handler.frames, ["frame"]) def test_emptyReceive(self): """ Received an empty string doesn't do anything. """ self.decoder.dataReceived("") self.assertFalse(self.channel.transport.disconnected) def test_maxLength(self): """ If a frame is received which is bigger than C{MAX_LENGTH}, the connection is dropped. """ self.decoder.dataReceived("\x00" + "x" * 101) self.assertTrue(self.channel.transport.disconnected) def test_maxLengthFrameCompleted(self): """ If a too big frame is received in several fragments, the connection is dropped. """ self.decoder.dataReceived("\x00" + "x" * 90) self.decoder.dataReceived("x" * 11 + "\xff") self.assertTrue(self.channel.transport.disconnected) def test_frameLengthReset(self): """ The length of frames is reset between frame, thus not creating an error when the accumulated length exceeds the maximum frame length. """ for i in range(15): self.decoder.dataReceived("\x00" + "x" * 10 + "\xff") self.assertFalse(self.channel.transport.disconnected)
class WebSocketFrameDecoderTestCase(TestCase): """ Test for C{WebSocketFrameDecoder}. """ def setUp(self): self.channel = DummyChannel() request = Request(self.channel, False) transport = WebSocketTransport(request) handler = TestHandler(transport) transport._attachHandler(handler) self.decoder = WebSocketFrameDecoder(request, handler) self.decoder.MAX_LENGTH = 100 self.decoder.MAX_BINARY_LENGTH = 1000 def assertOneDecodingError(self): """ Assert that exactly one L{DecodingError} has been logged and return that error. """ errors = self.flushLoggedErrors(DecodingError) self.assertEquals(len(errors), 1) return errors[0] def test_oneFrame(self): """ We can send one frame handled with one C{dataReceived} call. """ self.decoder.dataReceived("\x00frame\xff") self.assertEquals(self.decoder.handler.frames, ["frame"]) def test_oneFrameSplitted(self): """ A frame can be split into several C{dataReceived} calls, and will be combined again when sent to the C{WebSocketHandler}. """ self.decoder.dataReceived("\x00fra") self.decoder.dataReceived("me\xff") self.assertEquals(self.decoder.handler.frames, ["frame"]) def test_multipleFrames(self): """ Several frames can be received in a single C{dataReceived} call. """ self.decoder.dataReceived("\x00frame1\xff\x00frame2\xff") self.assertEquals(self.decoder.handler.frames, ["frame1", "frame2"]) def test_missingNull(self): """ If a frame not starting with C{\\x00} is received, the connection is dropped. """ self.decoder.dataReceived("frame\xff") self.assertOneDecodingError() self.assertTrue(self.channel.transport.disconnected) def test_missingNullAfterGoodFrame(self): """ If a frame not starting with C{\\x00} is received after a correct frame, the connection is dropped. """ self.decoder.dataReceived("\x00frame\xfffoo") self.assertOneDecodingError() self.assertTrue(self.channel.transport.disconnected) self.assertEquals(self.decoder.handler.frames, ["frame"]) def test_emptyReceive(self): """ Received an empty string doesn't do anything. """ self.decoder.dataReceived("") self.assertFalse(self.channel.transport.disconnected) def test_maxLength(self): """ If a frame is received which is bigger than C{MAX_LENGTH}, the connection is dropped. """ self.decoder.dataReceived("\x00" + "x" * 101) self.assertTrue(self.channel.transport.disconnected) def test_maxLengthFrameCompleted(self): """ If a too big frame is received in several fragments, the connection is dropped. """ self.decoder.dataReceived("\x00" + "x" * 90) self.decoder.dataReceived("x" * 11 + "\xff") self.assertTrue(self.channel.transport.disconnected) def test_frameLengthReset(self): """ The length of frames is reset between frame, thus not creating an error when the accumulated length exceeds the maximum frame length. """ for i in range(15): self.decoder.dataReceived("\x00" + "x" * 10 + "\xff") self.assertFalse(self.channel.transport.disconnected) def test_oneBinaryFrame(self): """ A binary frame is parsed and ignored, the following text frame is delivered. """ self.decoder.dataReceived("\xff\x0abinarydata\x00text frame\xff") self.assertEquals(self.decoder.handler.frames, ["text frame"]) def test_multipleBinaryFrames(self): """ Text frames intermingled with binary frames are parsed correctly. """ tf1, tf2, tf3 = "\x00frame1\xff", "\x00frame2\xff", "\x00frame3\xff" bf1, bf2, bf3 = "\xff\x01X", "\xff\x1a" + "X" * 0x1a, "\xff\x02AB" self.decoder.dataReceived(tf1 + bf1 + bf2 + tf2 + tf3 + bf3) self.assertEquals(self.decoder.handler.frames, ["frame1", "frame2", "frame3"]) def test_binaryFrameMultipleLengthBytes(self): """ A binary frame can have its length field spread across multiple bytes. """ bf = "\xff\x81\x48" + "X" * 200 tf = "\x00frame\xff" self.decoder.dataReceived(bf + tf + bf) self.assertEquals(self.decoder.handler.frames, ["frame"]) def test_binaryAndTextSplitted(self): """ Intermingled binary and text frames can be split across several C{dataReceived} calls. """ tf1, tf2 = "\x00text\xff", "\x00other text\xff" bf1, bf2, bf3 = ("\xff\x01X", "\xff\x81\x48" + "X" * 200, "\xff\x20" + "X" * 32) chunks = [bf1[0], bf1[1:], tf1[:2], tf1[2:] + bf2[:2], bf2[2:-2], bf2[-2:-1], bf2[1] + tf2[:-1], tf2[-1], bf3] for c in chunks: self.decoder.dataReceived(c) self.assertEquals(self.decoder.handler.frames, ["text", "other text"]) self.assertFalse(self.channel.transport.disconnected) def text_maxBinaryLength(self): """ If a binary frame's declared length exceeds MAX_BINARY_LENGTH, the connection is dropped. """ self.decoder.dataReceived("\xff\xff\xff\xff\xff\xff") self.assertTrue(self.channel.transport.disconnected) def test_closingHandshake(self): """ After receiving the closing handshake, the server sends its own closing handshake and ignores all future data. """ self.decoder.dataReceived("\x00frame\xff\xff\x00random crap") self.decoder.dataReceived("more random crap, that's discarded") self.assertEquals(self.decoder.handler.frames, ["frame"]) self.assertTrue(self.decoder.closing) def test_invalidFrameType(self): """ Frame types other than 0x00 and 0xff cause the connection to be dropped. """ ok = "\x00ok\xff" wrong = "\x05foo\xff" self.decoder.dataReceived(ok + wrong + ok) self.assertEquals(self.decoder.handler.frames, ["ok"]) error = self.assertOneDecodingError() self.assertTrue(self.channel.transport.disconnected) def test_emptyFrame(self): """ An empty text frame is correctly parsed. """ self.decoder.dataReceived("\x00\xff") self.assertEquals(self.decoder.handler.frames, [""]) self.assertFalse(self.channel.transport.disconnected)
class WebSocketFrameDecoderTestCase(TestCase): """ Test for C{WebSocketFrameDecoder}. """ def setUp(self): self.channel = DummyChannel() request = Request(self.channel, False) transport = WebSocketTransport(request) handler = TestHandler(transport) transport._attachHandler(handler) self.decoder = WebSocketFrameDecoder(request, handler) self.decoder.MAX_LENGTH = 100 self.decoder.MAX_BINARY_LENGTH = 1000 def assertOneDecodingError(self): """ Assert that exactly one L{DecodingError} has been logged and return that error. """ errors = self.flushLoggedErrors(DecodingError) self.assertEquals(len(errors), 1) return errors[0] def test_oneFrame(self): """ We can send one frame handled with one C{dataReceived} call. """ self.decoder.dataReceived("\x00frame\xff") self.assertEquals(self.decoder.handler.frames, ["frame"]) def test_oneFrameSplitted(self): """ A frame can be split into several C{dataReceived} calls, and will be combined again when sent to the C{WebSocketHandler}. """ self.decoder.dataReceived("\x00fra") self.decoder.dataReceived("me\xff") self.assertEquals(self.decoder.handler.frames, ["frame"]) def test_multipleFrames(self): """ Several frames can be received in a single C{dataReceived} call. """ self.decoder.dataReceived("\x00frame1\xff\x00frame2\xff") self.assertEquals(self.decoder.handler.frames, ["frame1", "frame2"]) def test_missingNull(self): """ If a frame not starting with C{\\x00} is received, the connection is dropped. """ self.decoder.dataReceived("frame\xff") self.assertOneDecodingError() self.assertTrue(self.channel.transport.disconnected) def test_missingNullAfterGoodFrame(self): """ If a frame not starting with C{\\x00} is received after a correct frame, the connection is dropped. """ self.decoder.dataReceived("\x00frame\xfffoo") self.assertOneDecodingError() self.assertTrue(self.channel.transport.disconnected) self.assertEquals(self.decoder.handler.frames, ["frame"]) def test_emptyReceive(self): """ Received an empty string doesn't do anything. """ self.decoder.dataReceived("") self.assertFalse(self.channel.transport.disconnected) def test_maxLength(self): """ If a frame is received which is bigger than C{MAX_LENGTH}, the connection is dropped. """ self.decoder.dataReceived("\x00" + "x" * 101) self.assertTrue(self.channel.transport.disconnected) def test_maxLengthFrameCompleted(self): """ If a too big frame is received in several fragments, the connection is dropped. """ self.decoder.dataReceived("\x00" + "x" * 90) self.decoder.dataReceived("x" * 11 + "\xff") self.assertTrue(self.channel.transport.disconnected) def test_frameLengthReset(self): """ The length of frames is reset between frame, thus not creating an error when the accumulated length exceeds the maximum frame length. """ for i in range(15): self.decoder.dataReceived("\x00" + "x" * 10 + "\xff") self.assertFalse(self.channel.transport.disconnected) def test_oneBinaryFrame(self): """ A binary frame is parsed and ignored, the following text frame is delivered. """ self.decoder.dataReceived("\xff\x0abinarydata\x00text frame\xff") self.assertEquals(self.decoder.handler.frames, ["text frame"]) def test_multipleBinaryFrames(self): """ Text frames intermingled with binary frames are parsed correctly. """ tf1, tf2, tf3 = "\x00frame1\xff", "\x00frame2\xff", "\x00frame3\xff" bf1, bf2, bf3 = "\xff\x01X", "\xff\x1a" + "X" * 0x1a, "\xff\x02AB" self.decoder.dataReceived(tf1 + bf1 + bf2 + tf2 + tf3 + bf3) self.assertEquals(self.decoder.handler.frames, ["frame1", "frame2", "frame3"]) def test_binaryFrameMultipleLengthBytes(self): """ A binary frame can have its length field spread across multiple bytes. """ bf = "\xff\x81\x48" + "X" * 200 tf = "\x00frame\xff" self.decoder.dataReceived(bf + tf + bf) self.assertEquals(self.decoder.handler.frames, ["frame"]) def test_binaryAndTextSplitted(self): """ Intermingled binary and text frames can be split across several C{dataReceived} calls. """ tf1, tf2 = "\x00text\xff", "\x00other text\xff" bf1, bf2, bf3 = ("\xff\x01X", "\xff\x81\x48" + "X" * 200, "\xff\x20" + "X" * 32) chunks = [ bf1[0], bf1[1:], tf1[:2], tf1[2:] + bf2[:2], bf2[2:-2], bf2[-2:-1], bf2[1] + tf2[:-1], tf2[-1], bf3 ] for c in chunks: self.decoder.dataReceived(c) self.assertEquals(self.decoder.handler.frames, ["text", "other text"]) self.assertFalse(self.channel.transport.disconnected) def text_maxBinaryLength(self): """ If a binary frame's declared length exceeds MAX_BINARY_LENGTH, the connection is dropped. """ self.decoder.dataReceived("\xff\xff\xff\xff\xff\xff") self.assertTrue(self.channel.transport.disconnected) def test_closingHandshake(self): """ After receiving the closing handshake, the server sends its own closing handshake and ignores all future data. """ self.decoder.dataReceived("\x00frame\xff\xff\x00random crap") self.decoder.dataReceived("more random crap, that's discarded") self.assertEquals(self.decoder.handler.frames, ["frame"]) self.assertTrue(self.decoder.closing) def test_invalidFrameType(self): """ Frame types other than 0x00 and 0xff cause the connection to be dropped. """ ok = "\x00ok\xff" wrong = "\x05foo\xff" self.decoder.dataReceived(ok + wrong + ok) self.assertEquals(self.decoder.handler.frames, ["ok"]) error = self.assertOneDecodingError() self.assertTrue(self.channel.transport.disconnected) def test_emptyFrame(self): """ An empty text frame is correctly parsed. """ self.decoder.dataReceived("\x00\xff") self.assertEquals(self.decoder.handler.frames, [""]) self.assertFalse(self.channel.transport.disconnected)