diff options
Diffstat (limited to 'websockify')
-rw-r--r-- | websockify/auth_plugins.py | 12 | ||||
-rw-r--r-- | websockify/sysloghandler.py | 2 | ||||
-rw-r--r-- | websockify/token_plugins.py | 71 | ||||
-rw-r--r-- | websockify/websocket.py | 186 | ||||
-rw-r--r-- | websockify/websocketproxy.py | 63 | ||||
-rw-r--r-- | websockify/websocketserver.py | 14 | ||||
-rw-r--r-- | websockify/websockifyserver.py | 91 |
7 files changed, 221 insertions, 218 deletions
diff --git a/websockify/auth_plugins.py b/websockify/auth_plugins.py index 2d636c2..36fac52 100644 --- a/websockify/auth_plugins.py +++ b/websockify/auth_plugins.py @@ -1,4 +1,4 @@ -class BasePlugin(object): +class BasePlugin(): def __init__(self, src=None): self.source = src @@ -15,7 +15,7 @@ class AuthenticationError(Exception): if log_msg is None: log_msg = response_msg - super(AuthenticationError, self).__init__('%s %s' % (self.code, log_msg)) + super().__init__('%s %s' % (self.code, log_msg)) class InvalidOriginError(AuthenticationError): @@ -23,13 +23,13 @@ class InvalidOriginError(AuthenticationError): self.expected_origin = expected self.actual_origin = actual - super(InvalidOriginError, self).__init__( + super().__init__( response_msg='Invalid Origin', log_msg="Invalid Origin Header: Expected one of " "%s, got '%s'" % (expected, actual)) -class BasicHTTPAuth(object): +class BasicHTTPAuth(): """Verifies Basic Auth headers. Specify src as username:password""" def __init__(self, src=None): @@ -76,7 +76,7 @@ class BasicHTTPAuth(object): raise AuthenticationError(response_code=401, response_headers={'WWW-Authenticate': 'Basic realm="Websockify"'}) -class ExpectOrigin(object): +class ExpectOrigin(): def __init__(self, src=None): if src is None: self.source = [] @@ -88,7 +88,7 @@ class ExpectOrigin(object): if origin is None or origin not in self.source: raise InvalidOriginError(expected=self.source, actual=origin) -class ClientCertCNAuth(object): +class ClientCertCNAuth(): """Verifies client by SSL certificate. Specify src as whitespace separated list of common names.""" def __init__(self, src=None): diff --git a/websockify/sysloghandler.py b/websockify/sysloghandler.py index 92ca66f..37ee9dd 100644 --- a/websockify/sysloghandler.py +++ b/websockify/sysloghandler.py @@ -44,7 +44,7 @@ class WebsockifySysLogHandler(handlers.SysLogHandler): self._legacy = True
self._head_fmt = self._legacy_head_fmt
- handlers.SysLogHandler.__init__(self, address, facility, socktype)
+ super().__init__(address, facility, socktype)
def emit(self, record):
diff --git a/websockify/token_plugins.py b/websockify/token_plugins.py index 1f01e0a..0484023 100644 --- a/websockify/token_plugins.py +++ b/websockify/token_plugins.py @@ -1,9 +1,9 @@ -from __future__ import print_function import os import sys +import time import re -class BasePlugin(object): +class BasePlugin(): def __init__(self, src): self.source = src @@ -16,7 +16,7 @@ class ReadOnlyTokenFile(BasePlugin): # token: host:port # or a directory of such files def __init__(self, *args, **kwargs): - super(ReadOnlyTokenFile, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self._targets = None def _load_targets(self): @@ -35,7 +35,7 @@ class ReadOnlyTokenFile(BasePlugin): tok, target = re.split(':\s', line) self._targets[tok] = target.strip().rsplit(':', 1) except ValueError: - print >>sys.stderr, "Syntax error in %s on line %d" % (self.source, index) + print("Syntax error in %s on line %d" % (self.source, index), file=sys.stderr) index += 1 def lookup(self, token): @@ -58,7 +58,7 @@ class TokenFile(ReadOnlyTokenFile): def lookup(self, token): self._load_targets() - return super(TokenFile, self).lookup(token) + return super().lookup(token) class BaseTokenAPI(BasePlugin): @@ -129,6 +129,18 @@ class JWTTokenApi(BasePlugin): token = jwt.JWT(key=key, jwt=token.claims) parsed = json.loads(token.claims) + + if 'nbf' in parsed: + # Not Before is present, so we need to check it + if time.time() < parsed['nbf']: + print('Token can not be used yet!', file=sys.stderr) + return None + + if 'exp' in parsed: + # Expiration time is present, so we need to check it + if time.time() > parsed['exp']: + print('Token has expired!', file=sys.stderr) + return None return (parsed['host'], parsed['port']) except Exception as e: @@ -138,32 +150,63 @@ class JWTTokenApi(BasePlugin): print("package jwcrypto not found, are you sure you've installed it correctly?", file=sys.stderr) return None -class TokenRedis(object): +class TokenRedis(): + """ + The TokenRedis plugin expects the format of the data in a form of json. + + Prepare data with: + redis-cli set hello '{"host":"127.0.0.1:5000"}' + + Verify with: + redis-cli --raw get hello + + Spawn a test "server" using netcat + nc -l 5000 -v + + Note: you have to install also the 'redis' and 'simplejson' modules + pip install redis simplejson + """ def __init__(self, src): - self._server, self._port = src.split(":") + try: + # import those ahead of time so we provide error earlier + import redis + import simplejson + self._server, self._port = src.split(":") + print("TokenRedis backend initilized (%s:%s)" % + (self._server, self._port), file=sys.stderr) + except ValueError: + print("The provided --token-source='%s' is not in an expected format <host>:<port>" % + src, file=sys.stderr) + sys.exit() + except ImportError: + print("package redis or simplejson not found, are you sure you've installed them correctly?", file=sys.stderr) + sys.exit() def lookup(self, token): try: import redis import simplejson - except ImportError as e: + except ImportError: print("package redis or simplejson not found, are you sure you've installed them correctly?", file=sys.stderr) - return None + sys.exit() - client = redis.Redis(host=self._server,port=self._port) + print("resolving token '%s'" % token, file=sys.stderr) + client = redis.Redis(host=self._server, port=self._port) stuff = client.get(token) if stuff is None: return None else: - combo = simplejson.loads(stuff.decode("utf-8")) + responseStr = stuff.decode("utf-8") + print("response from redis : %s" % responseStr, file=sys.stderr) + combo = simplejson.loads(responseStr) (host, port) = combo["host"].split(':') - port = port.encode('ascii','ignore') - return [ host, port ] + print("host: %s, port: %s" % (host,port), file=sys.stderr) + return [host, port] class UnixDomainSocketDirectory(BasePlugin): def __init__(self, *args, **kwargs): - super(UnixDomainSocketDirectory, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self._dir_path = os.path.abspath(self.source) def lookup(self, token): diff --git a/websockify/websocket.py b/websockify/websocket.py index c8226cc..f0db3ba 100644 --- a/websockify/websocket.py +++ b/websockify/websocket.py @@ -22,6 +22,7 @@ import ssl import struct from base64 import b64encode from hashlib import sha1 +from urllib.parse import urlparse try: import numpy @@ -30,25 +31,10 @@ except ImportError: warnings.warn("no 'numpy' module, HyBi protocol will be slower") numpy = None -# python 3.0 differences -try: - from urllib.parse import urlparse -except ImportError: - from urlparse import urlparse - -# SSLWant*Error is 2.7.9+ -try: - class WebSocketWantReadError(ssl.SSLWantReadError): - pass - class WebSocketWantWriteError(ssl.SSLWantWriteError): - pass -except AttributeError: - class WebSocketWantReadError(OSError): - def __init__(self): - OSError.__init__(self, errno.EWOULDBLOCK) - class WebSocketWantWriteError(OSError): - def __init__(self): - OSError.__init__(self, errno.EWOULDBLOCK) +class WebSocketWantReadError(ssl.SSLWantReadError): + pass +class WebSocketWantWriteError(ssl.SSLWantWriteError): + pass class WebSocket(object): """WebSocket protocol socket like class. @@ -87,11 +73,13 @@ class WebSocket(object): self._state = "new" - self._partial_msg = ''.encode("ascii") + self._partial_msg = b'' - self._recv_buffer = ''.encode("ascii") + self._recv_buffer = b'' self._recv_queue = [] - self._send_buffer = ''.encode("ascii") + self._send_buffer = b'' + + self._previous_sendmsg = None self._sent_close = False self._received_close = False @@ -164,9 +152,7 @@ class WebSocket(object): self._key = '' for i in range(16): self._key += chr(random.randrange(256)) - if sys.hexversion >= 0x3000000: - self._key = bytes(self._key, "latin-1") - self._key = b64encode(self._key).decode("ascii") + self._key = b64encode(self._key.encode("latin-1")).decode("ascii") path = uri.path if not path: @@ -196,10 +182,10 @@ class WebSocket(object): if not self._recv(): raise Exception("Socket closed unexpectedly") - if self._recv_buffer.find('\r\n\r\n'.encode("ascii")) == -1: + if self._recv_buffer.find(b'\r\n\r\n') == -1: raise WebSocketWantReadError - (request, self._recv_buffer) = self._recv_buffer.split('\r\n'.encode("ascii"), 1) + (request, self._recv_buffer) = self._recv_buffer.split(b'\r\n', 1) request = request.decode("latin-1") words = request.split() @@ -208,7 +194,7 @@ class WebSocket(object): if words[1] != "101": raise Exception("WebSocket request denied: %s" % " ".join(words[1:])) - (headers, self._recv_buffer) = self._recv_buffer.split('\r\n\r\n'.encode("ascii"), 1) + (headers, self._recv_buffer) = self._recv_buffer.split(b'\r\n\r\n', 1) headers = headers.decode('latin-1') + '\r\n' headers = email.message_from_string(headers) @@ -254,8 +240,8 @@ class WebSocket(object): the value "websocket" in such cases. WebSocketWantWriteError can be raised if the response cannot be - sent right away. Repeated calls to accept() does not need to - retain the arguments. + sent right away. accept() must be called again once more space + is available using the same arguments. """ # This is a state machine in order to handle @@ -419,8 +405,12 @@ class WebSocket(object): data from other calls, or split it over multiple messages. WebSocketWantWriteError can be raised if there is insufficient - space in the underlying socket. + space in the underlying socket. send() must be called again + once more space is available using the same arguments. """ + if len(bytes) == 0: + return 0 + return self.sendmsg(bytes) def sendmsg(self, msg): @@ -431,23 +421,81 @@ class WebSocket(object): single WebSocket message. WebSocketWantWriteError can be raised if there is insufficient - space in the underlying socket. + space in the underlying socket. sendmsg() must be called again + once more space is available using the same arguments. """ - if not self._sent_close: - # Only called to flush? - if msg: - self._sendmsg(0x2, msg) + if not isinstance(msg, bytes): + raise TypeError + + if self._sent_close: + return 0 + + if self._previous_sendmsg is not None: + if self._previous_sendmsg != msg: + raise ValueError + + self._flush() + self._previous_sendmsg = None + + return len(msg) + + try: + self._sendmsg(0x2, msg) + except WebSocketWantWriteError: + self._previous_sendmsg = msg + raise - self._flush() return len(msg) - def ping(self, data=''.encode('ascii')): - """Write a ping message to the WebSocket.""" - self._sendmsg(0x9, data) + def ping(self, data=b''): + """Write a ping message to the WebSocket + + WebSocketWantWriteError can be raised if there is insufficient + space in the underlying socket. ping() must be called again once + more space is available using the same arguments. + """ + if not isinstance(data, bytes): + raise TypeError + + if self._previous_sendmsg is not None: + if self._previous_sendmsg != data: + raise ValueError + + self._flush() + self._previous_sendmsg = None + + return + + try: + self._sendmsg(0x9, data) + except WebSocketWantWriteError: + self._previous_sendmsg = data + raise - def pong(self, data=''.encode('ascii')): - """Write a pong message to the WebSocket.""" - self._sendmsg(0xA, data) + def pong(self, data=b''): + """Write a pong message to the WebSocket + + WebSocketWantWriteError can be raised if there is insufficient + space in the underlying socket. pong() must be called again once + more space is available using the same arguments. + """ + if not isinstance(data, bytes): + raise TypeError + + if self._previous_sendmsg is not None: + if self._previous_sendmsg != data: + raise ValueError + + self._flush() + self._previous_sendmsg = None + + return + + try: + self._sendmsg(0xA, data) + except WebSocketWantWriteError: + self._previous_sendmsg = data + raise def shutdown(self, how, code=1000, reason=None): """Gracefully terminate the WebSocket connection. @@ -459,7 +507,9 @@ class WebSocket(object): ignored. WebSocketWantWriteError can be raised if there is insufficient - space in the underlying socket for the close message. + space in the underlying socket for the close message. shutdown() + must be called again once more space is available using the same + arguments. The how argument is currently ignored. """ @@ -476,7 +526,7 @@ class WebSocket(object): self._sent_close = True - msg = ''.encode('ascii') + msg = b'' if code is not None: msg += struct.pack(">H", code) if reason is not None: @@ -491,7 +541,9 @@ class WebSocket(object): a close message to the peer. WebSocketWantWriteError can be raised if there is insufficient - space in the underlying socket for the close message. + space in the underlying socket for the close message. close() + must be called again once more space is available using the same + arguments. """ self.shutdown(socket.SHUT_RDWR, code, reason) self._close() @@ -503,16 +555,9 @@ class WebSocket(object): while True: try: data = self.socket.recv(4096) - except (socket.error, OSError): - exc = sys.exc_info()[1] - if hasattr(exc, 'errno'): - err = exc.errno - else: - err = exc[0] - - if err == errno.EWOULDBLOCK: + except OSError as exc: + if exc.errno == errno.EWOULDBLOCK: raise WebSocketWantReadError - raise if len(data) == 0: @@ -569,7 +614,7 @@ class WebSocket(object): if frame["fin"]: msg = self._partial_msg - self._partial_msg = ''.decode("ascii") + self._partial_msg = b'' return msg elif frame["opcode"] == 0x1: self.shutdown(socket.SHUT_RDWR, 1003, "Unsupported: Text frames are not supported") @@ -644,16 +689,9 @@ class WebSocket(object): try: sent = self.socket.send(self._send_buffer) - except (socket.error, OSError): - exc = sys.exc_info()[1] - if hasattr(exc, 'errno'): - err = exc.errno - else: - err = exc[0] - - if err == errno.EWOULDBLOCK: + except OSError as exc: + if exc.errno == errno.EWOULDBLOCK: raise WebSocketWantWriteError - raise self._send_buffer = self._send_buffer[sent:] @@ -679,11 +717,9 @@ class WebSocket(object): def _sendmsg(self, opcode, msg): # Sends a standard data message if self.client: - mask = '' + mask = b'' for i in range(4): - mask += chr(random.randrange(256)) - if sys.hexversion >= 0x3000000: - mask = bytes(mask, "latin-1") + mask += random.randrange(256) frame = self._encode_hybi(opcode, msg, mask) else: frame = self._encode_hybi(opcode, msg) @@ -705,7 +741,7 @@ class WebSocket(object): plen = len(buf) pstart = 0 pend = plen - b = c = ''.encode('ascii') + b = c = b'' if plen >= 4: dtype=numpy.dtype('<u4') if sys.byteorder == 'big': @@ -713,7 +749,7 @@ class WebSocket(object): mask = numpy.frombuffer(mask, dtype, count=1) data = numpy.frombuffer(buf, dtype, count=int(plen / 4)) #b = numpy.bitwise_xor(data, mask).data - b = numpy.bitwise_xor(data, mask).tostring() + b = numpy.bitwise_xor(data, mask).tobytes() if plen % 4: dtype=numpy.dtype('B') @@ -722,17 +758,15 @@ class WebSocket(object): mask = numpy.frombuffer(mask, dtype, count=(plen % 4)) data = numpy.frombuffer(buf, dtype, offset=plen - (plen % 4), count=(plen % 4)) - c = numpy.bitwise_xor(data, mask).tostring() + c = numpy.bitwise_xor(data, mask).tobytes() return b + c else: # Slower fallback - if sys.hexversion < 0x3000000: - mask = [ ord(c) for c in mask ] data = array.array('B') - data.fromstring(buf) + data.frombytes(buf) for i in range(len(data)): data[i] ^= mask[i % 4] - return data.tostring() + return data.tobytes() def _encode_hybi(self, opcode, buf, mask_key=None, fin=True): """ Encode a HyBi style WebSocket frame. diff --git a/websockify/websocketproxy.py b/websockify/websocketproxy.py index d3c7130..09d7882 100644 --- a/websockify/websocketproxy.py +++ b/websockify/websocketproxy.py @@ -12,24 +12,13 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates ''' import signal, socket, optparse, time, os, sys, subprocess, logging, errno, ssl -try: - from socketserver import ThreadingMixIn -except ImportError: - from SocketServer import ThreadingMixIn - -try: - from http.server import HTTPServer -except ImportError: - from BaseHTTPServer import HTTPServer +from socketserver import ThreadingMixIn +from http.server import HTTPServer import select from websockify import websockifyserver from websockify import auth_plugins as auth -try: - from urllib.parse import parse_qs, urlparse -except ImportError: - from cgi import parse_qs - from urlparse import urlparse +from urllib.parse import parse_qs, urlparse class ProxyRequestHandler(websockifyserver.WebSockifyRequestHandler): @@ -293,6 +282,7 @@ class WebSocketProxy(websockifyserver.WebSockifyServer): wsdir = os.path.dirname(sys.argv[0]) rebinder_path = [os.path.join(wsdir, "..", "lib"), os.path.join(wsdir, "..", "lib", "websockify"), + os.path.join(wsdir, ".."), wsdir] self.rebinder = None @@ -318,7 +308,7 @@ class WebSocketProxy(websockifyserver.WebSockifyServer): "REBIND_OLD_PORT": str(kwargs['listen_port']), "REBIND_NEW_PORT": str(self.target_port)}) - websockifyserver.WebSockifyServer.__init__(self, RequestHandlerClass, *args, **kwargs) + super().__init__(RequestHandlerClass, *args, **kwargs) def run_wrap_cmd(self): self.msg("Starting '%s'", " ".join(self.wrap_cmd)) @@ -394,35 +384,15 @@ def _subprocess_setup(): signal.signal(signal.SIGPIPE, signal.SIG_DFL) -try : - # First try SSL options for Python 3.4 and above - SSL_OPTIONS = { - 'default': ssl.OP_ALL, - 'tlsv1_1': ssl.PROTOCOL_TLS | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | - ssl.OP_NO_TLSv1, - 'tlsv1_2': ssl.PROTOCOL_TLS | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | - ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1, - 'tlsv1_3': ssl.PROTOCOL_TLS | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | - ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_TLSv1_2, - } -except AttributeError: - try: - # Python 3.3 uses a different scheme for SSL options - # tlsv1_3 is not supported on older Python versions - SSL_OPTIONS = { - 'default': ssl.OP_ALL, - 'tlsv1_1': ssl.PROTOCOL_TLSv1 | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | - ssl.OP_NO_TLSv1, - 'tlsv1_2': ssl.PROTOCOL_TLSv1 | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | - ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1, - } - except AttributeError: - # Python 2.6 does not support TLS v1.2, and uses a different scheme - # for SSL options - SSL_OPTIONS = { - 'default': ssl.PROTOCOL_SSLv23, - 'tlsv1_1': ssl.PROTOCOL_TLSv1, - } +SSL_OPTIONS = { + 'default': ssl.OP_ALL, + 'tlsv1_1': ssl.PROTOCOL_SSLv23 | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | + ssl.OP_NO_TLSv1, + 'tlsv1_2': ssl.PROTOCOL_SSLv23 | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | + ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1, + 'tlsv1_3': ssl.PROTOCOL_SSLv23 | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | + ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_TLSv1_2, +} def select_ssl_version(version): """Returns SSL options for the most secure TSL version available on this @@ -768,14 +738,13 @@ class LibProxyServer(ThreadingMixIn, HTTPServer): if web: os.chdir(web) - HTTPServer.__init__(self, (listen_host, listen_port), - RequestHandlerClass) + super().__init__((listen_host, listen_port), RequestHandlerClass) def process_request(self, request, client_address): """Override process_request to implement a counter""" self.handler_id += 1 - ThreadingMixIn.process_request(self, request, client_address) + super().process_request(request, client_address) if __name__ == '__main__': diff --git a/websockify/websocketserver.py b/websockify/websocketserver.py index d2e02c9..9088fe9 100644 --- a/websockify/websocketserver.py +++ b/websockify/websocketserver.py @@ -8,12 +8,7 @@ Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) ''' import sys - -# python 3.0 differences -try: - from http.server import BaseHTTPRequestHandler, HTTPServer -except ImportError: - from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer +from http.server import BaseHTTPRequestHandler, HTTPServer from websockify.websocket import WebSocket, WebSocketWantReadError, WebSocketWantWriteError @@ -42,12 +37,7 @@ class WebSocketRequestHandlerMixIn: self._real_do_GET = self.do_GET self.do_GET = self._websocket_do_GET try: - # super() only works for new style classes - if issubclass(WebSocketRequestHandlerMixIn, object): - super(WebSocketRequestHandlerMixIn, self).handle_one_request() - else: - # Assume handle_one_request() hasn't been overriden - BaseHTTPRequestHandler.handle_one_request(self) + super().handle_one_request() finally: self.do_GET = self._real_do_GET diff --git a/websockify/websockifyserver.py b/websockify/websockifyserver.py index c801725..0199e42 100644 --- a/websockify/websockifyserver.py +++ b/websockify/websockifyserver.py @@ -14,19 +14,7 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates import os, sys, time, errno, signal, socket, select, logging import multiprocessing - -# Imports that vary by python version - -# python 3.0 differences -if sys.hexversion > 0x3000000: - s2b = lambda s: s.encode('latin_1') -else: - s2b = lambda s: s # No-op - -try: - from http.server import SimpleHTTPRequestHandler -except ImportError: - from SimpleHTTPServer import SimpleHTTPRequestHandler +from http.server import SimpleHTTPRequestHandler # Degraded functionality if these imports are missing for mod, msg in [('ssl', 'TLS/SSL/wss is disabled'), @@ -46,7 +34,7 @@ from websockify.websocketserver import WebSocketRequestHandlerMixIn class CompatibleWebSocket(WebSocket): def select_subprotocol(self, protocols): - # Handle old websockify clients that still specifiy a sub-protocol + # Handle old websockify clients that still specify a sub-protocol if 'binary' in protocols: return 'binary' else: @@ -96,7 +84,7 @@ class WebSockifyRequestHandler(WebSocketRequestHandlerMixIn, SimpleHTTPRequestHa if self.logger is None: self.logger = WebSockifyServer.get_logger() - SimpleHTTPRequestHandler.__init__(self, req, addr, server) + super().__init__(req, addr, server) def log_message(self, format, *args): self.logger.info("%s - - [%s] %s" % (self.client_address[0], self.log_date_time_string(), format % args)) @@ -146,20 +134,14 @@ class WebSockifyRequestHandler(WebSocketRequestHandlerMixIn, SimpleHTTPRequestHa self.rec.write("'{{{0}{{{1}',\n".format(tdelta, bufstr)) self.send_parts.append(buf) - # Flush any previously queued data - try: - self.request.sendmsg('') - except WebSocketWantWriteError: - return True - while self.send_parts: # Send pending frames - buf = self.send_parts.pop(0) try: - self.request.sendmsg(buf) + self.request.sendmsg(self.send_parts[0]) except WebSocketWantWriteError: self.print_traffic("<.") return True + self.send_parts.pop(0) self.print_traffic("<") return False @@ -218,7 +200,7 @@ class WebSockifyRequestHandler(WebSocketRequestHandlerMixIn, SimpleHTTPRequestHa self.validate_connection() self.auth_connection() - WebSocketRequestHandlerMixIn.handle_upgrade(self) + super().handle_upgrade() def handle_websocket(self): # Indicate to server that a Websocket upgrade was done @@ -270,13 +252,13 @@ class WebSockifyRequestHandler(WebSocketRequestHandlerMixIn, SimpleHTTPRequestHa if self.only_upgrade: self.send_error(405, "Method Not Allowed") else: - SimpleHTTPRequestHandler.do_GET(self) + super().do_GET() def list_directory(self, path): if self.file_only: self.send_error(404, "No such file") else: - return SimpleHTTPRequestHandler.list_directory(self, path) + return super().list_directory(path) def new_websocket_client(self): """ Do something with a WebSockets client connection. """ @@ -297,13 +279,13 @@ class WebSockifyRequestHandler(WebSocketRequestHandlerMixIn, SimpleHTTPRequestHa if self.only_upgrade: self.send_error(405, "Method Not Allowed") else: - SimpleHTTPRequestHandler.do_HEAD(self) + super().do_HEAD() def finish(self): if self.rec: self.rec.write("'EOF'];\n") self.rec.close() - SimpleHTTPRequestHandler.finish(self) + super().finish() def handle(self): # When using run_once, we have a single process, so @@ -312,14 +294,14 @@ class WebSockifyRequestHandler(WebSocketRequestHandlerMixIn, SimpleHTTPRequestHa if self.run_once: self.handle_one_request() else: - SimpleHTTPRequestHandler.handle(self) + super().handle() def log_request(self, code='-', size='-'): if self.verbose: - SimpleHTTPRequestHandler.log_request(self, code, size) + super().log_request(code, size) -class WebSockifyServer(object): +class WebSockifyServer(): """ WebSockets server class. As an alternative, the standard library SocketServer can be used @@ -559,7 +541,7 @@ class WebSockifyServer(object): if not handshake: raise self.EClose("") - elif handshake[0] in ("\x16", "\x80", 22, 128): + elif handshake[0] in (22, 128): # SSL wrap the connection if not ssl: raise self.EClose("SSL connection but no 'ssl' module") @@ -568,32 +550,21 @@ class WebSockifyServer(object): % self.cert) retsock = None try: - if (hasattr(ssl, 'create_default_context') - and callable(ssl.create_default_context)): - # create new-style SSL wrapping for extended features - context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) - if self.ssl_ciphers is not None: - context.set_ciphers(self.ssl_ciphers) - context.options = self.ssl_options - context.load_cert_chain(certfile=self.cert, keyfile=self.key, password=self.key_password) - if self.verify_client: - context.verify_mode = ssl.CERT_REQUIRED - if self.cafile: - context.load_verify_locations(cafile=self.cafile) - else: - context.set_default_verify_paths() - retsock = context.wrap_socket( - sock, - server_side=True) - else: - if self.verify_client: - raise self.EClose("Client certificate verification requested, but this Python is too old.") - # new-style SSL wrapping is not needed, using to old style - retsock = ssl.wrap_socket( - sock, - server_side=True, - certfile=self.cert, - keyfile=self.key) + # create new-style SSL wrapping for extended features + context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + if self.ssl_ciphers is not None: + context.set_ciphers(self.ssl_ciphers) + context.options = self.ssl_options + context.load_cert_chain(certfile=self.cert, keyfile=self.key, password=self.key_password) + if self.verify_client: + context.verify_mode = ssl.CERT_REQUIRED + if self.cafile: + context.load_verify_locations(cafile=self.cafile) + else: + context.set_default_verify_paths() + retsock = context.wrap_socket( + sock, + server_side=True) except ssl.SSLError: _, x, _ = sys.exc_info() if x.args[0] == ssl.SSL_ERROR_EOF: @@ -729,10 +700,6 @@ class WebSockifyServer(object): if self.listen_fd != None: lsock = socket.fromfd(self.listen_fd, socket.AF_INET, socket.SOCK_STREAM) - if sys.hexversion < 0x3000000: - # For python 2 we have to wrap the "raw" socket into a socket object, - # otherwise ssl wrap_socket doesn't work. - lsock = socket.socket(_sock=lsock) else: lsock = self.socket(self.listen_host, self.listen_port, False, self.prefer_ipv6, |