diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | README.rst | 35 | ||||
-rwxr-xr-x | bin/wsdump.py | 8 | ||||
-rw-r--r-- | examples/echoapp_client.py | 4 | ||||
-rw-r--r-- | setup.py | 4 | ||||
-rw-r--r-- | test_websocket.py | 233 | ||||
-rw-r--r-- | websocket.py | 95 |
7 files changed, 208 insertions, 172 deletions
@@ -6,3 +6,4 @@ build dist websocket_client.egg-info +websocket_client_py3.egg-info @@ -6,6 +6,13 @@ websocket-client module is WebSocket client for python. This provide the low le websocket-client supports only hybi-13. +How about Python 2 +=========================== + +py2( https://github.com/liris/websocket-client ) branch is for python 2. Every test case is passed. +If you are using python2, please check it. + + License ============ @@ -14,18 +21,12 @@ License Installation ============= -This module is tested on only Python 2.7. +This module is tested on Python 3.3. -Type "python setup.py install" or "pip install websocket-client" to install. +Check out git repository of py3 branch, and type "python3 setup.py install". This module does not depend on any other module. -How about Python 3 -=========================== - -py3( https://github.com/liris/websocket-client/tree/py3 ) branch is for python 3.3. Every test case is passed. -If you are using python3, please check it. - Example ============ @@ -33,12 +34,12 @@ Low Level API example:: from websocket import create_connection ws = create_connection("ws://echo.websocket.org/") - print "Sending 'Hello, World'..." + print("Sending 'Hello, World'...") ws.send("Hello, World") - print "Sent" - print "Reeiving..." + print("Sent") + print("Reeiving...") result = ws.recv() - print "Received '%s'" % result + print("Received '%s'" % result) ws.close() If you want to customize socket options, set sockopt. @@ -53,17 +54,17 @@ sockopt example: JavaScript websocket-like API example:: import websocket - import thread + import threading import time def on_message(ws, message): print message def on_error(ws, error): - print error + print(error) def on_close(ws): - print "### closed ###" + print("### closed ###") def on_open(ws): def run(*args): @@ -72,8 +73,8 @@ JavaScript websocket-like API example:: ws.send("Hello %d" % i) time.sleep(1) ws.close() - print "thread terminating..." - thread.start_new_thread(run, ()) + print("thread terminating...") + threading.Thread(target=run, args=()).start() if __name__ == "__main__": diff --git a/bin/wsdump.py b/bin/wsdump.py index 9cc5f03..f9708f9 100755 --- a/bin/wsdump.py +++ b/bin/wsdump.py @@ -45,11 +45,11 @@ class InteractiveConsole(code.InteractiveConsole): sys.stdout.flush() def raw_input(self, prompt): - line = raw_input(prompt) - if ENCODING and ENCODING != "utf-8" and not isinstance(line, unicode): + line = input(prompt) + if ENCODING and ENCODING != "utf-8" and not isinstance(line, str): line = line.decode(ENCODING).encode("utf-8") - elif isinstance(line, unicode): - line = encode("utf-8") + elif isinstance(line, str): + line = line.encode("utf-8") return line diff --git a/examples/echoapp_client.py b/examples/echoapp_client.py index e5232f7..76a55c7 100644 --- a/examples/echoapp_client.py +++ b/examples/echoapp_client.py @@ -1,5 +1,5 @@ import websocket -import thread +import _thread import time import sys @@ -29,7 +29,7 @@ def on_open(ws): ws.close() print("Thread terminating...") - thread.start_new_thread(run, ()) + _thread.start_new_thread(run, ()) if __name__ == "__main__": websocket.enableTrace(True) @@ -4,9 +4,9 @@ VERSION = "0.12.0" setup( - name="websocket-client", + name="websocket-client-py3", version=VERSION, - description="WebSocket client for python. hybi13 is supported.", + description="WebSocket client for python3. hybi13 is supported.", long_description=open("README.rst").read(), author="liris", author_email="liris.pp@gmail.com", diff --git a/test_websocket.py b/test_websocket.py index 0800a3c..30fdd50 100644 --- a/test_websocket.py +++ b/test_websocket.py @@ -29,7 +29,7 @@ TRACABLE = False def create_mask_key(n): - return "abcd" + return b"abcd" class SockMock(object): @@ -38,6 +38,8 @@ class SockMock(object): self.sent = [] def add_packet(self, data): + if isinstance(data, str): + data = bytes(data, "utf-8") self.data.append(data) def recv(self, bufsize): @@ -53,12 +55,16 @@ class SockMock(object): self.sent.append(data) return len(data) + def close(self): + pass + class HeaderSockMock(SockMock): def __init__(self, fname): SockMock.__init__(self) - self.add_packet(open(fname).read()) + with open(fname, "rb") as f: + self.add_packet(f.read()) class WebSocketTest(unittest.TestCase): @@ -69,72 +75,72 @@ class WebSocketTest(unittest.TestCase): pass def testDefaultTimeout(self): - self.assertEquals(ws.getdefaulttimeout(), None) + self.assertEqual(ws.getdefaulttimeout(), None) ws.setdefaulttimeout(10) - self.assertEquals(ws.getdefaulttimeout(), 10) + self.assertEqual(ws.getdefaulttimeout(), 10) ws.setdefaulttimeout(None) def testParseUrl(self): p = ws._parse_url("ws://www.example.com/r") - self.assertEquals(p[0], "www.example.com") - self.assertEquals(p[1], 80) - self.assertEquals(p[2], "/r") - self.assertEquals(p[3], False) + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 80) + self.assertEqual(p[2], "/r") + self.assertEqual(p[3], False) p = ws._parse_url("ws://www.example.com/r/") - self.assertEquals(p[0], "www.example.com") - self.assertEquals(p[1], 80) - self.assertEquals(p[2], "/r/") - self.assertEquals(p[3], False) + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 80) + self.assertEqual(p[2], "/r/") + self.assertEqual(p[3], False) p = ws._parse_url("ws://www.example.com/") - self.assertEquals(p[0], "www.example.com") - self.assertEquals(p[1], 80) - self.assertEquals(p[2], "/") - self.assertEquals(p[3], False) + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 80) + self.assertEqual(p[2], "/") + self.assertEqual(p[3], False) p = ws._parse_url("ws://www.example.com") - self.assertEquals(p[0], "www.example.com") - self.assertEquals(p[1], 80) - self.assertEquals(p[2], "/") - self.assertEquals(p[3], False) + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 80) + self.assertEqual(p[2], "/") + self.assertEqual(p[3], False) p = ws._parse_url("ws://www.example.com:8080/r") - self.assertEquals(p[0], "www.example.com") - self.assertEquals(p[1], 8080) - self.assertEquals(p[2], "/r") - self.assertEquals(p[3], False) + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 8080) + self.assertEqual(p[2], "/r") + self.assertEqual(p[3], False) p = ws._parse_url("ws://www.example.com:8080/") - self.assertEquals(p[0], "www.example.com") - self.assertEquals(p[1], 8080) - self.assertEquals(p[2], "/") - self.assertEquals(p[3], False) + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 8080) + self.assertEqual(p[2], "/") + self.assertEqual(p[3], False) p = ws._parse_url("ws://www.example.com:8080") - self.assertEquals(p[0], "www.example.com") - self.assertEquals(p[1], 8080) - self.assertEquals(p[2], "/") - self.assertEquals(p[3], False) + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 8080) + self.assertEqual(p[2], "/") + self.assertEqual(p[3], False) p = ws._parse_url("wss://www.example.com:8080/r") - self.assertEquals(p[0], "www.example.com") - self.assertEquals(p[1], 8080) - self.assertEquals(p[2], "/r") - self.assertEquals(p[3], True) + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 8080) + self.assertEqual(p[2], "/r") + self.assertEqual(p[3], True) p = ws._parse_url("wss://www.example.com:8080/r?key=value") - self.assertEquals(p[0], "www.example.com") - self.assertEquals(p[1], 8080) - self.assertEquals(p[2], "/r?key=value") - self.assertEquals(p[3], True) + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 8080) + self.assertEqual(p[2], "/r?key=value") + self.assertEqual(p[3], True) self.assertRaises(ValueError, ws._parse_url, "http://www.example.com/r") def testWSKey(self): key = ws._create_sec_websocket_key() - self.assert_(key != 24) - self.assert_("¥n" not in key) + self.assertTrue(key != 24) + self.assertTrue("¥n" not in key) def testWsUtils(self): sock = ws.WebSocket() @@ -145,32 +151,34 @@ class WebSocketTest(unittest.TestCase): "connection": "upgrade", "sec-websocket-accept": "Kxep+hNu9n51529fGidYu7a3wO0=", } - self.assertEquals(sock._validate_header(required_header, key), True) + self.assertEqual(sock._validate_header(required_header, key), True) header = required_header.copy() header["upgrade"] = "http" - self.assertEquals(sock._validate_header(header, key), False) + self.assertEqual(sock._validate_header(header, key), False) del header["upgrade"] - self.assertEquals(sock._validate_header(header, key), False) + self.assertEqual(sock._validate_header(header, key), False) header = required_header.copy() header["connection"] = "something" - self.assertEquals(sock._validate_header(header, key), False) + self.assertEqual(sock._validate_header(header, key), False) del header["connection"] - self.assertEquals(sock._validate_header(header, key), False) + self.assertEqual(sock._validate_header(header, key), False) header = required_header.copy() header["sec-websocket-accept"] = "something" - self.assertEquals(sock._validate_header(header, key), False) + self.assertEqual(sock._validate_header(header, key), False) del header["sec-websocket-accept"] - self.assertEquals(sock._validate_header(header, key), False) + self.assertEqual(sock._validate_header(header, key), False) + sock.close() def testReadHeader(self): sock = ws.WebSocket() + sock.sock.close() sock.sock = HeaderSockMock("data/header01.txt") status, header = sock._read_headers() - self.assertEquals(status, 101) - self.assertEquals(header["connection"], "upgrade") + self.assertEqual(status, 101) + self.assertEqual(header["connection"], "upgrade") sock.sock = HeaderSockMock("data/header02.txt") self.assertRaises(ws.WebSocketException, sock._read_headers) @@ -179,70 +187,75 @@ class WebSocketTest(unittest.TestCase): # TODO: add longer frame data sock = ws.WebSocket() sock.set_mask_key(create_mask_key) + sock.sock.close() s = sock.sock = HeaderSockMock("data/header01.txt") sock.send("Hello") - self.assertEquals(s.sent[0], "\x81\x85abcd)\x07\x0f\x08\x0e") + self.assertEqual(s.sent[0], b"\x81\x85abcd)\x07\x0f\x08\x0e") sock.send("こんにちは") - self.assertEquals(s.sent[1], "\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc") + self.assertEqual(s.sent[1], b"\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc") sock.send(u"こんにちは") - self.assertEquals(s.sent[1], "\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc") + self.assertEqual(s.sent[1], b"\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc") def testRecv(self): # TODO: add longer frame data sock = ws.WebSocket() + sock.sock.close() s = sock.sock = SockMock() - s.add_packet("\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc") + s.add_packet(b"\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc") data = sock.recv() - self.assertEquals(data, "こんにちは") + self.assertEqual(data, "こんにちは") - s.add_packet("\x81\x85abcd)\x07\x0f\x08\x0e") + s.add_packet(b"\x81\x85abcd)\x07\x0f\x08\x0e") data = sock.recv() - self.assertEquals(data, "Hello") + self.assertEqual(data, "Hello") def testInternalRecvStrict(self): sock = ws.WebSocket() + sock.sock.close() s = sock.sock = SockMock() - s.add_packet("foo") + s.add_packet(b"foo") s.add_packet(socket.timeout()) - s.add_packet("bar") + s.add_packet(b"bar") s.add_packet(SSLError("The read operation timed out")) - s.add_packet("baz") + s.add_packet(b"baz") with self.assertRaises(ws.WebSocketTimeoutException): data = sock._recv_strict(9) with self.assertRaises(ws.WebSocketTimeoutException): data = sock._recv_strict(9) data = sock._recv_strict(9) - self.assertEquals(data, "foobarbaz") + self.assertEqual(data, b"foobarbaz") with self.assertRaises(ws.WebSocketConnectionClosedException): data = sock._recv_strict(1) def testRecvTimeout(self): sock = ws.WebSocket() + sock.sock.close() s = sock.sock = SockMock() - s.add_packet("\x81") + s.add_packet(b"\x81") s.add_packet(socket.timeout()) - s.add_packet("\x8dabcd\x29\x07\x0f\x08\x0e") + s.add_packet(b"\x8dabcd\x29\x07\x0f\x08\x0e") s.add_packet(socket.timeout()) - s.add_packet("\x4e\x43\x33\x0e\x10\x0f\x00\x40") + s.add_packet(b"\x4e\x43\x33\x0e\x10\x0f\x00\x40") with self.assertRaises(ws.WebSocketTimeoutException): data = sock.recv() with self.assertRaises(ws.WebSocketTimeoutException): data = sock.recv() data = sock.recv() - self.assertEquals(data, "Hello, World!") + self.assertEqual(data, "Hello, World!") with self.assertRaises(ws.WebSocketConnectionClosedException): data = sock.recv() @unittest.skipUnless(TEST_FRAGMENTATION, "fragmentation not implemented") def testRecvWithSimpleFragmentation(self): sock = ws.WebSocket() + sock.sock.close() s = sock.sock = SockMock() # OPCODE=TEXT, FIN=0, MSG="Brevity is " - s.add_packet("\x01\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C") + s.add_packet(b"\x01\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C") # 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") + s.add_packet(b"\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17") data = sock.recv() self.assertEqual(data, "Brevity is the soul of wit") with self.assertRaises(ws.WebSocketConnectionClosedException): @@ -251,23 +264,25 @@ class WebSocketTest(unittest.TestCase): @unittest.skipUnless(TEST_FRAGMENTATION, "fragmentation not implemented") def testRecvContFragmentation(self): sock = ws.WebSocket() + sock.sock.close() 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") + s.add_packet(b"\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") def testRecvWithProlongedFragmentation(self): sock = ws.WebSocket() + sock.sock.close() s = sock.sock = SockMock() # OPCODE=TEXT, FIN=0, MSG="Once more unto the breach, " - s.add_packet("\x01\x9babcd.\x0c\x00\x01A\x0f\x0c\x16\x04B\x16\n\x15" \ - "\rC\x10\t\x07C\x06\x13\x07\x02\x07\tNC") + s.add_packet(b"\x01\x9babcd.\x0c\x00\x01A\x0f\x0c\x16\x04B\x16\n\x15" \ + b"\rC\x10\t\x07C\x06\x13\x07\x02\x07\tNC") # OPCODE=CONT, FIN=0, MSG="dear friends, " - s.add_packet("\x00\x8eabcd\x05\x07\x02\x16A\x04\x11\r\x04\x0c\x07" \ - "\x17MB") + s.add_packet(b"\x00\x8eabcd\x05\x07\x02\x16A\x04\x11\r\x04\x0c\x07" \ + b"\x17MB") # OPCODE=CONT, FIN=1, MSG="once more" - s.add_packet("\x80\x89abcd\x0e\x0c\x00\x01A\x0f\x0c\x16\x04") + s.add_packet(b"\x80\x89abcd\x0e\x0c\x00\x01A\x0f\x0c\x16\x04") data = sock.recv() self.assertEqual(data, "Once more unto the breach, dear friends, " \ "once more") @@ -278,36 +293,37 @@ class WebSocketTest(unittest.TestCase): def testRecvWithFragmentationAndControlFrame(self): sock = ws.WebSocket() sock.set_mask_key(create_mask_key) + sock.sock.close() s = sock.sock = SockMock() # OPCODE=TEXT, FIN=0, MSG="Too much " - s.add_packet("\x01\x89abcd5\r\x0cD\x0c\x17\x00\x0cA") + s.add_packet(b"\x01\x89abcd5\r\x0cD\x0c\x17\x00\x0cA") # OPCODE=PING, FIN=1, MSG="Please PONG this" - s.add_packet("\x89\x90abcd1\x0e\x06\x05\x12\x07C4.,$D\x15\n\n\x17") + s.add_packet(b"\x89\x90abcd1\x0e\x06\x05\x12\x07C4.,$D\x15\n\n\x17") # OPCODE=CONT, FIN=1, MSG="of a good thing" - s.add_packet("\x80\x8fabcd\x0e\x04C\x05A\x05\x0c\x0b\x05B\x17\x0c" \ - "\x08\x0c\x04") + s.add_packet(b"\x80\x8fabcd\x0e\x04C\x05A\x05\x0c\x0b\x05B\x17\x0c" \ + b"\x08\x0c\x04") data = sock.recv() self.assertEqual(data, "Too much of a good thing") with self.assertRaises(ws.WebSocketConnectionClosedException): sock.recv() - self.assertEqual(s.sent[0], "\x8a\x90abcd1\x0e\x06\x05\x12\x07C4.,$D" \ - "\x15\n\n\x17") + self.assertEqual(s.sent[0], b"\x8a\x90abcd1\x0e\x06\x05\x12\x07C4.,$D" \ + b"\x15\n\n\x17") def testWebSocket(self): s = ws.create_connection("ws://echo.websocket.org/") - self.assertNotEquals(s, None) + self.assertNotEqual(s, None) s.send("Hello, World") result = s.recv() - self.assertEquals(result, "Hello, World") + self.assertEqual(result, "Hello, World") s.send("こにゃにゃちは、世界") result = s.recv() - self.assertEquals(result, "こにゃにゃちは、世界") + self.assertEqual(result, "こにゃにゃちは、世界") s.close() def testPingPong(self): s = ws.create_connection("ws://echo.websocket.org/") - self.assertNotEquals(s, None) + self.assertNotEqual(s, None) s.ping("Hello") s.pong("Hi") s.close() @@ -316,41 +332,56 @@ class WebSocketTest(unittest.TestCase): try: import ssl s = ws.create_connection("wss://echo.websocket.org/") - self.assertNotEquals(s, None) - self.assert_(isinstance(s.sock, ssl.SSLSock)) + self.assertNotEqual(s, None) + self.assertTrue(isinstance(s.sock, ssl.SSLSock)) s.send("Hello, World") result = s.recv() - self.assertEquals(result, "Hello, World") + self.assertEqual(result, "Hello, World") s.send("こにゃにゃちは、世界") result = s.recv() - self.assertEquals(result, "こにゃにゃちは、世界") + self.assertEqual(result, "こにゃにゃちは、世界") s.close() except: pass def testWebSocketWihtCustomHeader(self): s = ws.create_connection("ws://echo.websocket.org/", + headers={"User-Agent": "PythonWebsocketClient"}) + self.assertNotEqual(s, None) + + def testWebSocketWihtCustomHeader(self): + s = ws.create_connection("ws://echo.websocket.org/", headers={"User-Agent": "PythonWebsocketClient"}) - self.assertNotEquals(s, None) + self.assertNotEqual(s, None) s.send("Hello, World") result = s.recv() - self.assertEquals(result, "Hello, World") + self.assertEqual(result, "Hello, World") s.close() def testAfterClose(self): - from socket import error s = ws.create_connection("ws://echo.websocket.org/") - self.assertNotEquals(s, None) + self.assertNotEqual(s, None) s.close() - self.assertRaises(error, s.send, "Hello") - self.assertRaises(error, s.recv) + try: + s.send("Hello") + #never reach here + self.assertEqual(1, 0) + except OSError: + pass + + try: + s.recv() + #never reach here + self.assertEqual(1, 0) + except OSError: + pass def testUUID4(self): """ WebSocket key should be a UUID4. """ key = ws._create_sec_websocket_key() u = uuid.UUID(bytes=base64.b64decode(key)) - self.assertEquals(4, u.version) + self.assertEqual(4, u.version) class WebSocketAppTest(unittest.TestCase): @@ -397,8 +428,8 @@ class WebSocketAppTest(unittest.TestCase): self.assertFalse(isinstance(WebSocketAppTest.keep_running_close, WebSocketAppTest.NotSetYet)) - self.assertEquals(True, WebSocketAppTest.keep_running_open) - self.assertEquals(False, WebSocketAppTest.keep_running_close) + self.assertEqual(True, WebSocketAppTest.keep_running_open) + self.assertEqual(False, WebSocketAppTest.keep_running_close) def testSockMaskKey(self): """ A WebSocketApp should forward the received mask_key function down @@ -419,16 +450,18 @@ class WebSocketAppTest(unittest.TestCase): app.run_forever() # Note: We can't use 'is' for comparing the functions directly, need to use 'id'. - self.assertEquals(WebSocketAppTest.get_mask_key_id, id(my_mask_key_func)) + self.assertEqual(WebSocketAppTest.get_mask_key_id, id(my_mask_key_func)) class SockOptTest(unittest.TestCase): def testSockOpt(self): sockopt = ((socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),) s = ws.WebSocket(sockopt=sockopt) - self.assertNotEquals(s.sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY), 0) + self.assertNotEqual(s.sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY), 0) + s.close() s = ws.create_connection("ws://echo.websocket.org", sockopt=sockopt) - self.assertNotEquals(s.sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY), 0) + self.assertNotEqual(s.sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY), 0) + s.close() if __name__ == "__main__": diff --git a/websocket.py b/websocket.py index bdd6b45..aadc08f 100644 --- a/websocket.py +++ b/websocket.py @@ -21,19 +21,18 @@ Copyright (C) 2010 Hiroki Ohtani(liris) import socket - try: import ssl from ssl import SSLError HAVE_SSL = True except ImportError: - # dummy class of SSLError for ssl none-support environment. + HAVE_SSL = False + + # dummy class for SSLError class SSLError(Exception): pass - HAVE_SSL = False - -from urlparse import urlparse +from urllib.parse import urlparse import os import array import struct @@ -202,7 +201,7 @@ def create_connection(url, timeout=None, **options): return websock _MAX_INTEGER = (1 << 32) -1 -_AVAILABLE_KEY_CHARS = range(0x21, 0x2f + 1) + range(0x3a, 0x7e + 1) +_AVAILABLE_KEY_CHARS = list(range(0x21, 0x2f + 1)) + list(range(0x3a, 0x7e + 1)) _MAX_CHAR_BYTE = (1<<8) -1 # ref. Websocket gets an update, and it breaks stuff. @@ -211,7 +210,7 @@ _MAX_CHAR_BYTE = (1<<8) -1 def _create_sec_websocket_key(): uid = uuid.uuid4() - return base64.encodestring(uid.bytes).strip() + return base64.encodebytes(uid.bytes).strip().decode("utf-8") _HEADERS_TO_CHECK = { @@ -285,7 +284,7 @@ class ABNF(object): opcode: operation code. please see OPCODE_XXX. """ - if opcode == ABNF.OPCODE_TEXT and isinstance(data, unicode): + if opcode == ABNF.OPCODE_TEXT and isinstance(data, str): data = data.encode("utf-8") # mask must be set if send data from client return ABNF(1, 0, 0, 0, opcode, 1, data) @@ -302,16 +301,17 @@ class ABNF(object): if length >= ABNF.LENGTH_63: raise ValueError("data is too long") - frame_header = chr(self.fin << 7 + frame_header = [] + frame_header = bytes((self.fin << 7 | self.rsv1 << 6 | self.rsv2 << 5 | self.rsv3 << 4 - | self.opcode) + | self.opcode,)) if length < ABNF.LENGTH_7: - frame_header += chr(self.mask << 7 | length) + frame_header += bytes((self.mask << 7 | length,)) elif length < ABNF.LENGTH_16: - frame_header += chr(self.mask << 7 | 0x7e) + frame_header += bytes((self.mask << 7 | 0x7e,)) frame_header += struct.pack("!H", length) else: - frame_header += chr(self.mask << 7 | 0x7f) + frame_header += bytes((self.mask << 7 | 0x7f,)) frame_header += struct.pack("!Q", length) if not self.mask: @@ -322,7 +322,7 @@ class ABNF(object): def _get_masked(self, mask_key): s = ABNF.mask(mask_key, self.data) - return mask_key + "".join(s) + return mask_key + s @staticmethod def mask(mask_key, data): @@ -335,9 +335,10 @@ class ABNF(object): """ _m = array.array("B", mask_key) _d = array.array("B", data) - for i in xrange(len(_d)): + for i in range(len(_d)): _d[i] ^= _m[i % 4] - return _d.tostring() + + return _d.tobytes() class WebSocket(object): @@ -455,7 +456,6 @@ class WebSocket(object): self._handshake(hostname, port, resource, **options) def _handshake(self, host, port, resource, **options): - sock = self.sock headers = [] headers.append("GET %s HTTP/1.1" % resource) headers.append("Upgrade: websocket") @@ -481,7 +481,7 @@ class WebSocket(object): headers.append("") header_str = "\r\n".join(headers) - self._send(header_str) + self._send(bytes(header_str, "utf-8")) if traceEnabled: logger.debug("--- request header ---") logger.debug(header_str) @@ -500,7 +500,7 @@ class WebSocket(object): self.connected = True def _validate_header(self, headers, key): - for k, v in _HEADERS_TO_CHECK.iteritems(): + for k, v in _HEADERS_TO_CHECK.items(): r = headers.get(k, None) if not r: return False @@ -514,7 +514,8 @@ class WebSocket(object): result = result.lower() value = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - hashed = base64.encodestring(hashlib.sha1(value).digest()).strip().lower() + digest = hashlib.sha1(bytes(value, "utf-8")).digest() + hashed = base64.encodebytes(digest).strip().decode("utf-8").lower() return hashed == result def _read_headers(self): @@ -522,7 +523,6 @@ class WebSocket(object): headers = {} if traceEnabled: logger.debug("--- response header ---") - while True: line = self._recv_line() if line == "\r\n": @@ -550,12 +550,14 @@ class WebSocket(object): """ Send the data as string. - payload: Payload must be utf-8 string or unicoce, + payload: Payload must be utf-8 bytes or str, if the opcode is OPCODE_TEXT. - Otherwise, it must be string(byte array) + Otherwise, it must be byte array opcode: operation code to send. Please see OPCODE_XXX. """ + if isinstance(payload, str): + payload = bytes(payload, "utf-8") frame = ABNF.create_frame(payload, opcode) if self.get_mask_key: frame.get_mask_key = self.get_mask_key @@ -594,6 +596,8 @@ class WebSocket(object): return value: string(byte array) value. """ opcode, data = self.recv_data() + if opcode == ABNF.OPCODE_TEXT: + return data.decode("utf-8") return data def recv_data(self): @@ -635,14 +639,16 @@ class WebSocket(object): # Header if self._frame_header is None: self._frame_header = self._recv_strict(2) - b1 = ord(self._frame_header[0]) + b1 = self._frame_header[0] fin = b1 >> 7 & 1 rsv1 = b1 >> 6 & 1 rsv2 = b1 >> 5 & 1 rsv3 = b1 >> 4 & 1 opcode = b1 & 0xf - b2 = ord(self._frame_header[1]) + + b2 = self._frame_header[1] has_mask = b2 >> 7 & 1 + # Frame length if self._frame_length is None: length_bits = b2 & 0x7f @@ -654,30 +660,35 @@ class WebSocket(object): self._frame_length = struct.unpack("!Q", length_data)[0] else: self._frame_length = length_bits + # Mask if self._frame_mask is None: self._frame_mask = self._recv_strict(4) if has_mask else "" + # Payload payload = self._recv_strict(self._frame_length) if has_mask: payload = ABNF.mask(self._frame_mask, payload) + # Reset for next frame self._frame_header = None self._frame_length = None self._frame_mask = None - return ABNF(fin, rsv1, rsv2, rsv3, opcode, has_mask, payload) + return ABNF(fin, rsv1, rsv2, rsv3, opcode, has_mask, payload) - def send_close(self, status=STATUS_NORMAL, reason=""): + def send_close(self, status=STATUS_NORMAL, reason=b""): """ send close data to the server. status: status code to send. see STATUS_XXX. - reason: the reason to close. This must be string. + reason: the reason to close. This must be string or bytes """ if status < 0 or status >= ABNF.LENGTH_16: raise ValueError("code is invalid range") + if isinstance(reason, str): + reason = bytes(reason, "utf-8") self.send(struct.pack('!H', status) + reason, ABNF.OPCODE_CLOSE) def close(self, status=STATUS_NORMAL, reason=""): @@ -718,35 +729,26 @@ class WebSocket(object): try: return self.sock.send(data) except socket.timeout as e: - raise WebSocketTimeoutException(e.message) - except Exception as e: - if "timed out" in e.message: - raise WebSocketTimeoutException(e.message) - else: - raise e + raise WebSocketTimeoutException(*e.args) def _recv(self, bufsize): try: bytes = self.sock.recv(bufsize) except socket.timeout as e: - raise WebSocketTimeoutException(e.message) - except SSLError as e: - if e.message == "The read operation timed out": - raise WebSocketTimeoutException(e.message) - else: - raise + raise WebSocketTimeoutException(*e.args) + if not bytes: raise WebSocketConnectionClosedException() return bytes - def _recv_strict(self, bufsize): shortage = bufsize - sum(len(x) for x in self._recv_buffer) while shortage > 0: bytes = self._recv(shortage) self._recv_buffer.append(bytes) shortage -= len(bytes) - unified = "".join(self._recv_buffer) + + unified = b"".join(self._recv_buffer) if shortage == 0: self._recv_buffer = [] return unified @@ -754,15 +756,14 @@ class WebSocket(object): self._recv_buffer = [unified[bufsize:]] return unified[:bufsize] - def _recv_line(self): line = [] while True: c = self._recv(1) line.append(c) - if c == "\n": + if c == b"\n": break - return "".join(line) + return b"".join(line).decode("utf-8") class WebSocketApp(object): @@ -857,7 +858,7 @@ class WebSocketApp(object): if data is None: break self._callback(self.on_message, data) - except Exception, e: + except Exception as e: self._callback(self.on_error, e) finally: if thread: @@ -870,7 +871,7 @@ class WebSocketApp(object): if callback: try: callback(self, *args) - except Exception, e: + except Exception as e: logger.error(e) if logger.isEnabledFor(logging.DEBUG): _, _, tb = sys.exc_info() |