fixed #43
This commit is contained in:
@@ -23,15 +23,16 @@ import websocket as ws
|
|||||||
# [1]: https://tools.ietf.org/html/rfc6455#section-5.4
|
# [1]: https://tools.ietf.org/html/rfc6455#section-5.4
|
||||||
# "RFC6455: 5.4. Fragmentation"
|
# "RFC6455: 5.4. Fragmentation"
|
||||||
#
|
#
|
||||||
TEST_FRAGMENTATION=False
|
TEST_FRAGMENTATION = True
|
||||||
|
|
||||||
|
TRACABLE = False
|
||||||
|
|
||||||
TRACABLE=False
|
|
||||||
|
|
||||||
def create_mask_key(n):
|
def create_mask_key(n):
|
||||||
return "abcd"
|
return "abcd"
|
||||||
|
|
||||||
class SockMock(object):
|
|
||||||
|
|
||||||
|
class SockMock(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.data = []
|
self.data = []
|
||||||
self.sent = []
|
self.sent = []
|
||||||
@@ -247,6 +248,14 @@ class WebSocketTest(unittest.TestCase):
|
|||||||
with self.assertRaises(ws.WebSocketConnectionClosedException):
|
with self.assertRaises(ws.WebSocketConnectionClosedException):
|
||||||
sock.recv()
|
sock.recv()
|
||||||
|
|
||||||
|
@unittest.skipUnless(TEST_FRAGMENTATION, "fragmentation not implemented")
|
||||||
|
def testRecvContFragmentation(self):
|
||||||
|
sock = ws.WebSocket()
|
||||||
|
s = sock.sock = SockMock()
|
||||||
|
# OPCODE=CONT, FIN=1, MSG="the soul of wit"
|
||||||
|
s.add_packet("\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17")
|
||||||
|
self.assertRaises(ws.WebSocketException, sock.recv)
|
||||||
|
|
||||||
@unittest.skipUnless(TEST_FRAGMENTATION, "fragmentation not implemented")
|
@unittest.skipUnless(TEST_FRAGMENTATION, "fragmentation not implemented")
|
||||||
def testRecvWithProlongedFragmentation(self):
|
def testRecvWithProlongedFragmentation(self):
|
||||||
sock = ws.WebSocket()
|
sock = ws.WebSocket()
|
||||||
|
26
websocket.py
26
websocket.py
@@ -228,6 +228,7 @@ class ABNF(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# operation code values.
|
# operation code values.
|
||||||
|
OPCODE_CONT = 0x0
|
||||||
OPCODE_TEXT = 0x1
|
OPCODE_TEXT = 0x1
|
||||||
OPCODE_BINARY = 0x2
|
OPCODE_BINARY = 0x2
|
||||||
OPCODE_CLOSE = 0x8
|
OPCODE_CLOSE = 0x8
|
||||||
@@ -235,11 +236,12 @@ class ABNF(object):
|
|||||||
OPCODE_PONG = 0xa
|
OPCODE_PONG = 0xa
|
||||||
|
|
||||||
# available operation code value tuple
|
# available operation code value tuple
|
||||||
OPCODES = (OPCODE_TEXT, OPCODE_BINARY, OPCODE_CLOSE,
|
OPCODES = (OPCODE_CONT, OPCODE_TEXT, OPCODE_BINARY, OPCODE_CLOSE,
|
||||||
OPCODE_PING, OPCODE_PONG)
|
OPCODE_PING, OPCODE_PONG)
|
||||||
|
|
||||||
# opcode human readable string
|
# opcode human readable string
|
||||||
OPCODE_MAP = {
|
OPCODE_MAP = {
|
||||||
|
OPCODE_CONT: "cont",
|
||||||
OPCODE_TEXT: "text",
|
OPCODE_TEXT: "text",
|
||||||
OPCODE_BINARY: "binary",
|
OPCODE_BINARY: "binary",
|
||||||
OPCODE_CLOSE: "close",
|
OPCODE_CLOSE: "close",
|
||||||
@@ -267,6 +269,11 @@ class ABNF(object):
|
|||||||
self.data = data
|
self.data = data
|
||||||
self.get_mask_key = os.urandom
|
self.get_mask_key = os.urandom
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "fin=" + str(self.fin) \
|
||||||
|
+ " opcode=" + str(self.opcode) \
|
||||||
|
+ " data=" + str(self.data)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_frame(data, opcode):
|
def create_frame(data, opcode):
|
||||||
"""
|
"""
|
||||||
@@ -379,6 +386,7 @@ class WebSocket(object):
|
|||||||
self._frame_header = None
|
self._frame_header = None
|
||||||
self._frame_length = None
|
self._frame_length = None
|
||||||
self._frame_mask = None
|
self._frame_mask = None
|
||||||
|
self._cont_data = None
|
||||||
|
|
||||||
def fileno(self):
|
def fileno(self):
|
||||||
return self.sock.fileno()
|
return self.sock.fileno()
|
||||||
@@ -552,11 +560,13 @@ class WebSocket(object):
|
|||||||
if self.get_mask_key:
|
if self.get_mask_key:
|
||||||
frame.get_mask_key = self.get_mask_key
|
frame.get_mask_key = self.get_mask_key
|
||||||
data = frame.format()
|
data = frame.format()
|
||||||
|
length = len(data)
|
||||||
if traceEnabled:
|
if traceEnabled:
|
||||||
logger.debug("send: " + repr(data))
|
logger.debug("send: " + repr(data))
|
||||||
while data:
|
while data:
|
||||||
l = self._send(data)
|
l = self._send(data)
|
||||||
data = data[l:]
|
data = data[l:]
|
||||||
|
return length
|
||||||
|
|
||||||
def send_binary(self, payload):
|
def send_binary(self, payload):
|
||||||
return self.send(payload, ABNF.OPCODE_BINARY)
|
return self.send(payload, ABNF.OPCODE_BINARY)
|
||||||
@@ -598,8 +608,18 @@ class WebSocket(object):
|
|||||||
# handle error:
|
# handle error:
|
||||||
# 'NoneType' object has no attribute 'opcode'
|
# 'NoneType' object has no attribute 'opcode'
|
||||||
raise WebSocketException("Not a valid frame %s" % frame)
|
raise WebSocketException("Not a valid frame %s" % frame)
|
||||||
elif frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY):
|
elif frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY, ABNF.OPCODE_CONT):
|
||||||
return (frame.opcode, frame.data)
|
if frame.opcode == ABNF.OPCODE_CONT and not self._cont_data:
|
||||||
|
raise WebSocketException("Illegal frame")
|
||||||
|
if self._cont_data:
|
||||||
|
self._cont_data[1] += frame.data
|
||||||
|
else:
|
||||||
|
self._cont_data = [frame.opcode, frame.data]
|
||||||
|
|
||||||
|
if frame.fin:
|
||||||
|
data = self._cont_data
|
||||||
|
self._cont_data = None
|
||||||
|
return data
|
||||||
elif frame.opcode == ABNF.OPCODE_CLOSE:
|
elif frame.opcode == ABNF.OPCODE_CLOSE:
|
||||||
self.send_close()
|
self.send_close()
|
||||||
return (frame.opcode, None)
|
return (frame.opcode, None)
|
||||||
|
Reference in New Issue
Block a user