From c3fc642b0044726e6e35d5c4d56d6832ed37fe52 Mon Sep 17 00:00:00 2001 From: Larivact Date: Wed, 23 Nov 2016 05:47:28 +0100 Subject: [PATCH 01/26] add parentheses to print statements in the readme The examples should be copy'n'pastable for Python 3. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 18675b3..03641f1 100644 --- a/README.md +++ b/README.md @@ -25,10 +25,10 @@ class SimpleEcho(WebSocket): self.sendMessage(self.data) def handleConnected(self): - print self.address, 'connected' + print(self.address, 'connected') def handleClose(self): - print self.address, 'closed' + print(self.address, 'closed') server = SimpleWebSocketServer('', 8000, SimpleEcho) server.serveforever() @@ -49,14 +49,14 @@ class SimpleChat(WebSocket): client.sendMessage(self.address[0] + u' - ' + self.data) def handleConnected(self): - print self.address, 'connected' + print(self.address, 'connected') for client in clients: client.sendMessage(self.address[0] + u' - connected') clients.append(self) def handleClose(self): clients.remove(self) - print self.address, 'closed' + print(self.address, 'closed') for client in clients: client.sendMessage(self.address[0] + u' - disconnected') From 3bd885ad8b88c146a466d1ac6494304032aff429 Mon Sep 17 00:00:00 2001 From: dpallot Date: Fri, 10 Feb 2017 12:14:45 +0800 Subject: [PATCH 02/26] Set recv() size to 16k to account for SSL record size --- SimpleWebSocketServer/SimpleWebSocketServer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index c505e60..94152d7 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -271,7 +271,7 @@ def _handleData(self): # else do normal data else: - data = self.client.recv(8192) + data = self.client.recv(16384) if not data: raise Exception("remote socket closed") From a715cfd8ee0887af2b0a5ce16bf2d0a94e1e1b53 Mon Sep 17 00:00:00 2001 From: dpallot Date: Fri, 10 Feb 2017 12:40:18 +0800 Subject: [PATCH 03/26] Add flag to send message immedately --- SimpleWebSocketServer/SimpleWebSocketServer.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index 94152d7..9e0737f 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -305,7 +305,7 @@ def close(self, status = 1000, reason = u''): self.closed = True - def _sendBuffer(self, buff): + def _sendBuffer(self, buff, send_all = False): size = len(buff) tosend = size already_sent = 0 @@ -323,6 +323,8 @@ def _sendBuffer(self, buff): except socket.error as e: # if we have full buffers then wait for them to drain and try again if e.errno in [errno.EAGAIN, errno.EWOULDBLOCK]: + if send_all: + continue return buff[already_sent:] else: raise e @@ -361,7 +363,7 @@ def sendFragmentEnd(self, data): """ self._sendMessage(False, STREAM, data) - def sendMessage(self, data): + def sendMessage(self, data, immediate = False): """ Send websocket data frame to the client. @@ -371,10 +373,10 @@ def sendMessage(self, data): opcode = BINARY if _check_unicode(data): opcode = TEXT - self._sendMessage(False, opcode, data) + self._sendMessage(False, opcode, data, immediate) - def _sendMessage(self, fin, opcode, data): + def _sendMessage(self, fin, opcode, data, immediate = False): payload = bytearray() @@ -407,7 +409,10 @@ def _sendMessage(self, fin, opcode, data): if length > 0: payload.extend(data) - self.sendq.append((opcode, payload)) + if immediate: + self._sendBuffer(payload, True) + else: + self.sendq.append((opcode, payload)) def _parseMessage(self, byte): From 05b9a8bab6313f90dd0c99b3bb6add4364526a9d Mon Sep 17 00:00:00 2001 From: dpallot Date: Fri, 10 Feb 2017 13:59:38 +0800 Subject: [PATCH 04/26] Change variable name to sync --- SimpleWebSocketServer/SimpleWebSocketServer.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index 9e0737f..0c19abd 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -363,7 +363,7 @@ def sendFragmentEnd(self, data): """ self._sendMessage(False, STREAM, data) - def sendMessage(self, data, immediate = False): + def sendMessage(self, data, sync = False): """ Send websocket data frame to the client. @@ -373,10 +373,10 @@ def sendMessage(self, data, immediate = False): opcode = BINARY if _check_unicode(data): opcode = TEXT - self._sendMessage(False, opcode, data, immediate) + self._sendMessage(False, opcode, data, sync) - def _sendMessage(self, fin, opcode, data, immediate = False): + def _sendMessage(self, fin, opcode, data, sync = False): payload = bytearray() @@ -409,7 +409,7 @@ def _sendMessage(self, fin, opcode, data, immediate = False): if length > 0: payload.extend(data) - if immediate: + if sync: self._sendBuffer(payload, True) else: self.sendq.append((opcode, payload)) From 6e455f45b7ceb4ddfefb9e757fea7bc4403f84b8 Mon Sep 17 00:00:00 2001 From: dpallot Date: Fri, 10 Feb 2017 19:26:58 +0800 Subject: [PATCH 05/26] Revert sync changes --- SimpleWebSocketServer/SimpleWebSocketServer.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index 0c19abd..ba048fd 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -363,7 +363,7 @@ def sendFragmentEnd(self, data): """ self._sendMessage(False, STREAM, data) - def sendMessage(self, data, sync = False): + def sendMessage(self, data): """ Send websocket data frame to the client. @@ -373,10 +373,10 @@ def sendMessage(self, data, sync = False): opcode = BINARY if _check_unicode(data): opcode = TEXT - self._sendMessage(False, opcode, data, sync) + self._sendMessage(False, opcode, data) - def _sendMessage(self, fin, opcode, data, sync = False): + def _sendMessage(self, fin, opcode, data): payload = bytearray() @@ -409,10 +409,7 @@ def _sendMessage(self, fin, opcode, data, sync = False): if length > 0: payload.extend(data) - if sync: - self._sendBuffer(payload, True) - else: - self.sendq.append((opcode, payload)) + self.sendq.append((opcode, payload)) def _parseMessage(self, byte): From b322ea6c62780a5ecee88d059232e822c1aa561b Mon Sep 17 00:00:00 2001 From: dpallot Date: Thu, 13 Apr 2017 13:51:19 +0800 Subject: [PATCH 06/26] Only call handleClose if we have a successful handshake --- .../SimpleWebSocketServer.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index ba048fd..782d154 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -594,8 +594,16 @@ def close(self): for desc, conn in self.connections.items(): conn.close() - conn.handleClose() + self._handleClose(conn) + def _handleClose(self, client): + client.client.close() + # only call handleClose when we have a successful websocket connection + if client.handshaked: + try: + client.handleClose() + except: + pass def serveforever(self): while True: @@ -626,8 +634,7 @@ def serveforever(self): raise Exception('received client close') except Exception as n: - client.client.close() - client.handleClose() + self._handleClose(client) del self.connections[ready] self.listeners.remove(ready) @@ -650,8 +657,7 @@ def serveforever(self): try: client._handleData() except Exception as n: - client.client.close() - client.handleClose() + self._handleClose(client) del self.connections[ready] self.listeners.remove(ready) @@ -663,8 +669,7 @@ def serveforever(self): if failed not in self.connections: continue client = self.connections[failed] - client.client.close() - client.handleClose() + self._handleClose(client) del self.connections[failed] self.listeners.remove(failed) From 729d41439b31050d94ceab85f44fecf3ea2a9c73 Mon Sep 17 00:00:00 2001 From: kalissar30 Date: Fri, 28 Apr 2017 12:55:26 +0200 Subject: [PATCH 07/26] SimpleWebSocketServer now implement serveonce() --- .../SimpleWebSocketServer.py | 118 +++++++++--------- 1 file changed, 60 insertions(+), 58 deletions(-) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index 782d154..d15f17c 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -605,74 +605,76 @@ def _handleClose(self, client): except: pass - def serveforever(self): - while True: - writers = [] - for fileno in self.listeners: - if fileno == self.serversocket: - continue - client = self.connections[fileno] - if client.sendq: - writers.append(fileno) + def serveonce(self): + writers = [] + for fileno in self.listeners: + if fileno == self.serversocket: + continue + client = self.connections[fileno] + if client.sendq: + writers.append(fileno) + + if self.selectInterval: + rList, wList, xList = select(self.listeners, writers, self.listeners, self.selectInterval) + else: + rList, wList, xList = select(self.listeners, writers, self.listeners) - if self.selectInterval: - rList, wList, xList = select(self.listeners, writers, self.listeners, self.selectInterval) - else: - rList, wList, xList = select(self.listeners, writers, self.listeners) + for ready in wList: + client = self.connections[ready] + try: + while client.sendq: + opcode, payload = client.sendq.popleft() + remaining = client._sendBuffer(payload) + if remaining is not None: + client.sendq.appendleft((opcode, remaining)) + break + else: + if opcode == CLOSE: + raise Exception('received client close') + + except Exception as n: + self._handleClose(client) + del self.connections[ready] + self.listeners.remove(ready) - for ready in wList: + for ready in rList: + if ready == self.serversocket: + try: + sock, address = self.serversocket.accept() + newsock = self._decorateSocket(sock) + newsock.setblocking(0) + fileno = newsock.fileno() + self.connections[fileno] = self._constructWebSocket(newsock, address) + self.listeners.append(fileno) + except Exception as n: + if sock is not None: + sock.close() + else: + if ready not in self.connections: + continue client = self.connections[ready] try: - while client.sendq: - opcode, payload = client.sendq.popleft() - remaining = client._sendBuffer(payload) - if remaining is not None: - client.sendq.appendleft((opcode, remaining)) - break - else: - if opcode == CLOSE: - raise Exception('received client close') - + client._handleData() except Exception as n: self._handleClose(client) del self.connections[ready] self.listeners.remove(ready) - for ready in rList: - if ready == self.serversocket: - try: - sock, address = self.serversocket.accept() - newsock = self._decorateSocket(sock) - newsock.setblocking(0) - fileno = newsock.fileno() - self.connections[fileno] = self._constructWebSocket(newsock, address) - self.listeners.append(fileno) - except Exception as n: - if sock is not None: - sock.close() - else: - if ready not in self.connections: - continue - client = self.connections[ready] - try: - client._handleData() - except Exception as n: - self._handleClose(client) - del self.connections[ready] - self.listeners.remove(ready) - - for failed in xList: - if failed == self.serversocket: - self.close() - raise Exception('server socket failed') - else: - if failed not in self.connections: - continue - client = self.connections[failed] - self._handleClose(client) - del self.connections[failed] - self.listeners.remove(failed) + for failed in xList: + if failed == self.serversocket: + self.close() + raise Exception('server socket failed') + else: + if failed not in self.connections: + continue + client = self.connections[failed] + self._handleClose(client) + del self.connections[failed] + self.listeners.remove(failed) + def serveforever(self): + while True: + self.serveonce() class SimpleSSLWebSocketServer(SimpleWebSocketServer): From 3ad85acec3f0d0666062baf0d92a519532ce1bef Mon Sep 17 00:00:00 2001 From: deisner Date: Fri, 23 Jun 2017 12:21:48 -0400 Subject: [PATCH 08/26] clarify that address member is available in all callbacks --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 03641f1..3ee81b8 100644 --- a/README.md +++ b/README.md @@ -106,8 +106,10 @@ handleConnected: called when handshake is complete - self.address: TCP address port tuple of the endpoint handleClose: called when the endpoint is closed or there is an error + - self.address: TCP address port tuple of the endpoint handleMessage: gets called when there is an incoming message from the client endpoint + - self.address: TCP address port tuple of the endpoint - self.opcode: the WebSocket frame type (STREAM, TEXT, BINARY) - self.data: bytearray (BINARY frame) or unicode string payload (TEXT frame) - self.request: HTTP details from the WebSocket handshake (refer to BaseHTTPRequestHandler) From 8ab1a9c5fac346e272b333155e86ec6788951492 Mon Sep 17 00:00:00 2001 From: Mattijs Kneppers Date: Thu, 6 Jul 2017 11:49:35 +0200 Subject: [PATCH 09/26] fix websocket class erroneously referring to member instead of global --- SimpleWebSocketServer/SimpleWebSocketServer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index d15f17c..2981b22 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -454,7 +454,7 @@ def _parseMessage(self, byte): try: self._handlePacket() finally: - self.state = self.HEADERB1 + self.state = HEADERB1 self.data = bytearray() # we have no mask and some payload From 68b71090c17ebbfc9beaab695f1e0b2d010307bf Mon Sep 17 00:00:00 2001 From: Lucas Mlsna Date: Mon, 14 Aug 2017 20:10:15 -0500 Subject: [PATCH 10/26] Fixed SSL typo Based on the other examples, it looks like the ssl key file needs to be included as well as the cert. I added a command line option `--key` for that. Also, the SSL server was being given the cert file twice. This works for me now. Thanks for the great ws examples! --- SimpleWebSocketServer/SimpleExampleServer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SimpleWebSocketServer/SimpleExampleServer.py b/SimpleWebSocketServer/SimpleExampleServer.py index 8031d3c..d025679 100644 --- a/SimpleWebSocketServer/SimpleExampleServer.py +++ b/SimpleWebSocketServer/SimpleExampleServer.py @@ -49,6 +49,7 @@ def handleClose(self): parser.add_option("--example", default='echo', type='string', action="store", dest="example", help="echo, chat") parser.add_option("--ssl", default=0, type='int', action="store", dest="ssl", help="ssl (1: on, 0: off (default))") parser.add_option("--cert", default='./cert.pem', type='string', action="store", dest="cert", help="cert (./cert.pem)") + parser.add_option("--key", default='./key.pem', type='string', action="store", dest="key", help="key (./key.pem)") parser.add_option("--ver", default=ssl.PROTOCOL_TLSv1, type=int, action="store", dest="ver", help="ssl version") (options, args) = parser.parse_args() @@ -58,7 +59,7 @@ def handleClose(self): cls = SimpleChat if options.ssl == 1: - server = SimpleSSLWebSocketServer(options.host, options.port, cls, options.cert, options.cert, version=options.ver) + server = SimpleSSLWebSocketServer(options.host, options.port, cls, options.cert, options.key, version=options.ver) else: server = SimpleWebSocketServer(options.host, options.port, cls) From 96af6e7a4bd1b5a758ffa42fab7545d5f9e34d2b Mon Sep 17 00:00:00 2001 From: Kevin Freeman Date: Wed, 8 Nov 2017 10:55:23 +0100 Subject: [PATCH 11/26] Change overwriting of cert file --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ee81b8..9944a59 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Chat Server (open up multiple *websocket.html* files) 1) Generate a certificate with key - openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout cert.pem + openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout key.pem 2) Run the secure TSL/SSL server (in this case the cert.pem file is in the same directory) From a02c0bf106b8e32fe6ad5e25487d73e06ad5281b Mon Sep 17 00:00:00 2001 From: Alexey V Paramonov Date: Tue, 20 Mar 2018 18:22:59 +0300 Subject: [PATCH 12/26] Fix undefined 'sock' Exception --- SimpleWebSocketServer/SimpleWebSocketServer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index 2981b22..7e0d946 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -639,6 +639,7 @@ def serveonce(self): for ready in rList: if ready == self.serversocket: + sock = None try: sock, address = self.serversocket.accept() newsock = self._decorateSocket(sock) From 8f1ab2cf4322de635c81816e8a26d524d8c18eae Mon Sep 17 00:00:00 2001 From: LW Date: Sun, 5 Aug 2018 09:40:13 -0700 Subject: [PATCH 13/26] Make use of socket.getaddrinfo() to support IPv6 and passing hostnames as the bind address --- SimpleWebSocketServer/SimpleWebSocketServer.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index 7e0d946..e956e08 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -575,9 +575,10 @@ def _parseMessage(self, byte): class SimpleWebSocketServer(object): def __init__(self, host, port, websocketclass, selectInterval = 0.1): self.websocketclass = websocketclass - self.serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + hostInfo = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP) + self.serversocket = socket.socket(hostInfo[0][0], hostInfo[0][1], hostInfo[0][2]) self.serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.serversocket.bind((host, port)) + self.serversocket.bind(hostInfo[0][4]) self.serversocket.listen(5) self.selectInterval = selectInterval self.connections = {} From a7a2fd9c0390938b84567b76c94e47d8b6a588b0 Mon Sep 17 00:00:00 2001 From: LW Date: Sun, 5 Aug 2018 10:22:58 -0700 Subject: [PATCH 14/26] Fix binding to '' (empty string) and set it to prefer IPv6 (which includes IPv4 via IPv4-mapped IPv6) when that is the case. To force IPv4-only the bind address should be set to '0.0.0.0' --- SimpleWebSocketServer/SimpleWebSocketServer.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index e956e08..cf6be15 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -575,7 +575,16 @@ def _parseMessage(self, byte): class SimpleWebSocketServer(object): def __init__(self, host, port, websocketclass, selectInterval = 0.1): self.websocketclass = websocketclass - hostInfo = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM, socket.IPPROTO_TCP) + + if (host == ''): + host = None + + if host is None: + fam = socket.AF_INET6 + else: + fam = 0 + + hostInfo = socket.getaddrinfo(host, port, fam, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_PASSIVE) self.serversocket = socket.socket(hostInfo[0][0], hostInfo[0][1], hostInfo[0][2]) self.serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.serversocket.bind(hostInfo[0][4]) From ce6c12dce5095ce9dc3f8fc6bedd7e186458addc Mon Sep 17 00:00:00 2001 From: LW Date: Sun, 5 Aug 2018 11:15:11 -0700 Subject: [PATCH 15/26] Allow customized SSL Contexts --- SimpleWebSocketServer/SimpleWebSocketServer.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index 7e0d946..9ced35c 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -679,14 +679,17 @@ def serveforever(self): class SimpleSSLWebSocketServer(SimpleWebSocketServer): - def __init__(self, host, port, websocketclass, certfile, - keyfile, version = ssl.PROTOCOL_TLSv1, selectInterval = 0.1): + def __init__(self, host, port, websocketclass, certfile = None, + keyfile = None, version = ssl.PROTOCOL_TLSv1, selectInterval = 0.1, ssl_context = None): SimpleWebSocketServer.__init__(self, host, port, websocketclass, selectInterval) - self.context = ssl.SSLContext(version) - self.context.load_cert_chain(certfile, keyfile) + if ssl_context is None: + self.context = ssl.SSLContext(version) + self.context.load_cert_chain(certfile, keyfile) + else: + self.context = ssl_context def close(self): super(SimpleSSLWebSocketServer, self).close() From 3cbcaea9f914322e64ba91fbe4f31de5a6aaab59 Mon Sep 17 00:00:00 2001 From: LW Date: Sun, 5 Aug 2018 12:38:26 -0700 Subject: [PATCH 16/26] Send an error on handshake failure --- SimpleWebSocketServer/SimpleWebSocketServer.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index 7e0d946..6deed23 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -53,6 +53,15 @@ def __init__(self, request_text): "Sec-WebSocket-Accept: %(acceptstr)s\r\n\r\n" ) +FAILED_HANDSHAKE_STR = ( + "HTTP/1.1 426 Upgrade Required\r\n" + "Upgrade: WebSocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Content-Type: text/plain\r\n\r\n" + "This service requires use of the WebSocket protocol\r\n" +) + GUID_STR = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' STREAM = 0x0 @@ -267,6 +276,9 @@ def _handleData(self): self.handshaked = True self.handleConnected() except Exception as e: + hStr = FAILED_HANDSHAKE_STR + self._sendBuffer(hStr.encode('ascii'), True) + self.client.close() raise Exception('handshake failed: %s', str(e)) # else do normal data From 86cf74ea3870940ba48b9180b6e87af7fbc0dedd Mon Sep 17 00:00:00 2001 From: John Belmonte Date: Wed, 22 Aug 2018 12:17:35 +0900 Subject: [PATCH 17/26] SimpleWebSocketServer: honor selectInterval=0 Currently for selectInterval=0 case, SimpleWebSocketServer removes the argument completely from the `select()` call, which means "no timeout". For wrapping SimpleWebSocketServer in an async library, it's useful to support a timeout of 0 so that select returns immediately. The cited behavior has been removed, so that selectInterval=0 and selectInterval=None are passed as-is to `select()`. The default value of .1 seconds is unchanged. --- SimpleWebSocketServer/SimpleWebSocketServer.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index 7e0d946..72fc405 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -614,10 +614,7 @@ def serveonce(self): if client.sendq: writers.append(fileno) - if self.selectInterval: - rList, wList, xList = select(self.listeners, writers, self.listeners, self.selectInterval) - else: - rList, wList, xList = select(self.listeners, writers, self.listeners) + rList, wList, xList = select(self.listeners, writers, self.listeners, self.selectInterval) for ready in wList: client = self.connections[ready] From c93fac4e2ab0267c82b8eeb7255ce24bfb3843f6 Mon Sep 17 00:00:00 2001 From: Guangtu Liu Date: Fri, 31 Aug 2018 17:35:27 -0700 Subject: [PATCH 18/26] Update SimpleWebSocketServer.py should send TEXT back if environment is Python2 and data is str. --- SimpleWebSocketServer/SimpleWebSocketServer.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index 7e0d946..1d8e6be 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -370,9 +370,7 @@ def sendMessage(self, data): If data is a unicode object then the frame is sent as Text. If the data is a bytearray object then the frame is sent as Binary. """ - opcode = BINARY - if _check_unicode(data): - opcode = TEXT + opcode = TEXT if isinstance(data, basestring if VER < 3 else str) else BINARY self._sendMessage(False, opcode, data) From 572637101a58e3e25c51aff3e0e934b42ef71558 Mon Sep 17 00:00:00 2001 From: Guangtu Liu Date: Wed, 19 Sep 2018 00:57:23 -0700 Subject: [PATCH 19/26] Update SimpleWebSocketServer.py should send TEXT back if environment is Python2 and data is str. --- SimpleWebSocketServer/SimpleWebSocketServer.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index 1d8e6be..0061c07 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -31,7 +31,7 @@ def _check_unicode(val): if VER >= 3: return isinstance(val, str) else: - return isinstance(val, unicode) + return isinstance(val, basestring) class HTTPRequest(BaseHTTPRequestHandler): def __init__(self, request_text): @@ -370,7 +370,9 @@ def sendMessage(self, data): If data is a unicode object then the frame is sent as Text. If the data is a bytearray object then the frame is sent as Binary. """ - opcode = TEXT if isinstance(data, basestring if VER < 3 else str) else BINARY + opcode = BINARY + if _check_unicode(data): + opcode = TEXT self._sendMessage(False, opcode, data) From d49f2e6aafc53f591e7f4547da4c7311b2058ebb Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 7 Feb 2019 10:51:53 +0800 Subject: [PATCH 20/26] Update SimpleHTTPSServer.py Add __main__ and clean up imports for python2 and 3 --- SimpleWebSocketServer/SimpleHTTPSServer.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/SimpleWebSocketServer/SimpleHTTPSServer.py b/SimpleWebSocketServer/SimpleHTTPSServer.py index 356ab6b..341e1e7 100644 --- a/SimpleWebSocketServer/SimpleHTTPSServer.py +++ b/SimpleWebSocketServer/SimpleHTTPSServer.py @@ -3,10 +3,21 @@ Copyright (c) 2013 Dave P. ''' -import BaseHTTPServer, SimpleHTTPServer import ssl -# openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout cert.pem -httpd = BaseHTTPServer.HTTPServer(('', 443), SimpleHTTPServer.SimpleHTTPRequestHandler) -httpd.socket = ssl.wrap_socket(httpd.socket, server_side=True, certfile='./cert.pem', keyfile='./cert.pem', ssl_version=ssl.PROTOCOL_TLSv1) -httpd.serve_forever() +try: + from BaseHTTPServer import HTTPServer +except: + from http.server import HTTPServer + +try: + from SimpleHTTPServer import SimpleHTTPRequestHandler +except: + from http.server import SimpleHTTPRequestHandler + +if __name__ == "__main__": + # openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout key.pem + httpd = BaseHTTPServer.HTTPServer(('', 443), SimpleHTTPServer.SimpleHTTPRequestHandler) + httpd.socket = ssl.wrap_socket(httpd.socket, server_side=True, certfile='./cert.pem', + keyfile='./key.pem', ssl_version=ssl.PROTOCOL_TLSv1) + httpd.serve_forever() From 4a9defa176919142d4bf738d307fb77fea0a04ad Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 7 Feb 2019 10:53:05 +0800 Subject: [PATCH 21/26] Update SimpleHTTPSServer.py Removed BaseHTTPServer, SimpleHTTPServer --- SimpleWebSocketServer/SimpleHTTPSServer.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SimpleWebSocketServer/SimpleHTTPSServer.py b/SimpleWebSocketServer/SimpleHTTPSServer.py index 341e1e7..bc3ac20 100644 --- a/SimpleWebSocketServer/SimpleHTTPSServer.py +++ b/SimpleWebSocketServer/SimpleHTTPSServer.py @@ -16,8 +16,8 @@ from http.server import SimpleHTTPRequestHandler if __name__ == "__main__": - # openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout key.pem - httpd = BaseHTTPServer.HTTPServer(('', 443), SimpleHTTPServer.SimpleHTTPRequestHandler) - httpd.socket = ssl.wrap_socket(httpd.socket, server_side=True, certfile='./cert.pem', + # openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout key.pem + httpd = HTTPServer(('', 443), SimpleHTTPRequestHandler) + httpd.socket = ssl.wrap_socket(httpd.socket, server_side=True, certfile='./cert.pem', keyfile='./key.pem', ssl_version=ssl.PROTOCOL_TLSv1) - httpd.serve_forever() + httpd.serve_forever() From a6ddef766a4f5fa655c9072107fccc28ecf2b3e3 Mon Sep 17 00:00:00 2001 From: dpallot Date: Thu, 7 Feb 2019 13:52:49 +0800 Subject: [PATCH 22/26] PyPI package setup. --- MANIFEST.in | 6 ++++++ README.md | 2 +- SimpleWebSocketServer/__init__.py | 2 ++ setup.py | 30 +++++++++++++++++++----------- 4 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..7faa742 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,6 @@ +include README.md +include setup.py +include SimpleWebSocketServer/SimpleExampleServer.py +include SimpleWebSocketServer/SimpleHTTPSServer.py +include SimpleWebSocketServer/SimpleWebSocketServer.py +include SimpleWebSocketServer/__init__.py diff --git a/README.md b/README.md index 9944a59..71b485c 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ You can install SimpleWebSocketServer by running the following command... -`sudo pip install git+https://github.com/dpallot/simple-websocket-server.git` +`pip install SimpleWebSocketServer` Or by downloading the repository and running `sudo python setup.py install`. Installation via pip is suggested. diff --git a/SimpleWebSocketServer/__init__.py b/SimpleWebSocketServer/__init__.py index 678693c..c7e52b8 100644 --- a/SimpleWebSocketServer/__init__.py +++ b/SimpleWebSocketServer/__init__.py @@ -1 +1,3 @@ from .SimpleWebSocketServer import * + +name="SimpleWebSocketServer" diff --git a/setup.py b/setup.py index 833b7b3..1131790 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,19 @@ -from distutils.core import setup - -setup( - name='SimpleWebSocketServer', - version='0.1.0', - author='Dave', - packages=['SimpleWebSocketServer'], - url='https://github.com/dpallot/simple-websocket-server/', - description='A Simple Websocket Server written in Python', - long_description=open('README.md').read() -) +from distutils.core import setup + +setup( + name='SimpleWebSocketServer', + version='0.1.1', + author='Dave Pallot', + author_email='d.e.pallot@gmail.com', + packages=['SimpleWebSocketServer'], + url='https://github.com/dpallot/simple-websocket-server/', + description='A Simple Websocket Server written in Python', + long_description_content_type='text/markdown', + long_description=open('README.md').read(), + classifiers=[ + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ], +) From 692f0ae6bd9f35aa627713705a112c21857a0faa Mon Sep 17 00:00:00 2001 From: Levente Szabadkai Date: Wed, 29 Jul 2020 16:22:26 +0200 Subject: [PATCH 23/26] bump TLS version to keep compatibility with chrome Chrome browser started to deprecate TLS1.0 in 2020. bupming the protocol to 1.2 keeps it alive for an other while. --- SimpleWebSocketServer/SimpleWebSocketServer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index 787b538..4800131 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -699,7 +699,7 @@ def serveforever(self): class SimpleSSLWebSocketServer(SimpleWebSocketServer): def __init__(self, host, port, websocketclass, certfile = None, - keyfile = None, version = ssl.PROTOCOL_TLSv1, selectInterval = 0.1, ssl_context = None): + keyfile = None, version = ssl.PROTOCOL_TLSv1_2, selectInterval = 0.1, ssl_context = None): SimpleWebSocketServer.__init__(self, host, port, websocketclass, selectInterval) From 21ebb197fe311da6862b2b51b7bb2d125e8c9f0c Mon Sep 17 00:00:00 2001 From: Geert Linders Date: Tue, 16 Mar 2021 18:58:57 +0100 Subject: [PATCH 24/26] Handle SSLWantReadError and SSLWantWriteErrors on recv and send. --- SimpleWebSocketServer/SimpleWebSocketServer.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index 787b538..2a6f2c5 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -251,7 +251,11 @@ def _handleData(self): # do the HTTP header and handshake if self.handshaked is False: - data = self.client.recv(self.headertoread) + try: + data = self.client.recv(self.headertoread) + except (ssl.SSLWantReadError, ssl.SSLWantWriteError): + # SSL socket not ready to read yet, wait and try again + return if not data: raise Exception('remote socket closed') @@ -283,7 +287,11 @@ def _handleData(self): # else do normal data else: - data = self.client.recv(16384) + try: + data = self.client.recv(16384) + except (ssl.SSLWantReadError, ssl.SSLWantWriteError): + # SSL socket not ready to read yet, wait and try again + return if not data: raise Exception("remote socket closed") @@ -332,6 +340,12 @@ def _sendBuffer(self, buff, send_all = False): already_sent += sent tosend -= sent + except (ssl.SSLWantReadError, ssl.SSLWantWriteError): + # SSL socket not ready to send yet, wait and try again + if send_all: + continue + return buff[already_sent:] + except socket.error as e: # if we have full buffers then wait for them to drain and try again if e.errno in [errno.EAGAIN, errno.EWOULDBLOCK]: From cfa6529272c4066d7fc0a560d74c3b838232371b Mon Sep 17 00:00:00 2001 From: Junhao Liao Date: Sun, 20 Jun 2021 17:13:11 +0800 Subject: [PATCH 25/26] bind on both IPv4 and IPv6 localhost --- SimpleWebSocketServer/SimpleWebSocketServer.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/SimpleWebSocketServer/SimpleWebSocketServer.py b/SimpleWebSocketServer/SimpleWebSocketServer.py index 787b538..0b2cfa9 100644 --- a/SimpleWebSocketServer/SimpleWebSocketServer.py +++ b/SimpleWebSocketServer/SimpleWebSocketServer.py @@ -599,6 +599,12 @@ def __init__(self, host, port, websocketclass, selectInterval = 0.1): hostInfo = socket.getaddrinfo(host, port, fam, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_PASSIVE) self.serversocket = socket.socket(hostInfo[0][0], hostInfo[0][1], hostInfo[0][2]) self.serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + if host is None: + # bind on both IPv4 and IPv6 localhost + # if we don't explicitly set this, the behaviour isn't guranteed on some platforms. e.g. Windows + self.serversocket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) + self.serversocket.bind(hostInfo[0][4]) self.serversocket.listen(5) self.selectInterval = selectInterval From d47b1437bf1144fe80e6ed56d2d580f627a6f4f8 Mon Sep 17 00:00:00 2001 From: ahadley1124 <49847299+ahadley1124@users.noreply.github.com> Date: Wed, 23 Feb 2022 12:07:30 -0500 Subject: [PATCH 26/26] Fixed incorrect spelling --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 71b485c..7d699f0 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ Chat Server (open up multiple *websocket.html* files) openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout key.pem -2) Run the secure TSL/SSL server (in this case the cert.pem file is in the same directory) +2) Run the secure TLS/SSL server (in this case the cert.pem file is in the same directory) python SimpleExampleServer.py --example chat --ssl 1 --cert ./cert.pem