diff options
author | Hiroki Ohtani <hiro@hiro-no-iMac.local> | 2010-12-28 15:42:43 +0900 |
---|---|---|
committer | Hiroki Ohtani <hiro@hiro-no-iMac.local> | 2010-12-28 15:42:43 +0900 |
commit | eadcbf7e891b88e7e7e24ca5bbf0a7df3988dbb3 (patch) | |
tree | bafe06c7af2a52ea19ebceecfe6424a0c77e6a14 | |
download | websocket-client-eadcbf7e891b88e7e7e24ca5bbf0a7df3988dbb3.tar.gz |
first commit
-rw-r--r-- | README | 23 | ||||
-rw-r--r-- | setup.py | 23 | ||||
-rw-r--r-- | websocket.py | 209 |
3 files changed, 255 insertions, 0 deletions
@@ -0,0 +1,23 @@ +================= +websocket-client +================= + +websocket-client module is WebSocket client for python. This provide the low level APIs for WebSocket. All APIs are the synchronous functions. + +Installation +============= + +This module is tested on only Python 2.7. +Type "python setup.py install" to install. + +Example +======== + + from websocket import create_connection + ws = create_connection("ws://localhost:5000/echo") + print "Sending 'Hello, World'..." + ws.send("Hello, World") + print "Sent" + print "Reeiving..." + result = ws.recv() + print "Received '%s'" % result diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..46defa6 --- /dev/null +++ b/setup.py @@ -0,0 +1,23 @@ +from setuptools import setup + +setup( + name="websocket-client", + version="0.1", + description="WebSocket client for python", + long_description=open("README").read(), + author="liris", + author_email="liris.pp@gmail.com", + license="LGPL", + url="https://github.com/liris/websocket-client", + classifiers = [ + "Development Status :: 3 - Alpha", + "License :: OSI Approved :: LGPL License", + "Programming Language :: Python", + "Operating System :: MacOS :: MacOS X", + "Operating System :: POSIX", + "Operating System :: Microsoft :: Windows", + "Topic :: Internet", + "Topic :: Software Development :: Libraries :: Python Modules", + "Intended Audience :: Developers", + ], +) diff --git a/websocket.py b/websocket.py new file mode 100644 index 0000000..47c4b80 --- /dev/null +++ b/websocket.py @@ -0,0 +1,209 @@ +import socket +from urlparse import urlparse +import random + + +class ConnectionClosedException(Exception): + pass + +default_timeout = None + +def setdefaulttimeout(timeout): + """ + Set the global timeout setting to connect. + """ + global default_timeout + default_timeout = timeout + + +def getdefaulttimeout(): + """ + Return the global timeout setting to connect. + """ + return default_timeout + +def _parse_url(url): + parsed = urlparse(url) + if parsed.hostname: + hostname = parsed.hostname + else: + raise ValueError("hostname is invalid") + + if parsed.scheme == "ws": + if parsed.port: + port = parsed.port + else: + port = 80 + else: + raise ValueError("scheme %s is invalid" % parsed.scheme) + + if parsed.path: + resource = parsed.path + else: + resource = "/" + + return (hostname, port, resource) + + +def create_connection(url, timeout=None): + """ + connect to url and return websocket object. + + Connect to url and return the WebSocket object. + Passing optional timeout parameter will set the timeout on the socket. + If no timeout is supplied, the global default timeout setting returned by getdefauttimeout() is used. + """ + try: + websock = WebSocket() + websock.settimeout(timeout != None and timeout or default_timeout) + websock.connect(url) + return websock + except Exception, e: + #websock.close() + raise e + +_MAX_INTEGER = (1 << 32) -1 +_AVAILABLE_KEY_CHARS = range(0x21, 0x2f + 1) + range(0x3a, 0x7e + 1) +_MAX_CHAR_BYTE = (1<<8) -1 + +# ref. Websocket gets an update, and it breaks stuff. +# http://axod.blogspot.com/2010/06/websocket-gets-update-and-it-breaks.html + +def _create_sec_websocket_key(): + spaces_n = random.randint(1, 12) + max_n = _MAX_INTEGER / spaces_n + number_n = random.randint(0, max_n) + product_n = number_n * spaces_n + key_n = str(product_n) + for i in range(random.randint(1, 12)): + c = random.choice(_AVAILABLE_KEY_CHARS) + pos = random.randint(0, len(key_n)) + key_n = key_n[0:pos] + chr(c) + key_n[pos:] + for i in range(spaces_n): + pos = random.randint(1, len(key_n)-1) + key_n = key_n[0:pos] + " " + key_n[pos:] + + return number_n, key_n + +def _create_key3(): + return "".join([chr(random.randint(0, _MAX_CHAR_BYTE)) for i in range(8)]) + + +class WebSocket(object): + def __init__(self): + """ + Initalize WebSocket object. + """ + self.sock = socket.socket() + + def settimeout(self, timeout): + """ + Set the timeout to the websocket. + """ + self.sock.settimeout(timeout) + + def gettimeout(self): + """ + Get the websocket timeout. + """ + return self.sock.gettimeout() + + def connect(self, url): + """ + Connect to url. url is websocket url scheme. ie. ws://host:port/resource + """ + hostname, port, resource = _parse_url(url) + self.sock.connect((hostname, port)) + self._handshake(hostname, port, resource) + + def _handshake(self, host, port, resource): + sock = self.sock + headers = [] + + headers.append("GET %s HTTP/1.1" % resource) + headers.append("Upgrade: WebSocket") + headers.append("Connection: Upgrade") + if port == 80: + hostport = host + else: + hostport = "%s:%d" % (host, port) + headers.append("Host: %s" % hostport) + headers.append("Origin: %s" % hostport) + + number_1, key_1 = _create_sec_websocket_key() + # headers.append("Sec-WebSocket-Key1: %s" % key_1) + number_2, key_2 = _create_sec_websocket_key() + # headers.append("Sec-WebSocket-Key2: %s" % key_2) + + header_str = "\r\n".join(headers) + sock.send(header_str) + sock.send("\r\n\r\n") + key3 = _create_key3() + #sock.send(key3) + + while True: + line = self._recv_line() + # TODO: check handshake response + if line == "\r\n": + break + + def send(self, payload): + """ + Send the data as string. payload must be utf-8 string or unicoce. + """ + if isinstance(payload, unicode): + payload = payload.encode("utf-8") + self.sock.send("".join(["\x00", payload, "\xff"])) + + def recv(self): + """ + Reeive utf-8 string data from the server. + """ + bytes = [] + while True: + b = self._recv(1) + if b == "\xff": + break + elif b == "\x00": + # start bytes + pass + else: + bytes.append(b) + return "".join(bytes) + + def close(self): + """ + Close Websocket object + """ + self.sock.close() + + def _recv(self, bufsize): + bytes = self.sock.recv(bufsize) + if not bytes: + raise ConnectionClosedException() + return bytes + + def _recv_line(self): + line = [] + while True: + c = self._recv(1) + line.append(c) + if c == "\n": + break + return "".join(line) + + + +if __name__ == "__main__": + ws = create_connection("ws://localhost:5000/chat") + print "Sending 'Hello, World'..." + ws.send("Hello, World") + print "Sent" + print "Reeiving..." + result = ws.recv() + print "Received '%s'" % result + + + + + |