From d19312ba751f03bc249ba6bdc096adf291d00d22 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 8 Jun 2025 22:20:49 +0900 Subject: [PATCH 01/20] tox: Restore pep8 target ... so that we can run flake8 check more easily. Note that the check is not yet enforced in CI. --- tox.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index 8c629cf..3d175eb 100644 --- a/tox.ini +++ b/tox.ini @@ -12,6 +12,6 @@ deps = -r{toxinidir}/test-requirements.txt # At some point we should enable this since tox expects it to exist but # the code will need pep8ising first. -#[testenv:pep8] -#commands = flake8 -#dep = flake8 +[testenv:pep8] +commands = flake8 +deps = flake8 From 12dd0c738e50ee4c1236176cb099867ce0d6bcd6 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 8 Jun 2025 22:12:55 +0900 Subject: [PATCH 02/20] Drop trailing semicolons --- websockify/sysloghandler.py | 2 +- websockify/websocket.py | 10 +++++----- websockify/websocketproxy.py | 2 +- websockify/websockifyserver.py | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/websockify/sysloghandler.py b/websockify/sysloghandler.py index 8b344a1..2b5f32f 100644 --- a/websockify/sysloghandler.py +++ b/websockify/sysloghandler.py @@ -64,7 +64,7 @@ class WebsockifySysLogHandler(handlers.SysLogHandler): pri = self.encodePriority(self.facility, self.mapPriority(record.levelname)) - timestamp = time.strftime(self._timestamp_fmt, time.gmtime()); + timestamp = time.strftime(self._timestamp_fmt, time.gmtime()) hostname = socket.gethostname()[:self._max_hostname] diff --git a/websockify/websocket.py b/websockify/websocket.py index 1bed8cc..3bfb594 100644 --- a/websockify/websocket.py +++ b/websockify/websocket.py @@ -118,7 +118,7 @@ class WebSocket: connect() must retain the same arguments. """ - self.client = True; + self.client = True uri = urlparse(uri) @@ -206,7 +206,7 @@ class WebSocket: accept = headers.get('Sec-WebSocket-Accept') if accept is None: - raise Exception("Missing Sec-WebSocket-Accept header"); + raise Exception("Missing Sec-WebSocket-Accept header") expected = sha1((self._key + self.GUID).encode("ascii")).digest() expected = b64encode(expected).decode("ascii") @@ -214,7 +214,7 @@ class WebSocket: del self._key if accept != expected: - raise Exception("Invalid Sec-WebSocket-Accept header"); + raise Exception("Invalid Sec-WebSocket-Accept header") self.protocol = headers.get('Sec-WebSocket-Protocol') if len(protocols) == 0: @@ -258,7 +258,7 @@ class WebSocket: ver = headers.get('Sec-WebSocket-Version') if ver is None: - raise Exception("Missing Sec-WebSocket-Version header"); + raise Exception("Missing Sec-WebSocket-Version header") # HyBi-07 report version 7 # HyBi-08 - HyBi-12 report version 8 @@ -270,7 +270,7 @@ class WebSocket: key = headers.get('Sec-WebSocket-Key') if key is None: - raise Exception("Missing Sec-WebSocket-Key header"); + raise Exception("Missing Sec-WebSocket-Key header") # Generate the hash value for the accept header accept = sha1((key + self.GUID).encode("ascii")).digest() diff --git a/websockify/websocketproxy.py b/websockify/websocketproxy.py index 5afec81..70da60d 100644 --- a/websockify/websocketproxy.py +++ b/websockify/websocketproxy.py @@ -655,7 +655,7 @@ def websockify_init(): opts.wrap_cmd = None if not websockifyserver.ssl and opts.ssl_target: - parser.error("SSL target requested and Python SSL module not loaded."); + parser.error("SSL target requested and Python SSL module not loaded.") if opts.ssl_only and not os.path.exists(opts.cert): parser.error("SSL only and %s not found" % opts.cert) diff --git a/websockify/websockifyserver.py b/websockify/websockifyserver.py index c5ba833..95cb451 100644 --- a/websockify/websockifyserver.py +++ b/websockify/websockifyserver.py @@ -454,7 +454,7 @@ class WebSockifyServer(): if connect and not (port or unix_socket): raise Exception("Connect mode requires a port") if use_ssl and not ssl: - raise Exception("SSL socket requested but Python SSL module not loaded."); + raise Exception("SSL socket requested but Python SSL module not loaded.") if not connect and use_ssl: raise Exception("SSL only supported in connect mode (for now)") if not connect: From 9ffe1493dab10e9e6eda99d1595529ade6d4519e Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 8 Jun 2025 22:14:14 +0900 Subject: [PATCH 03/20] Use print function, not print statement print statement is no longer available in Python 3. --- tests/load.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/load.py b/tests/load.py index 710b593..548d47f 100755 --- a/tests/load.py +++ b/tests/load.py @@ -26,7 +26,7 @@ class WebSocketLoad(WebSockifyRequestHandler): max_packet_size = 10000 def new_websocket_client(self): - print "Prepopulating random array" + print("Prepopulating random array") self.rand_array = [] for i in range(0, self.max_packet_size): self.rand_array.append(random.randint(0, 9)) @@ -37,7 +37,7 @@ class WebSocketLoad(WebSockifyRequestHandler): self.responder(self.request) - print "accumulated errors:", self.errors + print("accumulated errors:", self.errors) self.errors = 0 def responder(self, client): @@ -57,7 +57,7 @@ class WebSocketLoad(WebSockifyRequestHandler): err = self.check(frames) if err: self.errors = self.errors + 1 - print err + print(err) if closed: break @@ -106,7 +106,7 @@ class WebSocketLoad(WebSockifyRequestHandler): length = int(length) chksum = int(chksum) except ValueError: - print "\n" + repr(data) + "" + print("\n" + repr(data) + "") err += "Invalid data format\n" continue From e92a6ddf42df9caac0f826d9489f171743bb2be1 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 8 Jun 2025 22:15:27 +0900 Subject: [PATCH 04/20] Fix conflicting test case name ... and fix the wrong format of Sec-WebSocket-Protocol header in test case. --- tests/test_websocket.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_websocket.py b/tests/test_websocket.py index 8ee44f9..42d658d 100644 --- a/tests/test_websocket.py +++ b/tests/test_websocket.py @@ -81,7 +81,7 @@ class AcceptTestCase(unittest.TestCase): ws.accept(sock, {'upgrade': 'websocket', 'Sec-WebSocket-Version': '13', 'Sec-WebSocket-Key': 'DKURYVK9cRFul1vOZVA56Q==', - 'Sec-WebSocket-Protocol': 'foobar gazonk'}) + 'Sec-WebSocket-Protocol': 'foobar,gazonk'}) self.assertEqual(sock.data[:13], b'HTTP/1.1 101 ') self.assertTrue(b'\r\nSec-WebSocket-Protocol: gazonk\r\n' in sock.data) @@ -101,9 +101,9 @@ class AcceptTestCase(unittest.TestCase): sock, {'upgrade': 'websocket', 'Sec-WebSocket-Version': '13', 'Sec-WebSocket-Key': 'DKURYVK9cRFul1vOZVA56Q==', - 'Sec-WebSocket-Protocol': 'foobar gazonk'}) + 'Sec-WebSocket-Protocol': 'foobar,gazonk'}) - def test_protocol(self): + def test_unsupported_protocol(self): class ProtoSocket(websocket.WebSocket): def select_subprotocol(self, protocol): return 'oddball' @@ -114,7 +114,7 @@ class AcceptTestCase(unittest.TestCase): sock, {'upgrade': 'websocket', 'Sec-WebSocket-Version': '13', 'Sec-WebSocket-Key': 'DKURYVK9cRFul1vOZVA56Q==', - 'Sec-WebSocket-Protocol': 'foobar gazonk'}) + 'Sec-WebSocket-Protocol': 'foobar,gazonk'}) class PingPongTest(unittest.TestCase): def setUp(self): From 070876493bf980bfbeab7ce2f11cda7d58e600e0 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 8 Jun 2025 22:19:27 +0900 Subject: [PATCH 05/20] Drop unused/duplicate import --- setup.py | 2 +- tests/test_websocketproxy.py | 2 -- tests/test_websockifyserver.py | 8 +------- websockify/websocketserver.py | 2 +- 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/setup.py b/setup.py index 9ea3443..1d3f7ca 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -from setuptools import setup, find_packages +from setuptools import setup version = '0.13.0' name = 'websockify' diff --git a/tests/test_websocketproxy.py b/tests/test_websocketproxy.py index e56bef7..27c9317 100644 --- a/tests/test_websocketproxy.py +++ b/tests/test_websocketproxy.py @@ -16,8 +16,6 @@ """ Unit tests for websocketproxy """ -import sys -import unittest import unittest import socket from io import StringIO diff --git a/tests/test_websockifyserver.py b/tests/test_websockifyserver.py index dad55ac..c267330 100644 --- a/tests/test_websockifyserver.py +++ b/tests/test_websockifyserver.py @@ -17,18 +17,12 @@ """ Unit tests for websockifyserver """ import errno import os -import logging -import select -import shutil import socket import ssl -from unittest.mock import patch, MagicMock, ANY +from unittest.mock import patch import sys import tempfile import unittest -import socket -import signal -from http.server import BaseHTTPRequestHandler from io import StringIO from io import BytesIO diff --git a/websockify/websocketserver.py b/websockify/websocketserver.py index 4e62f2e..c5bab61 100644 --- a/websockify/websocketserver.py +++ b/websockify/websocketserver.py @@ -10,7 +10,7 @@ Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) import sys from http.server import BaseHTTPRequestHandler, HTTPServer -from websockify.websocket import WebSocket, WebSocketWantReadError, WebSocketWantWriteError +from websockify.websocket import WebSocket class HttpWebSocket(WebSocket): """Class to glue websocket and http request functionality together""" From 9e1c731089f137dbb133caf6c633aa0d4c86b804 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 8 Jun 2025 22:26:37 +0900 Subject: [PATCH 06/20] Fix flake8 errors related to blank lines --- tests/echo.py | 3 ++- tests/echo_client.py | 3 +++ tests/load.py | 3 +-- tests/test_token_plugins.py | 3 +++ tests/test_websocket.py | 4 ++++ tests/test_websocketproxy.py | 2 +- tests/test_websocketserver.py | 1 - tests/test_websockifyserver.py | 18 ++++++++++++++++++ websockify/auth_plugins.py | 2 ++ websockify/sysloghandler.py | 4 ---- websockify/token_plugins.py | 1 + websockify/websocket.py | 5 ++++- websockify/websocketproxy.py | 12 ++++-------- websockify/websocketserver.py | 4 ++++ websockify/websockifyserver.py | 8 ++++---- 15 files changed, 51 insertions(+), 22 deletions(-) diff --git a/tests/echo.py b/tests/echo.py index 780891c..1356400 100755 --- a/tests/echo.py +++ b/tests/echo.py @@ -14,6 +14,7 @@ import os, sys, select, optparse, logging sys.path.insert(0,os.path.join(os.path.dirname(__file__), "..")) from websockify.websockifyserver import WebSockifyServer, WebSockifyRequestHandler + class WebSocketEcho(WebSockifyRequestHandler): """ WebSockets server that echos back whatever is received from the @@ -50,6 +51,7 @@ class WebSocketEcho(WebSockifyRequestHandler): if closed: break + if __name__ == '__main__': parser = optparse.OptionParser(usage="%prog [options] listen_port") parser.add_option("--verbose", "-v", action="store_true", @@ -73,4 +75,3 @@ if __name__ == '__main__': opts.web = "." server = WebSockifyServer(WebSocketEcho, **opts.__dict__) server.start_server() - diff --git a/tests/echo_client.py b/tests/echo_client.py index 4f238f6..6421ec3 100755 --- a/tests/echo_client.py +++ b/tests/echo_client.py @@ -22,6 +22,7 @@ print("Connecting to %s..." % URL) sock.connect(URL) print("Connected.") + def send(msg): while True: try: @@ -36,6 +37,7 @@ def send(msg): ins, outs, excepts = select.select([], [sock], []) if excepts: raise Exception("Socket exception") + def read(): while True: try: @@ -47,6 +49,7 @@ def read(): ins, outs, excepts = select.select([], [sock], []) if excepts: raise Exception("Socket exception") + counter = 1 while True: msg = "Message #%d" % counter diff --git a/tests/load.py b/tests/load.py index 548d47f..1ace2f8 100755 --- a/tests/load.py +++ b/tests/load.py @@ -10,6 +10,7 @@ import sys, os, select, random, time, optparse, logging sys.path.insert(0,os.path.join(os.path.dirname(__file__), "..")) from websockify.websockifyserver import WebSockifyServer, WebSockifyRequestHandler + class WebSocketLoadServer(WebSockifyServer): recv_cnt = 0 @@ -85,7 +86,6 @@ class WebSocketLoad(WebSockifyRequestHandler): return data - def check(self, frames): err = "" @@ -165,4 +165,3 @@ if __name__ == '__main__': opts.web = "." server = WebSocketLoadServer(WebSocketLoad, **opts.__dict__) server.start_server() - diff --git a/tests/test_token_plugins.py b/tests/test_token_plugins.py index e1b967b..e4ec7dd 100644 --- a/tests/test_token_plugins.py +++ b/tests/test_token_plugins.py @@ -9,6 +9,7 @@ from jwcrypto import jwt, jwk from websockify.token_plugins import parse_source_args, ReadOnlyTokenFile, JWTTokenApi, TokenRedis + class ParseSourceArgumentsTestCase(unittest.TestCase): def test_parameterized(self): params = [ @@ -31,6 +32,7 @@ class ParseSourceArgumentsTestCase(unittest.TestCase): for src, args in params: self.assertEqual(args, parse_source_args(src)) + class ReadOnlyTokenFileTestCase(unittest.TestCase): def test_empty(self): mock_source_file = MagicMock() @@ -201,6 +203,7 @@ class JWSTokenTestCase(unittest.TestCase): self.assertEqual(result[0], "remote_host") self.assertEqual(result[1], "remote_port") + class TokenRedisTestCase(unittest.TestCase): def setUp(self): try: diff --git a/tests/test_websocket.py b/tests/test_websocket.py index 42d658d..7f89312 100644 --- a/tests/test_websocket.py +++ b/tests/test_websocket.py @@ -18,6 +18,7 @@ import unittest from websockify import websocket + class FakeSocket: def __init__(self): self.data = b'' @@ -26,6 +27,7 @@ class FakeSocket: self.data += buf return len(buf) + class AcceptTestCase(unittest.TestCase): def test_success(self): ws = websocket.WebSocket() @@ -116,6 +118,7 @@ class AcceptTestCase(unittest.TestCase): 'Sec-WebSocket-Key': 'DKURYVK9cRFul1vOZVA56Q==', 'Sec-WebSocket-Protocol': 'foobar,gazonk'}) + class PingPongTest(unittest.TestCase): def setUp(self): self.ws = websocket.WebSocket() @@ -142,6 +145,7 @@ class PingPongTest(unittest.TestCase): self.ws.pong(b'foo') self.assertEqual(self.sock.data, b'\x8a\x03foo') + class HyBiEncodeDecodeTestCase(unittest.TestCase): def test_decode_hybi_text(self): buf = b'\x81\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58' diff --git a/tests/test_websocketproxy.py b/tests/test_websocketproxy.py index 27c9317..2da8b08 100644 --- a/tests/test_websocketproxy.py +++ b/tests/test_websocketproxy.py @@ -56,6 +56,7 @@ class FakeServer: self.ssl_target = None self.unix_target = None + class ProxyRequestHandlerTestCase(unittest.TestCase): def setUp(self): super().setUp() @@ -126,4 +127,3 @@ class ProxyRequestHandlerTestCase(unittest.TestCase): self.handler.server.target_host = "someotherhost" self.handler.auth_connection() - diff --git a/tests/test_websocketserver.py b/tests/test_websocketserver.py index 0e37e3d..8822572 100644 --- a/tests/test_websocketserver.py +++ b/tests/test_websocketserver.py @@ -66,4 +66,3 @@ class HttpWebSocketTest(unittest.TestCase): # Then req_obj.end_headers.assert_called_once_with() - diff --git a/tests/test_websockifyserver.py b/tests/test_websockifyserver.py index c267330..91ca0e7 100644 --- a/tests/test_websockifyserver.py +++ b/tests/test_websockifyserver.py @@ -231,6 +231,7 @@ class WebSockifyServerTestCase(unittest.TestCase): def test_do_handshake_no_ssl(self): class FakeHandler: CALLED = False + def __init__(self, *args, **kwargs): type(self).CALLED = True @@ -286,12 +287,16 @@ class WebSockifyServerTestCase(unittest.TestCase): def __init__(self, purpose): self.verify_mode = None self.options = 0 + def load_cert_chain(self, certfile, keyfile, password): pass + def set_default_verify_paths(self): pass + def load_verify_locations(self, cafile): pass + def wrap_socket(self, *args, **kwargs): raise ssl.SSLError(ssl.SSL_ERROR_EOF) @@ -317,17 +322,23 @@ class WebSockifyServerTestCase(unittest.TestCase): class fake_create_default_context(): CIPHERS = '' + def __init__(self, purpose): self.verify_mode = None self.options = 0 + def load_cert_chain(self, certfile, keyfile, password): pass + def set_default_verify_paths(self): pass + def load_verify_locations(self, cafile): pass + def wrap_socket(self, *args, **kwargs): pass + def set_ciphers(self, ciphers_to_set): fake_create_default_context.CIPHERS = ciphers_to_set @@ -352,19 +363,26 @@ class WebSockifyServerTestCase(unittest.TestCase): class fake_create_default_context: OPTIONS = 0 + def __init__(self, purpose): self.verify_mode = None self._options = 0 + def load_cert_chain(self, certfile, keyfile, password): pass + def set_default_verify_paths(self): pass + def load_verify_locations(self, cafile): pass + def wrap_socket(self, *args, **kwargs): pass + def get_options(self): return self._options + def set_options(self, val): fake_create_default_context.OPTIONS = val options = property(get_options, set_options) diff --git a/websockify/auth_plugins.py b/websockify/auth_plugins.py index 36fac52..dda188e 100644 --- a/websockify/auth_plugins.py +++ b/websockify/auth_plugins.py @@ -76,6 +76,7 @@ class BasicHTTPAuth(): raise AuthenticationError(response_code=401, response_headers={'WWW-Authenticate': 'Basic realm="Websockify"'}) + class ExpectOrigin(): def __init__(self, src=None): if src is None: @@ -88,6 +89,7 @@ class ExpectOrigin(): if origin is None or origin not in self.source: raise InvalidOriginError(expected=self.source, actual=origin) + class ClientCertCNAuth(): """Verifies client by SSL certificate. Specify src as whitespace separated list of common names.""" diff --git a/websockify/sysloghandler.py b/websockify/sysloghandler.py index 2b5f32f..cfd995f 100644 --- a/websockify/sysloghandler.py +++ b/websockify/sysloghandler.py @@ -16,11 +16,8 @@ class WebsockifySysLogHandler(handlers.SysLogHandler): _max_ident = 24 #safer for old daemons _send_length = False _tail = '\n' - - ident = None - def __init__(self, address=('localhost', handlers.SYSLOG_UDP_PORT), facility=handlers.SysLogHandler.LOG_USER, socktype=None, ident=None, legacy=False): @@ -46,7 +43,6 @@ class WebsockifySysLogHandler(handlers.SysLogHandler): super().__init__(address, facility, socktype) - def emit(self, record): """ Emit a record. diff --git a/websockify/token_plugins.py b/websockify/token_plugins.py index d582032..bc27960 100644 --- a/websockify/token_plugins.py +++ b/websockify/token_plugins.py @@ -84,6 +84,7 @@ class TokenFile(ReadOnlyTokenFile): return super().lookup(token) + class TokenFileName(BasePlugin): # source is a directory # token is filename diff --git a/websockify/websocket.py b/websockify/websocket.py index 3bfb594..1dbda7d 100644 --- a/websockify/websocket.py +++ b/websockify/websocket.py @@ -31,11 +31,15 @@ except ImportError: warnings.warn("no 'numpy' module, HyBi protocol will be slower") numpy = None + class WebSocketWantReadError(ssl.SSLWantReadError): pass + + class WebSocketWantWriteError(ssl.SSLWantWriteError): pass + class WebSocket: """WebSocket protocol socket like class. @@ -873,4 +877,3 @@ class WebSocket: f['payload'] = buf[hlen:(hlen+length)] return f - diff --git a/websockify/websocketproxy.py b/websockify/websocketproxy.py index 70da60d..de75a34 100644 --- a/websockify/websocketproxy.py +++ b/websockify/websocketproxy.py @@ -20,6 +20,7 @@ from websockify import websockifyserver from websockify import auth_plugins as auth from urllib.parse import parse_qs, urlparse + class ProxyRequestHandler(websockifyserver.WebSockifyRequestHandler): buffer_size = 65536 @@ -248,7 +249,6 @@ Traffic Legend: self.server.target_host, self.server.target_port) raise self.CClose(closed['code'], closed['reason']) - if target in outs: # Send queued client data to the target dat = tqueue.pop(0) @@ -260,7 +260,6 @@ Traffic Legend: tqueue.insert(0, dat[sent:]) self.print_traffic(".>") - if target in ins: # Receive target data, encode it and queue for client buf = target.recv(self.buffer_size) @@ -283,6 +282,7 @@ Traffic Legend: cqueue.append(buf) self.print_traffic("{") + class WebSocketProxy(websockifyserver.WebSockifyServer): """ Proxy traffic to and from a WebSockets client to a normal TCP @@ -427,6 +427,7 @@ SSL_OPTIONS = { 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 Python version""" @@ -444,6 +445,7 @@ def select_ssl_version(version): return SSL_OPTIONS[fallback] + def websockify_init(): # Setup basic logging to stderr. stderr_handler = logging.StreamHandler() @@ -562,9 +564,7 @@ def websockify_init(): (opts, args) = parser.parse_args() - # Validate options. - if opts.token_source and not opts.token_plugin: parser.error("You must use --token-plugin to use --token-source") @@ -583,11 +583,9 @@ def websockify_init(): if opts.legacy_syslog and not opts.syslog: parser.error("You must use --syslog to use --legacy-syslog") - opts.ssl_options = select_ssl_version(opts.ssl_version) del opts.ssl_version - if opts.log_file: # Setup logging to user-specified file. opts.log_file = os.path.abspath(opts.log_file) @@ -638,7 +636,6 @@ def websockify_init(): root = logging.getLogger() root.setLevel(logging.DEBUG) - # Transform to absolute path as daemon may chdir if opts.target_cfg: opts.target_cfg = os.path.abspath(opts.target_cfg) @@ -795,7 +792,6 @@ class LibProxyServer(ThreadingMixIn, HTTPServer): 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 diff --git a/websockify/websocketserver.py b/websockify/websocketserver.py index c5bab61..146aa81 100644 --- a/websockify/websocketserver.py +++ b/websockify/websocketserver.py @@ -12,6 +12,7 @@ from http.server import BaseHTTPRequestHandler, HTTPServer from websockify.websocket import WebSocket + class HttpWebSocket(WebSocket): """Class to glue websocket and http request functionality together""" def __init__(self, request_handler): @@ -100,11 +101,14 @@ class WebSocketRequestHandlerMixIn: """ pass + # Convenient ready made classes + class WebSocketRequestHandler(WebSocketRequestHandlerMixIn, BaseHTTPRequestHandler): pass + class WebSocketServer(HTTPServer): pass diff --git a/websockify/websockifyserver.py b/websockify/websockifyserver.py index 95cb451..b656cc1 100644 --- a/websockify/websockifyserver.py +++ b/websockify/websockifyserver.py @@ -32,6 +32,7 @@ if sys.platform == 'win32': from websockify.websocket import WebSocketWantReadError, WebSocketWantWriteError from websockify.websocketserver import WebSocketRequestHandlerMixIn + class CompatibleWebSocket(WebSocketRequestHandlerMixIn.SocketClass): def select_subprotocol(self, protocols): # Handle old websockify clients that still specify a sub-protocol @@ -40,6 +41,7 @@ class CompatibleWebSocket(WebSocketRequestHandlerMixIn.SocketClass): else: return '' + # HTTP handler with WebSocket upgrade support class WebSockifyRequestHandler(WebSocketRequestHandlerMixIn, SimpleHTTPRequestHandler): """ @@ -513,7 +515,7 @@ class WebSockifyServer(): @staticmethod def daemonize(keepfd=None, chdir='/'): - + if keepfd is None: keepfd = [] @@ -644,10 +646,10 @@ class WebSockifyServer(): """ Same as msg() but as warning. """ self.logger.log(logging.WARN, *args, **kwargs) - # # Events that can/should be overridden in sub-classes # + def started(self): """ Called after WebSockets startup """ self.vmsg("WebSockets server started") @@ -879,5 +881,3 @@ class WebSockifyServer(): # Restore signals for sig, func in original_signals.items(): signal.signal(sig, func) - - From 9140ab7a7d5cc4fbd69d9870e58d1db5addbf45c Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 8 Jun 2025 22:38:15 +0900 Subject: [PATCH 07/20] Fix flake8 errors related to whitespace --- tests/echo.py | 2 +- tests/echo_client.py | 2 +- tests/load.py | 10 ++--- tests/test_token_plugins.py | 10 ++--- tests/test_websockifyserver.py | 4 +- websockify/sysloghandler.py | 6 +-- websockify/token_plugins.py | 8 ++-- websockify/websocket.py | 20 +++++----- websockify/websocketproxy.py | 56 +++++++++++++------------- websockify/websocketserver.py | 2 +- websockify/websockifyserver.py | 72 +++++++++++++++++----------------- 11 files changed, 96 insertions(+), 96 deletions(-) diff --git a/tests/echo.py b/tests/echo.py index 1356400..b297a52 100755 --- a/tests/echo.py +++ b/tests/echo.py @@ -11,7 +11,7 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates ''' import os, sys, select, optparse, logging -sys.path.insert(0,os.path.join(os.path.dirname(__file__), "..")) +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) from websockify.websockifyserver import WebSockifyServer, WebSockifyRequestHandler diff --git a/tests/echo_client.py b/tests/echo_client.py index 6421ec3..58bc039 100755 --- a/tests/echo_client.py +++ b/tests/echo_client.py @@ -5,7 +5,7 @@ import sys import optparse import select -sys.path.insert(0,os.path.join(os.path.dirname(__file__), "..")) +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) from websockify.websocket import WebSocket, \ WebSocketWantReadError, WebSocketWantWriteError diff --git a/tests/load.py b/tests/load.py index 1ace2f8..f0c0a7a 100755 --- a/tests/load.py +++ b/tests/load.py @@ -7,7 +7,7 @@ given a sequence number. Any errors are reported and counted. ''' import sys, os, select, random, time, optparse, logging -sys.path.insert(0,os.path.join(os.path.dirname(__file__), "..")) +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) from websockify.websockifyserver import WebSockifyServer, WebSockifyRequestHandler @@ -74,13 +74,13 @@ class WebSocketLoad(WebSockifyRequestHandler): def generate(self): length = random.randint(10, self.max_packet_size) - numlist = self.rand_array[self.max_packet_size-length:] + numlist = self.rand_array[self.max_packet_size - length:] # Error in length #numlist.append(5) chksum = sum(numlist) # Error in checksum #numlist[0] = 5 - nums = "".join( [str(n) for n in numlist] ) + nums = "".join([str(n) for n in numlist]) data = "^%d:%d:%d:%s$" % (self.send_cnt, length, chksum, nums) self.send_cnt += 1 @@ -102,7 +102,7 @@ class WebSocketLoad(WebSockifyRequestHandler): try: cnt, length, chksum, nums = data[1:-1].split(':') - cnt = int(cnt) + cnt = int(cnt) length = int(length) chksum = int(chksum) except ValueError: @@ -151,7 +151,7 @@ if __name__ == '__main__': if len(args) != 1: raise ValueError opts.listen_port = int(args[0]) - if len(args) not in [1,2]: raise ValueError + if len(args) not in [1, 2]: raise ValueError opts.listen_port = int(args[0]) if len(args) == 2: opts.delay = int(args[1]) diff --git a/tests/test_token_plugins.py b/tests/test_token_plugins.py index e4ec7dd..f433eee 100644 --- a/tests/test_token_plugins.py +++ b/tests/test_token_plugins.py @@ -114,7 +114,7 @@ class JWSTokenTestCase(unittest.TestCase): key = jwk.JWK() private_key = open("./tests/fixtures/private.pem", "rb").read() key.import_from_pem(private_key) - jwt_token = jwt.JWT({"alg": "RS256"}, {'host': "remote_host", 'port': "remote_port", 'nbf': 100, 'exp': 200 }) + jwt_token = jwt.JWT({"alg": "RS256"}, {'host': "remote_host", 'port': "remote_port", 'nbf': 100, 'exp': 200}) jwt_token.make_signed_token(key) mock_time.return_value = 150 @@ -131,7 +131,7 @@ class JWSTokenTestCase(unittest.TestCase): key = jwk.JWK() private_key = open("./tests/fixtures/private.pem", "rb").read() key.import_from_pem(private_key) - jwt_token = jwt.JWT({"alg": "RS256"}, {'host': "remote_host", 'port': "remote_port", 'nbf': 100, 'exp': 200 }) + jwt_token = jwt.JWT({"alg": "RS256"}, {'host': "remote_host", 'port': "remote_port", 'nbf': 100, 'exp': 200}) jwt_token.make_signed_token(key) mock_time.return_value = 50 @@ -146,7 +146,7 @@ class JWSTokenTestCase(unittest.TestCase): key = jwk.JWK() private_key = open("./tests/fixtures/private.pem", "rb").read() key.import_from_pem(private_key) - jwt_token = jwt.JWT({"alg": "RS256"}, {'host': "remote_host", 'port': "remote_port", 'nbf': 100, 'exp': 200 }) + jwt_token = jwt.JWT({"alg": "RS256"}, {'host': "remote_host", 'port': "remote_port", 'nbf': 100, 'exp': 200}) jwt_token.make_signed_token(key) mock_time.return_value = 250 @@ -159,7 +159,7 @@ class JWSTokenTestCase(unittest.TestCase): secret = open("./tests/fixtures/symmetric.key").read() key = jwk.JWK() - key.import_key(kty="oct",k=secret) + key.import_key(kty="oct", k=secret) jwt_token = jwt.JWT({"alg": "HS256"}, {'host': "remote_host", 'port': "remote_port"}) jwt_token.make_signed_token(key) @@ -174,7 +174,7 @@ class JWSTokenTestCase(unittest.TestCase): secret = open("./tests/fixtures/symmetric.key").read() key = jwk.JWK() - key.import_key(kty="oct",k=secret) + key.import_key(kty="oct", k=secret) jwt_token = jwt.JWT({"alg": "HS256"}, {'host': "remote_host", 'port': "remote_port"}) jwt_token.make_signed_token(key) diff --git a/tests/test_websockifyserver.py b/tests/test_websockifyserver.py index 91ca0e7..a455cd3 100644 --- a/tests/test_websockifyserver.py +++ b/tests/test_websockifyserver.py @@ -313,7 +313,7 @@ class WebSockifyServerTestCase(unittest.TestCase): def __init__(self, *args, **kwargs): pass - server = self._get_server(handler_class=FakeHandler, daemon=True, + server = self._get_server(handler_class=FakeHandler, daemon=True, idle_timeout=1, ssl_ciphers=test_ciphers) sock = FakeSocket(b"\x16some ssl data") @@ -354,7 +354,7 @@ class WebSockifyServerTestCase(unittest.TestCase): def __init__(self, *args, **kwargs): pass - server = self._get_server(handler_class=FakeHandler, daemon=True, + server = self._get_server(handler_class=FakeHandler, daemon=True, idle_timeout=1, ssl_options=test_options) sock = FakeSocket(b"\x16some ssl data") diff --git a/websockify/sysloghandler.py b/websockify/sysloghandler.py index cfd995f..b85596b 100644 --- a/websockify/sysloghandler.py +++ b/websockify/sysloghandler.py @@ -13,7 +13,7 @@ class WebsockifySysLogHandler(handlers.SysLogHandler): _legacy = False _timestamp_fmt = '%Y-%m-%dT%H:%M:%SZ' _max_hostname = 255 - _max_ident = 24 #safer for old daemons + _max_ident = 24 # safer for old daemons _send_length = False _tail = '\n' ident = None @@ -54,7 +54,7 @@ class WebsockifySysLogHandler(handlers.SysLogHandler): try: # Gather info. text = self.format(record).replace(self._tail, ' ') - if not text: # nothing to log + if not text: # nothing to log return pri = self.encodePriority(self.facility, @@ -69,7 +69,7 @@ class WebsockifySysLogHandler(handlers.SysLogHandler): else: ident = '' - pid = os.getpid() # shouldn't need truncation + pid = os.getpid() # shouldn't need truncation # Format the header. head = { diff --git a/websockify/token_plugins.py b/websockify/token_plugins.py index bc27960..26d1569 100644 --- a/websockify/token_plugins.py +++ b/websockify/token_plugins.py @@ -114,8 +114,8 @@ class BaseTokenAPI(BasePlugin): def process_result(self, resp): host, port = resp.text.split(':') - port = port.encode('ascii','ignore') - return [ host, port ] + port = port.encode('ascii', 'ignore') + return [host, port] def lookup(self, token): import requests @@ -159,7 +159,7 @@ class JWTTokenApi(BasePlugin): key.import_from_pem(key_data) except: try: - key.import_key(k=key_data.decode('utf-8'),kty='oct') + key.import_key(k=key_data.decode('utf-8'), kty='oct') except: logger.error('Failed to correctly parse key data!') return None @@ -373,7 +373,7 @@ class UnixDomainSocketDirectory(BasePlugin): if not stat.S_ISSOCK(uds_path.stat().st_mode): return None - return [ 'unix_socket', uds_path ] + return ['unix_socket', uds_path] except Exception as e: logger.error("Error finding unix domain socket: %s" % str(e)) return None diff --git a/websockify/websocket.py b/websockify/websocket.py index 1dbda7d..1ec9f68 100644 --- a/websockify/websocket.py +++ b/websockify/websocket.py @@ -761,7 +761,7 @@ class WebSocket: pend = plen b = c = b'' if plen >= 4: - dtype=numpy.dtype('') mask = numpy.frombuffer(mask, dtype, count=1) @@ -770,7 +770,7 @@ class WebSocket: b = numpy.bitwise_xor(data, mask).tobytes() if plen % 4: - dtype=numpy.dtype('B') + dtype = numpy.dtype('B') if sys.byteorder == 'big': dtype = dtype.newbyteorder('>') mask = numpy.frombuffer(mask, dtype, count=(plen % 4)) @@ -829,11 +829,11 @@ class WebSocket: 'payload' : decoded_buffer} """ - f = {'fin' : 0, - 'opcode' : 0, - 'masked' : False, - 'length' : 0, - 'payload' : None} + f = {'fin': 0, + 'opcode': 0, + 'masked': False, + 'length': 0, + 'payload': None} blen = len(buf) hlen = 2 @@ -871,9 +871,9 @@ class WebSocket: if f['masked']: # unmask payload - mask_key = buf[hlen-4:hlen] - f['payload'] = self._unmask(buf[hlen:(hlen+length)], mask_key) + mask_key = buf[hlen - 4:hlen] + f['payload'] = self._unmask(buf[hlen:(hlen + length)], mask_key) else: - f['payload'] = buf[hlen:(hlen+length)] + f['payload'] = buf[hlen:(hlen + length)] return f diff --git a/websockify/websocketproxy.py b/websockify/websocketproxy.py index de75a34..f7eadaf 100644 --- a/websockify/websocketproxy.py +++ b/websockify/websocketproxy.py @@ -63,7 +63,7 @@ Traffic Legend: # clear out any existing SSL_ headers that the client might # have maliciously set - ssl_headers = [ h for h in self.headers if h.startswith('SSL_') ] + ssl_headers = [h for h in self.headers if h.startswith('SSL_')] for h in ssl_headers: del self.headers[h] @@ -160,7 +160,7 @@ Traffic Legend: else: # Extract the token parameter from url - args = parse_qs(urlparse(self.path)[4]) # 4 is the query from url + args = parse_qs(urlparse(self.path)[4]) # 4 is the query from url if 'token' in args and len(args['token']): token = args['token'][0].rstrip('\n') @@ -269,7 +269,7 @@ Traffic Legend: # Send queued target data to the client if len(cqueue) != 0: c_pend = True - while(c_pend): + while (c_pend): c_pend = self.send_frames(cqueue) cqueue = [] @@ -293,20 +293,20 @@ class WebSocketProxy(websockifyserver.WebSockifyServer): def __init__(self, RequestHandlerClass=ProxyRequestHandler, *args, **kwargs): # Save off proxy specific options - self.target_host = kwargs.pop('target_host', None) - self.target_port = kwargs.pop('target_port', None) - self.wrap_cmd = kwargs.pop('wrap_cmd', None) - self.wrap_mode = kwargs.pop('wrap_mode', None) - self.unix_target = kwargs.pop('unix_target', None) - self.ssl_target = kwargs.pop('ssl_target', None) - self.heartbeat = kwargs.pop('heartbeat', None) + self.target_host = kwargs.pop('target_host', None) + self.target_port = kwargs.pop('target_port', None) + self.wrap_cmd = kwargs.pop('wrap_cmd', None) + self.wrap_mode = kwargs.pop('wrap_mode', None) + self.unix_target = kwargs.pop('unix_target', None) + self.ssl_target = kwargs.pop('ssl_target', None) + self.heartbeat = kwargs.pop('heartbeat', None) self.token_plugin = kwargs.pop('token_plugin', None) self.host_token = kwargs.pop('host_token', None) self.auth_plugin = kwargs.pop('auth_plugin', None) # Last 3 timestamps command was run - self.wrap_times = [0, 0, 0] + self.wrap_times = [0, 0, 0] if self.wrap_cmd: wsdir = os.path.dirname(sys.argv[0]) @@ -334,7 +334,7 @@ class WebSocketProxy(websockifyserver.WebSockifyServer): sock.close() # Insert rebinder at the head of the (possibly empty) LD_PRELOAD pathlist - ld_preloads = filter(None, [ self.rebinder, os.environ.get("LD_PRELOAD", None) ]) + ld_preloads = filter(None, [self.rebinder, os.environ.get("LD_PRELOAD", None)]) os.environ.update({ "LD_PRELOAD": os.pathsep.join(ld_preloads), @@ -401,7 +401,7 @@ class WebSocketProxy(websockifyserver.WebSockifyServer): sys.exit(ret) elif self.wrap_mode == "respawn": now = time.time() - avg = sum(self.wrap_times)/len(self.wrap_times) + avg = sum(self.wrap_times) / len(self.wrap_times) if (now - avg) < 10: # 3 times in the last 10 seconds if self.spawn_message: @@ -756,32 +756,32 @@ class LibProxyServer(ThreadingMixIn, HTTPServer): def __init__(self, RequestHandlerClass=ProxyRequestHandler, **kwargs): # Save off proxy specific options - self.target_host = kwargs.pop('target_host', None) - self.target_port = kwargs.pop('target_port', None) - self.wrap_cmd = kwargs.pop('wrap_cmd', None) - self.wrap_mode = kwargs.pop('wrap_mode', None) - self.unix_target = kwargs.pop('unix_target', None) - self.ssl_target = kwargs.pop('ssl_target', None) - self.token_plugin = kwargs.pop('token_plugin', None) - self.auth_plugin = kwargs.pop('auth_plugin', None) - self.heartbeat = kwargs.pop('heartbeat', None) + self.target_host = kwargs.pop('target_host', None) + self.target_port = kwargs.pop('target_port', None) + self.wrap_cmd = kwargs.pop('wrap_cmd', None) + self.wrap_mode = kwargs.pop('wrap_mode', None) + self.unix_target = kwargs.pop('unix_target', None) + self.ssl_target = kwargs.pop('ssl_target', None) + self.token_plugin = kwargs.pop('token_plugin', None) + self.auth_plugin = kwargs.pop('auth_plugin', None) + self.heartbeat = kwargs.pop('heartbeat', None) self.token_plugin = None self.auth_plugin = None self.daemon = False # Server configuration - listen_host = kwargs.pop('listen_host', '') - listen_port = kwargs.pop('listen_port', None) - web = kwargs.pop('web', '') + listen_host = kwargs.pop('listen_host', '') + listen_port = kwargs.pop('listen_port', None) + web = kwargs.pop('web', '') # Configuration affecting base request handler - self.only_upgrade = not web - self.verbose = kwargs.pop('verbose', False) + self.only_upgrade = not web + self.verbose = kwargs.pop('verbose', False) record = kwargs.pop('record', '') if record: self.record = os.path.abspath(record) - self.run_once = kwargs.pop('run_once', False) + self.run_once = kwargs.pop('run_once', False) self.handler_id = 0 for arg in kwargs.keys(): diff --git a/websockify/websocketserver.py b/websockify/websocketserver.py index 146aa81..1592dc8 100644 --- a/websockify/websocketserver.py +++ b/websockify/websocketserver.py @@ -94,7 +94,7 @@ class WebSocketRequestHandlerMixIn: def handle_websocket(self): """Handle a WebSocket connection. - + This is called when the WebSocket is ready to be used. A sub-class should perform the necessary communication here and return once done. diff --git a/websockify/websockifyserver.py b/websockify/websockifyserver.py index b656cc1..542378e 100644 --- a/websockify/websockifyserver.py +++ b/websockify/websockifyserver.py @@ -75,7 +75,7 @@ class WebSockifyRequestHandler(WebSocketRequestHandlerMixIn, SimpleHTTPRequestHa self.daemon = getattr(server, "daemon", False) self.record = getattr(server, "record", False) self.run_once = getattr(server, "run_once", False) - self.rec = None + self.rec = None self.handler_id = getattr(server, "handler_id", False) self.file_only = getattr(server, "file_only", False) self.traffic = getattr(server, "traffic", False) @@ -126,7 +126,7 @@ class WebSockifyRequestHandler(WebSocketRequestHandlerMixIn, SimpleHTTPRequestHa fully sent, in which case the caller should call again when the socket is ready. """ - tdelta = int(time.time()*1000) - self.start_time + tdelta = int(time.time() * 1000) - self.start_time if bufs: for buf in bufs: @@ -157,7 +157,7 @@ class WebSockifyRequestHandler(WebSocketRequestHandlerMixIn, SimpleHTTPRequestHa closed = False bufs = [] - tdelta = int(time.time()*1000) - self.start_time + tdelta = int(time.time() * 1000) - self.start_time while True: try: @@ -209,8 +209,8 @@ class WebSockifyRequestHandler(WebSocketRequestHandlerMixIn, SimpleHTTPRequestHa self.server.ws_connection = True # Initialize per client settings self.send_parts = [] - self.recv_part = None - self.start_time = int(time.time()*1000) + self.recv_part = None + self.start_time = int(time.time() * 1000) # client_address is empty with, say, UNIX domain sockets client_addr = "" @@ -347,35 +347,35 @@ class WebSockifyServer(): # settings self.RequestHandlerClass = RequestHandlerClass - self.verbose = verbose - self.listen_fd = listen_fd - self.unix_listen = unix_listen - self.unix_listen_mode = unix_listen_mode - self.listen_host = listen_host - self.listen_port = listen_port - self.prefer_ipv6 = source_is_ipv6 - self.ssl_only = ssl_only - self.ssl_ciphers = ssl_ciphers - self.ssl_options = ssl_options - self.verify_client = verify_client - self.daemon = daemon - self.run_once = run_once - self.timeout = timeout - self.idle_timeout = idle_timeout - self.traffic = traffic - self.file_only = file_only - self.web_auth = web_auth + self.verbose = verbose + self.listen_fd = listen_fd + self.unix_listen = unix_listen + self.unix_listen_mode = unix_listen_mode + self.listen_host = listen_host + self.listen_port = listen_port + self.prefer_ipv6 = source_is_ipv6 + self.ssl_only = ssl_only + self.ssl_ciphers = ssl_ciphers + self.ssl_options = ssl_options + self.verify_client = verify_client + self.daemon = daemon + self.run_once = run_once + self.timeout = timeout + self.idle_timeout = idle_timeout + self.traffic = traffic + self.file_only = file_only + self.web_auth = web_auth - self.launch_time = time.time() - self.ws_connection = False - self.handler_id = 1 - self.terminating = False + self.launch_time = time.time() + self.ws_connection = False + self.handler_id = 1 + self.terminating = False - self.logger = self.get_logger() - self.tcp_keepalive = tcp_keepalive - self.tcp_keepcnt = tcp_keepcnt - self.tcp_keepidle = tcp_keepidle - self.tcp_keepintvl = tcp_keepintvl + self.logger = self.get_logger() + self.tcp_keepalive = tcp_keepalive + self.tcp_keepcnt = tcp_keepcnt + self.tcp_keepidle = tcp_keepidle + self.tcp_keepintvl = tcp_keepintvl # keyfile path must be None if not specified self.key = None @@ -444,7 +444,7 @@ class WebSockifyServer(): @staticmethod def socket(host, port=None, connect=False, prefer_ipv6=False, unix_socket=None, unix_socket_mode=None, unix_socket_listen=False, - use_ssl=False, tcp_keepalive=True, tcp_keepcnt=None, + use_ssl=False, tcp_keepalive=True, tcp_keepcnt=None, tcp_keepidle=None, tcp_keepintvl=None): """ Resolve a host (and optional port) to an IPv4 or IPv6 address. Create a socket. Bind to it if listen is set, @@ -472,7 +472,7 @@ class WebSockifyServer(): addrs.reverse() sock = socket.socket(addrs[0][0], addrs[0][1]) - if tcp_keepalive: + if tcp_keepalive: sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) if tcp_keepcnt: sock.setsockopt(socket.SOL_TCP, socket.TCP_KEEPCNT, @@ -816,7 +816,7 @@ class WebSockifyServer(): startsock, address = lsock.accept() # Unix Socket will not report address (empty string), but address[0] is logged a bunch if self.unix_listen != None: - address = [ self.unix_listen ] + address = [self.unix_listen] else: continue except self.Terminate: @@ -838,7 +838,7 @@ class WebSockifyServer(): if self.run_once: # Run in same process if run_once self.top_new_client(startsock, address) - if self.ws_connection : + if self.ws_connection: self.msg('%s: exiting due to --run-once' % address[0]) break From b9194bf175f58910d1882764b7a763a245afb21b Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 8 Jun 2025 22:43:13 +0900 Subject: [PATCH 08/20] Avoid multiple statements on one line --- tests/echo.py | 9 ++++++--- tests/echo_client.py | 15 ++++++++++----- tests/load.py | 9 ++++++--- websockify/websocketproxy.py | 9 ++++++--- websockify/websockifyserver.py | 14 +++++++++----- 5 files changed, 37 insertions(+), 19 deletions(-) diff --git a/tests/echo.py b/tests/echo.py index b297a52..bd4aaee 100755 --- a/tests/echo.py +++ b/tests/echo.py @@ -34,9 +34,11 @@ class WebSocketEcho(WebSockifyRequestHandler): while True: wlist = [] - if cqueue or c_pend: wlist.append(self.request) + if cqueue or c_pend: + wlist.append(self.request) ins, outs, excepts = select.select(rlist, wlist, [], 1) - if excepts: raise Exception("Socket exception") + if excepts: + raise Exception("Socket exception") if self.request in outs: # Send queued target data to the client @@ -65,7 +67,8 @@ if __name__ == '__main__': (opts, args) = parser.parse_args() try: - if len(args) != 1: raise ValueError + if len(args) != 1: + raise ValueError opts.listen_port = int(args[0]) except ValueError: parser.error("Invalid arguments") diff --git a/tests/echo_client.py b/tests/echo_client.py index 58bc039..ce0c0d2 100755 --- a/tests/echo_client.py +++ b/tests/echo_client.py @@ -31,11 +31,13 @@ def send(msg): except WebSocketWantReadError: msg = '' ins, outs, excepts = select.select([sock], [], []) - if excepts: raise Exception("Socket exception") + if excepts: + raise Exception("Socket exception") except WebSocketWantWriteError: msg = '' ins, outs, excepts = select.select([], [sock], []) - if excepts: raise Exception("Socket exception") + if excepts: + raise Exception("Socket exception") def read(): @@ -44,10 +46,12 @@ def read(): return sock.recvmsg() except WebSocketWantReadError: ins, outs, excepts = select.select([sock], [], []) - if excepts: raise Exception("Socket exception") + if excepts: + raise Exception("Socket exception") except WebSocketWantWriteError: ins, outs, excepts = select.select([], [sock], []) - if excepts: raise Exception("Socket exception") + if excepts: + raise Exception("Socket exception") counter = 1 @@ -59,7 +63,8 @@ while True: while True: ins, outs, excepts = select.select([sock], [], [], 1.0) - if excepts: raise Exception("Socket exception") + if excepts: + raise Exception("Socket exception") if ins == []: break diff --git a/tests/load.py b/tests/load.py index f0c0a7a..14e1f2f 100755 --- a/tests/load.py +++ b/tests/load.py @@ -50,7 +50,8 @@ class WebSocketLoad(WebSockifyRequestHandler): while True: ins, outs, excepts = select.select(socks, socks, socks, 1) - if excepts: raise Exception("Socket exception") + if excepts: + raise Exception("Socket exception") if client in ins: frames, closed = self.recv_frames() @@ -148,10 +149,12 @@ if __name__ == '__main__': (opts, args) = parser.parse_args() try: - if len(args) != 1: raise ValueError + if len(args) != 1: + raise ValueError opts.listen_port = int(args[0]) - if len(args) not in [1, 2]: raise ValueError + if len(args) not in [1, 2]: + raise ValueError opts.listen_port = int(args[0]) if len(args) == 2: opts.delay = int(args[1]) diff --git a/websockify/websocketproxy.py b/websockify/websocketproxy.py index f7eadaf..6e19e72 100644 --- a/websockify/websocketproxy.py +++ b/websockify/websocketproxy.py @@ -201,8 +201,10 @@ Traffic Legend: self.heartbeat = now + self.server.heartbeat self.send_ping() - if tqueue: wlist.append(target) - if cqueue or c_pend: wlist.append(self.request) + if tqueue: + wlist.append(target) + if cqueue or c_pend: + wlist.append(self.request) try: ins, outs, excepts = select.select(rlist, wlist, [], 1) except OSError: @@ -217,7 +219,8 @@ Traffic Legend: else: continue - if excepts: raise Exception("Socket exception") + if excepts: + raise Exception("Socket exception") if self.request in outs: # Send queued target data to the client diff --git a/websockify/websockifyserver.py b/websockify/websockifyserver.py index 542378e..7342b6e 100644 --- a/websockify/websockifyserver.py +++ b/websockify/websockifyserver.py @@ -528,9 +528,11 @@ class WebSockifyServer(): os.setuid(os.getuid()) # relinquish elevations # Double fork to daemonize - if os.fork() > 0: os._exit(0) # Parent exits - os.setsid() # Obtain new process group - if os.fork() > 0: os._exit(0) # Parent exits + if os.fork() > 0: # Parent exits + os._exit(0) + os.setsid() # Obtain new process group + if os.fork() > 0: # Parent exits + os._exit(0) # Signal handling signal.signal(signal.SIGTERM, signal.SIG_IGN) @@ -538,14 +540,16 @@ class WebSockifyServer(): # Close open files maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] - if maxfd == resource.RLIM_INFINITY: maxfd = 256 + if maxfd == resource.RLIM_INFINITY: + maxfd = 256 for fd in reversed(range(maxfd)): try: if fd not in keepfd: os.close(fd) except OSError: _, exc, _ = sys.exc_info() - if exc.errno != errno.EBADF: raise + if exc.errno != errno.EBADF: + raise # Redirect I/O to /dev/null os.dup2(os.open(os.devnull, os.O_RDWR), sys.stdin.fileno()) From 992a2d17939eb5679d9154cfcd25d99ed69f4a94 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 8 Jun 2025 22:47:17 +0900 Subject: [PATCH 09/20] Use 'is' in comparison to None ... instead of == or != . --- websockify/websocketproxy.py | 8 ++++---- websockify/websockifyserver.py | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/websockify/websocketproxy.py b/websockify/websocketproxy.py index 6e19e72..ca90781 100644 --- a/websockify/websocketproxy.py +++ b/websockify/websocketproxy.py @@ -367,7 +367,7 @@ class WebSocketProxy(websockifyserver.WebSockifyServer): else: dst_string = "%s:%s" % (self.target_host, self.target_port) - if self.listen_fd != None: + if self.listen_fd is not None: src_string = "inetd" else: src_string = "%s:%s" % (self.listen_host, self.listen_port) @@ -392,11 +392,11 @@ class WebSocketProxy(websockifyserver.WebSockifyServer): if self.wrap_cmd and self.cmd: ret = self.cmd.poll() - if ret != None: + if ret is not None: self.vmsg("Wrapped command exited (or daemon). Returned %s" % ret) self.cmd = None - if self.wrap_cmd and self.cmd == None: + if self.wrap_cmd and self.cmd is None: # Response to wrapped command being gone if self.wrap_mode == "ignore": pass @@ -708,7 +708,7 @@ def websockify_init(): except ValueError: parser.error("Error parsing target port") - if len(args) > 0 and opts.wrap_cmd == None: + if len(args) > 0 and opts.wrap_cmd is None: parser.error("Too many arguments") if opts.token_plugin is not None: diff --git a/websockify/websockifyserver.py b/websockify/websockifyserver.py index 7342b6e..df180a3 100644 --- a/websockify/websockifyserver.py +++ b/websockify/websockifyserver.py @@ -22,7 +22,7 @@ for mod, msg in [('ssl', 'TLS/SSL/wss is disabled'), try: globals()[mod] = __import__(mod) except ImportError: - globals()[mod] = None + globals()[mod] is None print("WARNING: no '%s' module, %s" % (mod, msg)) if sys.platform == 'win32': @@ -405,9 +405,9 @@ class WebSockifyServer(): # Show configuration self.msg("WebSocket server settings:") - if self.listen_fd != None: + if self.listen_fd is not None: self.msg(" - Listen for inetd connections") - elif self.unix_listen != None: + elif self.unix_listen is not None: self.msg(" - Listen on unix socket %s", self.unix_listen) else: self.msg(" - Listen on %s:%s", @@ -741,9 +741,9 @@ class WebSockifyServer(): """ try: - if self.listen_fd != None: + if self.listen_fd is not None: lsock = socket.fromfd(self.listen_fd, socket.AF_INET, socket.SOCK_STREAM) - elif self.unix_listen != None: + elif self.unix_listen is not None: lsock = self.socket(host=None, unix_socket=self.unix_listen, unix_socket_mode=self.unix_listen_mode, @@ -819,7 +819,7 @@ class WebSockifyServer(): if lsock in ready: startsock, address = lsock.accept() # Unix Socket will not report address (empty string), but address[0] is logged a bunch - if self.unix_listen != None: + if self.unix_listen is not None: address = [self.unix_listen] else: continue From 053d37c1d6290c42692cf3fa2bdf4dc9e60c1b18 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 8 Jun 2025 22:54:26 +0900 Subject: [PATCH 10/20] Remove dead code ... to fix "E265 block comment should start with '# '". --- tests/load.py | 4 ---- websockify/websocket.py | 1 - websockify/websockifyserver.py | 2 -- 3 files changed, 7 deletions(-) diff --git a/tests/load.py b/tests/load.py index 14e1f2f..fecf462 100755 --- a/tests/load.py +++ b/tests/load.py @@ -76,11 +76,7 @@ class WebSocketLoad(WebSockifyRequestHandler): def generate(self): length = random.randint(10, self.max_packet_size) numlist = self.rand_array[self.max_packet_size - length:] - # Error in length - #numlist.append(5) chksum = sum(numlist) - # Error in checksum - #numlist[0] = 5 nums = "".join([str(n) for n in numlist]) data = "^%d:%d:%d:%s$" % (self.send_cnt, length, chksum, nums) self.send_cnt += 1 diff --git a/websockify/websocket.py b/websockify/websocket.py index 1ec9f68..349376f 100644 --- a/websockify/websocket.py +++ b/websockify/websocket.py @@ -766,7 +766,6 @@ class WebSocket: dtype = dtype.newbyteorder('>') 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).tobytes() if plen % 4: diff --git a/websockify/websockifyserver.py b/websockify/websockifyserver.py index df180a3..ce1f012 100644 --- a/websockify/websockifyserver.py +++ b/websockify/websockifyserver.py @@ -578,7 +578,6 @@ class WebSockifyServer(): # Peek, but do not read the data so that we have a opportunity # to SSL wrap the socket first handshake = sock.recv(1024, socket.MSG_PEEK) - #self.msg("Handshake [%s]" % handshake) if not handshake: raise self.EClose("") @@ -660,7 +659,6 @@ class WebSockifyServer(): def poll(self): """ Run periodically while waiting for connections. """ - #self.vmsg("Running poll()") pass def terminate(self): From de4acf07525dffd15b58f9cb5b57b0db263d25f3 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 8 Jun 2025 23:07:31 +0900 Subject: [PATCH 11/20] Drop unused variables --- tests/load.py | 2 -- tests/test_websockifyserver.py | 3 --- websockify/websocket.py | 2 -- websockify/websockifyserver.py | 2 +- 4 files changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/load.py b/tests/load.py index fecf462..6231bc8 100755 --- a/tests/load.py +++ b/tests/load.py @@ -43,8 +43,6 @@ class WebSocketLoad(WebSockifyRequestHandler): def responder(self, client): c_pend = 0 - cqueue = [] - cpartial = "" socks = [client] last_send = time.time() * 1000 diff --git a/tests/test_websockifyserver.py b/tests/test_websockifyserver.py index a455cd3..a9a554c 100644 --- a/tests/test_websockifyserver.py +++ b/tests/test_websockifyserver.py @@ -398,7 +398,6 @@ class WebSockifyServerTestCase(unittest.TestCase): def test_start_server_error(self): server = self._get_server(daemon=False, ssl_only=1, idle_timeout=1) - sock = server.socket('localhost') def fake_select(rlist, wlist, xlist, timeout=None): raise Exception("fake error") @@ -410,7 +409,6 @@ class WebSockifyServerTestCase(unittest.TestCase): def test_start_server_keyboardinterrupt(self): server = self._get_server(daemon=False, ssl_only=0, idle_timeout=1) - sock = server.socket('localhost') def fake_select(rlist, wlist, xlist, timeout=None): raise KeyboardInterrupt @@ -422,7 +420,6 @@ class WebSockifyServerTestCase(unittest.TestCase): def test_start_server_systemexit(self): server = self._get_server(daemon=False, ssl_only=0, idle_timeout=1) - sock = server.socket('localhost') def fake_select(rlist, wlist, xlist, timeout=None): sys.exit() diff --git a/websockify/websocket.py b/websockify/websocket.py index 349376f..5cc0ddd 100644 --- a/websockify/websocket.py +++ b/websockify/websocket.py @@ -757,8 +757,6 @@ class WebSocket: # Unmask a frame if numpy: plen = len(buf) - pstart = 0 - pend = plen b = c = b'' if plen >= 4: dtype = numpy.dtype(' Date: Mon, 9 Jun 2025 21:23:10 +0900 Subject: [PATCH 12/20] Fix indentations ... which are now complained by flake8. --- setup.py | 67 ++++++++++---------- tests/echo.py | 8 +-- tests/load.py | 8 +-- tests/test_token_plugins.py | 2 +- websockify/token_plugins.py | 6 +- websockify/websocket.py | 2 +- websockify/websocketproxy.py | 110 ++++++++++++++++----------------- websockify/websockifyserver.py | 36 +++++------ 8 files changed, 120 insertions(+), 119 deletions(-) diff --git a/setup.py b/setup.py index 1d3f7ca..4e20f72 100644 --- a/setup.py +++ b/setup.py @@ -5,41 +5,42 @@ name = 'websockify' long_description = open("README.md").read() + "\n" + \ open("CHANGES.txt").read() + "\n" -setup(name=name, - version=version, - description="Websockify.", - long_description=long_description, - long_description_content_type="text/markdown", - classifiers=[ - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - ], - python_requires='>=3.6', - keywords='noVNC websockify', - license='LGPLv3', - url="https://github.com/novnc/websockify", - author="Joel Martin", - author_email="github@martintribe.org", - - packages=['websockify'], - include_package_data=True, - install_requires=[ - 'numpy', 'requests', +setup( + name=name, + version=version, + description="Websockify.", + long_description=long_description, + long_description_content_type="text/markdown", + classifiers=[ + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + ], + python_requires='>=3.6', + keywords='noVNC websockify', + license='LGPLv3', + url="https://github.com/novnc/websockify", + author="Joel Martin", + author_email="github@martintribe.org", + packages=['websockify'], + include_package_data=True, + install_requires=[ + 'numpy', + 'requests', 'jwcrypto', 'redis', - ], - zip_safe=False, - entry_points={ + ], + zip_safe=False, + entry_points={ 'console_scripts': [ 'websockify = websockify.websocketproxy:websockify_init', ] - }, - ) + }, +) diff --git a/tests/echo.py b/tests/echo.py index bd4aaee..addce6a 100755 --- a/tests/echo.py +++ b/tests/echo.py @@ -57,13 +57,13 @@ class WebSocketEcho(WebSockifyRequestHandler): if __name__ == '__main__': parser = optparse.OptionParser(usage="%prog [options] listen_port") parser.add_option("--verbose", "-v", action="store_true", - help="verbose messages and per frame traffic") + help="verbose messages and per frame traffic") parser.add_option("--cert", default="self.pem", - help="SSL certificate file") + help="SSL certificate file") parser.add_option("--key", default=None, - help="SSL key file (if separate from cert)") + help="SSL key file (if separate from cert)") parser.add_option("--ssl-only", action="store_true", - help="disallow non-encrypted connections") + help="disallow non-encrypted connections") (opts, args) = parser.parse_args() try: diff --git a/tests/load.py b/tests/load.py index 6231bc8..27d1000 100755 --- a/tests/load.py +++ b/tests/load.py @@ -133,13 +133,13 @@ class WebSocketLoad(WebSockifyRequestHandler): if __name__ == '__main__': parser = optparse.OptionParser(usage="%prog [options] listen_port") parser.add_option("--verbose", "-v", action="store_true", - help="verbose messages and per frame traffic") + help="verbose messages and per frame traffic") parser.add_option("--cert", default="self.pem", - help="SSL certificate file") + help="SSL certificate file") parser.add_option("--key", default=None, - help="SSL key file (if separate from cert)") + help="SSL key file (if separate from cert)") parser.add_option("--ssl-only", action="store_true", - help="disallow non-encrypted connections") + help="disallow non-encrypted connections") (opts, args) = parser.parse_args() try: diff --git a/tests/test_token_plugins.py b/tests/test_token_plugins.py index f433eee..363ae35 100644 --- a/tests/test_token_plugins.py +++ b/tests/test_token_plugins.py @@ -194,7 +194,7 @@ class JWSTokenTestCase(unittest.TestCase): jwt_token = jwt.JWT({"alg": "RS256"}, {'host': "remote_host", 'port': "remote_port"}) jwt_token.make_signed_token(private_key) jwe_token = jwt.JWT(header={"alg": "RSA-OAEP", "enc": "A256CBC-HS512"}, - claims=jwt_token.serialize()) + claims=jwt_token.serialize()) jwe_token.make_encrypted_token(public_key) result = plugin.lookup(jwt_token.serialize()) diff --git a/websockify/token_plugins.py b/websockify/token_plugins.py index 26d1569..3c19206 100644 --- a/websockify/token_plugins.py +++ b/websockify/token_plugins.py @@ -306,7 +306,7 @@ class TokenRedis(BasePlugin): self._namespace += ":" logger.info("TokenRedis backend initialized (%s:%s)" % - (self._server, self._port)) + (self._server, self._port)) except ValueError: logger.error("The provided --token-source='%s' is not in the " "expected format [:[:[:[:]]]]" % @@ -375,5 +375,5 @@ class UnixDomainSocketDirectory(BasePlugin): return ['unix_socket', uds_path] except Exception as e: - logger.error("Error finding unix domain socket: %s" % str(e)) - return None + logger.error("Error finding unix domain socket: %s" % str(e)) + return None diff --git a/websockify/websocket.py b/websockify/websocket.py index 5cc0ddd..54d4ee1 100644 --- a/websockify/websocket.py +++ b/websockify/websocket.py @@ -772,7 +772,7 @@ class WebSocket: dtype = dtype.newbyteorder('>') mask = numpy.frombuffer(mask, dtype, count=(plen % 4)) data = numpy.frombuffer(buf, dtype, - offset=plen - (plen % 4), count=(plen % 4)) + offset=plen - (plen % 4), count=(plen % 4)) c = numpy.bitwise_xor(data, mask).tobytes() return b + c else: diff --git a/websockify/websocketproxy.py b/websockify/websocketproxy.py index ca90781..1dc0481 100644 --- a/websockify/websocketproxy.py +++ b/websockify/websocketproxy.py @@ -102,7 +102,7 @@ Traffic Legend: msg = "connecting to unix socket: %s" % self.server.unix_target else: msg = "connecting to: %s:%s" % ( - self.server.target_host, self.server.target_port) + self.server.target_host, self.server.target_port) if self.server.ssl_target: msg += " (using SSL)" @@ -110,10 +110,10 @@ Traffic Legend: try: tsock = websockifyserver.WebSockifyServer.socket(self.server.target_host, - self.server.target_port, - connect=True, - use_ssl=self.server.ssl_target, - unix_socket=self.server.unix_target) + self.server.target_port, + connect=True, + use_ssl=self.server.ssl_target, + unix_socket=self.server.unix_target) except Exception as e: self.log_message("Failed to connect to %s:%s: %s", self.server.target_host, self.server.target_port, e) @@ -136,7 +136,7 @@ Traffic Legend: tsock.close() if self.verbose: self.log_message("%s:%s: Closed target", - self.server.target_host, self.server.target_port) + self.server.target_host, self.server.target_port) def get_target(self, target_plugin): """ @@ -249,7 +249,7 @@ Traffic Legend: # TODO: What about blocking on client socket? if self.verbose: self.log_message("%s:%s: Client closed connection", - self.server.target_host, self.server.target_port) + self.server.target_host, self.server.target_port) raise self.CClose(closed['code'], closed['reason']) if target in outs: @@ -279,7 +279,7 @@ Traffic Legend: if self.verbose: self.log_message("%s:%s: Target closed connection", - self.server.target_host, self.server.target_port) + self.server.target_host, self.server.target_port) raise self.CClose(1000, "Target closed") cqueue.append(buf) @@ -351,7 +351,7 @@ class WebSocketProxy(websockifyserver.WebSockifyServer): self.wrap_times.append(time.time()) self.wrap_times.pop(0) self.cmd = subprocess.Popen( - self.wrap_cmd, env=os.environ, preexec_fn=_subprocess_setup) + self.wrap_cmd, env=os.environ, preexec_fn=_subprocess_setup) self.spawn_message = True def started(self): @@ -470,72 +470,72 @@ def websockify_init(): usage += " [source_addr:]source_port -- WRAP_COMMAND_LINE" parser = optparse.OptionParser(usage=usage) parser.add_option("--verbose", "-v", action="store_true", - help="verbose messages") + help="verbose messages") parser.add_option("--traffic", action="store_true", - help="per frame traffic") + help="per frame traffic") parser.add_option("--record", - help="record sessions to FILE.[session_number]", metavar="FILE") + help="record sessions to FILE.[session_number]", metavar="FILE") parser.add_option("--daemon", "-D", - dest="daemon", action="store_true", - help="become a daemon (background process)") + dest="daemon", action="store_true", + help="become a daemon (background process)") parser.add_option("--run-once", action="store_true", - help="handle a single WebSocket connection and exit") + help="handle a single WebSocket connection and exit") parser.add_option("--timeout", type=int, default=0, - help="after TIMEOUT seconds exit when not connected") + help="after TIMEOUT seconds exit when not connected") parser.add_option("--idle-timeout", type=int, default=0, - help="server exits after TIMEOUT seconds if there are no " - "active connections") + help="server exits after TIMEOUT seconds if there are no " + "active connections") parser.add_option("--cert", default="self.pem", - help="SSL certificate file") + help="SSL certificate file") parser.add_option("--key", default=None, - help="SSL key file (if separate from cert)") + help="SSL key file (if separate from cert)") parser.add_option("--key-password", default=None, - help="SSL key password") + help="SSL key password") parser.add_option("--ssl-only", action="store_true", - help="disallow non-encrypted client connections") + help="disallow non-encrypted client connections") parser.add_option("--ssl-target", action="store_true", - help="connect to SSL target as SSL client") + help="connect to SSL target as SSL client") parser.add_option("--verify-client", action="store_true", - help="require encrypted client to present a valid certificate " - "(needs Python 2.7.9 or newer or Python 3.4 or newer)") + help="require encrypted client to present a valid certificate " + "(needs Python 2.7.9 or newer or Python 3.4 or newer)") parser.add_option("--cafile", metavar="FILE", - help="file of concatenated certificates of authorities trusted " - "for validating clients (only effective with --verify-client). " - "If omitted, system default list of CAs is used.") + help="file of concatenated certificates of authorities trusted " + "for validating clients (only effective with --verify-client). " + "If omitted, system default list of CAs is used.") parser.add_option("--ssl-version", type="choice", default="default", - choices=["default", "tlsv1_1", "tlsv1_2", "tlsv1_3"], action="store", - help="minimum TLS version to use (default, tlsv1_1, tlsv1_2, tlsv1_3)") + choices=["default", "tlsv1_1", "tlsv1_2", "tlsv1_3"], action="store", + help="minimum TLS version to use (default, tlsv1_1, tlsv1_2, tlsv1_3)") parser.add_option("--ssl-ciphers", action="store", - help="list of ciphers allowed for connection. For a list of " - "supported ciphers run `openssl ciphers`") + help="list of ciphers allowed for connection. For a list of " + "supported ciphers run `openssl ciphers`") parser.add_option("--unix-listen", - help="listen to unix socket", metavar="FILE", default=None) + help="listen to unix socket", metavar="FILE", default=None) parser.add_option("--unix-listen-mode", default=None, - help="specify mode for unix socket (defaults to 0600)") + help="specify mode for unix socket (defaults to 0600)") parser.add_option("--unix-target", - help="connect to unix socket target", metavar="FILE") + help="connect to unix socket target", metavar="FILE") parser.add_option("--inetd", - help="inetd mode, receive listening socket from stdin", action="store_true") + help="inetd mode, receive listening socket from stdin", action="store_true") parser.add_option("--web", default=None, metavar="DIR", - help="run webserver on same port. Serve files from DIR.") + help="run webserver on same port. Serve files from DIR.") parser.add_option("--web-auth", action="store_true", - help="require authentication to access webserver.") + help="require authentication to access webserver.") parser.add_option("--wrap-mode", default="exit", metavar="MODE", - choices=["exit", "ignore", "respawn"], - help="action to take when the wrapped program exits " - "or daemonizes: exit (default), ignore, respawn") + choices=["exit", "ignore", "respawn"], + help="action to take when the wrapped program exits " + "or daemonizes: exit (default), ignore, respawn") parser.add_option("--prefer-ipv6", "-6", - action="store_true", dest="source_is_ipv6", - help="prefer IPv6 when resolving source_addr") + action="store_true", dest="source_is_ipv6", + help="prefer IPv6 when resolving source_addr") parser.add_option("--libserver", action="store_true", - help="use Python library SocketServer engine") + help="use Python library SocketServer engine") parser.add_option("--target-config", metavar="FILE", - dest="target_cfg", - help="Configuration file containing valid targets " - "in the form 'token: host:port' or, alternatively, a " - "directory containing configuration files of this form " - "(DEPRECATED: use `--token-plugin TokenFile --token-source " - " path/to/token/file` instead)") + dest="target_cfg", + help="Configuration file containing valid targets " + "in the form 'token: host:port' or, alternatively, a " + "directory containing configuration files of this form " + "(DEPRECATED: use `--token-plugin TokenFile --token-source " + " path/to/token/file` instead)") parser.add_option("--token-plugin", default=None, metavar="CLASS", help="use a Python class, usually one from websockify.token_plugins, " "such as TokenFile, to process tokens into host:port pairs") @@ -552,13 +552,13 @@ def websockify_init(): help="an argument to be passed to the auth plugin " "on instantiation") parser.add_option("--heartbeat", type=int, default=0, metavar="INTERVAL", - help="send a ping to the client every INTERVAL seconds") + help="send a ping to the client every INTERVAL seconds") parser.add_option("--log-file", metavar="FILE", - dest="log_file", - help="File where logs will be saved") + dest="log_file", + help="File where logs will be saved") parser.add_option("--syslog", default=None, metavar="SERVER", - help="Log to syslog server. SERVER can be local socket, " - "such as /dev/log, or a UDP host:port pair.") + help="Log to syslog server. SERVER can be local socket, " + "such as /dev/log, or a UDP host:port pair.") parser.add_option("--legacy-syslog", action="store_true", help="Use the old syslog protocol instead of RFC 5424. " "Use this if the messages produced by websockify seem abnormal.") diff --git a/websockify/websockifyserver.py b/websockify/websockifyserver.py index d2bc1fb..33a99f5 100644 --- a/websockify/websockifyserver.py +++ b/websockify/websockifyserver.py @@ -335,15 +335,15 @@ class WebSockifyServer(): pass def __init__(self, RequestHandlerClass, listen_fd=None, - listen_host='', listen_port=None, source_is_ipv6=False, - verbose=False, cert='', key='', key_password=None, ssl_only=None, - verify_client=False, cafile=None, - daemon=False, record='', web='', web_auth=False, - file_only=False, - run_once=False, timeout=0, idle_timeout=0, traffic=False, - tcp_keepalive=True, tcp_keepcnt=None, tcp_keepidle=None, - tcp_keepintvl=None, ssl_ciphers=None, ssl_options=0, - unix_listen=None, unix_listen_mode=None): + listen_host='', listen_port=None, source_is_ipv6=False, + verbose=False, cert='', key='', key_password=None, ssl_only=None, + verify_client=False, cafile=None, + daemon=False, record='', web='', web_auth=False, + file_only=False, + run_once=False, timeout=0, idle_timeout=0, traffic=False, + tcp_keepalive=True, tcp_keepcnt=None, tcp_keepidle=None, + tcp_keepintvl=None, ssl_ciphers=None, ssl_options=0, + unix_listen=None, unix_listen_mode=None): # settings self.RequestHandlerClass = RequestHandlerClass @@ -411,7 +411,7 @@ class WebSockifyServer(): self.msg(" - Listen on unix socket %s", self.unix_listen) else: self.msg(" - Listen on %s:%s", - self.listen_host, self.listen_port) + self.listen_host, self.listen_port) if self.web: if self.file_only: self.msg(" - Web server (no directory listings). Web root: %s", self.web) @@ -464,7 +464,7 @@ class WebSockifyServer(): if not unix_socket: addrs = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM, - socket.IPPROTO_TCP, flags) + socket.IPPROTO_TCP, flags) if not addrs: raise Exception("Could not resolve host '%s'" % host) addrs.sort(key=lambda x: x[0]) @@ -604,8 +604,8 @@ class WebSockifyServer(): else: context.set_default_verify_paths() retsock = context.wrap_socket( - sock, - server_side=True) + sock, + server_side=True) except ssl.SSLError: _, x, _ = sys.exc_info() if x.args[0] == ssl.SSL_ERROR_EOF: @@ -794,7 +794,7 @@ class WebSockifyServer(): time_elapsed = time.time() - self.launch_time if self.timeout and time_elapsed > self.timeout: self.msg('listener exit due to --timeout %s' - % self.timeout) + % self.timeout) break if self.idle_timeout: @@ -807,7 +807,7 @@ class WebSockifyServer(): if idle_time > self.idle_timeout and child_count == 0: self.msg('listener exit due to --idle-timeout %s' - % self.idle_timeout) + % self.idle_timeout) break try: @@ -842,13 +842,13 @@ class WebSockifyServer(): self.top_new_client(startsock, address) if self.ws_connection: self.msg('%s: exiting due to --run-once' - % address[0]) + % address[0]) break else: self.vmsg('%s: new handler Process' % address[0]) p = multiprocessing.Process( - target=self.top_new_client, - args=(startsock, address)) + target=self.top_new_client, + args=(startsock, address)) p.start() # child will not return From 21ed8747e25f196580ca8d2a5e309ecef73eabc5 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Mon, 9 Jun 2025 21:33:57 +0900 Subject: [PATCH 13/20] Replace bare except ... to ensure the logic works only valid exceptions. --- websockify/sysloghandler.py | 4 +--- websockify/token_plugins.py | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/websockify/sysloghandler.py b/websockify/sysloghandler.py index b85596b..eb8da6d 100644 --- a/websockify/sysloghandler.py +++ b/websockify/sysloghandler.py @@ -108,7 +108,5 @@ class WebsockifySysLogHandler(handlers.SysLogHandler): else: self.socket.sendall(msg) - except (KeyboardInterrupt, SystemExit): - raise - except: + except Exception: self.handleError(record) diff --git a/websockify/token_plugins.py b/websockify/token_plugins.py index 3c19206..9307356 100644 --- a/websockify/token_plugins.py +++ b/websockify/token_plugins.py @@ -157,10 +157,10 @@ class JWTTokenApi(BasePlugin): try: key.import_from_pem(key_data) - except: + except Exception: try: key.import_key(k=key_data.decode('utf-8'), kty='oct') - except: + except Exception: logger.error('Failed to correctly parse key data!') return None From 10889ff79b8cdd7658b39374e3470fecfc0bd802 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Mon, 9 Jun 2025 21:37:30 +0900 Subject: [PATCH 14/20] Use explicit import ... to avoid pep8 errors caused by implicit import of a few modules. --- websockify/websockifyserver.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/websockify/websockifyserver.py b/websockify/websockifyserver.py index 33a99f5..fffc3be 100644 --- a/websockify/websockifyserver.py +++ b/websockify/websockifyserver.py @@ -17,13 +17,17 @@ import multiprocessing from http.server import SimpleHTTPRequestHandler # Degraded functionality if these imports are missing -for mod, msg in [('ssl', 'TLS/SSL/wss is disabled'), - ('resource', 'daemonizing is disabled')]: - try: - globals()[mod] = __import__(mod) - except ImportError: - globals()[mod] is None - print("WARNING: no '%s' module, %s" % (mod, msg)) +try: + import ssl +except ImportError: + ssl = None + print("WARNING: no 'ssl' module, TLS/SSL/wss is disabled") + +try: + import resource +except ImportError: + resource = None + print("WARNING: no 'resource' module, daemonizing is disabled") if sys.platform == 'win32': # make sockets pickle-able/inheritable From 9a38c0ce17fa24a42838060a12f7d4e92e1254e3 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Thu, 12 Jun 2025 22:40:28 +0900 Subject: [PATCH 15/20] Fix flake8 caused by top level imports --- tests/echo.py | 13 ++++++++++--- tests/echo_client.py | 11 +++++++---- tests/load.py | 14 ++++++++++++-- websockify/sysloghandler.py | 5 ++++- websockify/websocketproxy.py | 19 +++++++++++++++---- websockify/websockifyserver.py | 11 +++++++++-- 6 files changed, 57 insertions(+), 16 deletions(-) diff --git a/tests/echo.py b/tests/echo.py index addce6a..710fbff 100755 --- a/tests/echo.py +++ b/tests/echo.py @@ -1,5 +1,5 @@ #!/usr/bin/env python - +# flake8: noqa: E402 ''' A WebSocket server that echos back whatever it receives from the client. Copyright 2010 Joel Martin @@ -10,9 +10,16 @@ openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem as taken from http://docs.python.org/dev/library/ssl.html#certificates ''' -import os, sys, select, optparse, logging +import logging +import optparse +import os +import select +import sys + sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) -from websockify.websockifyserver import WebSockifyServer, WebSockifyRequestHandler + +from websockify.websockifyserver import WebSockifyServer +from websockify.websockifyserver import WebSockifyRequestHandler class WebSocketEcho(WebSockifyRequestHandler): diff --git a/tests/echo_client.py b/tests/echo_client.py index ce0c0d2..dbc799d 100755 --- a/tests/echo_client.py +++ b/tests/echo_client.py @@ -1,13 +1,16 @@ #!/usr/bin/env python +# flake8: noqa: E402 -import os -import sys import optparse +import os import select +import sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) -from websockify.websocket import WebSocket, \ - WebSocketWantReadError, WebSocketWantWriteError + +from websockify.websocket import WebSocket +from websockify.websocket import WebSocketWantReadError +from websockify.websocket import WebSocketWantWriteError parser = optparse.OptionParser(usage="%prog URL") (opts, args) = parser.parse_args() diff --git a/tests/load.py b/tests/load.py index 27d1000..8f0fc79 100755 --- a/tests/load.py +++ b/tests/load.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# flake8: noqa: E402 ''' WebSocket server-side load test program. Sends and receives traffic @@ -6,9 +7,18 @@ that has a random payload (length and content) that is checksummed and given a sequence number. Any errors are reported and counted. ''' -import sys, os, select, random, time, optparse, logging +import logging +import optparse +import os +import random +import select +import sys +import time + sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) -from websockify.websockifyserver import WebSockifyServer, WebSockifyRequestHandler + +from websockify.websockifyserver import WebSockifyRequestHandler +from websockify.websockifyserver import WebSockifyServer class WebSocketLoadServer(WebSockifyServer): diff --git a/websockify/sysloghandler.py b/websockify/sysloghandler.py index eb8da6d..14e4f03 100644 --- a/websockify/sysloghandler.py +++ b/websockify/sysloghandler.py @@ -1,4 +1,7 @@ -import logging.handlers as handlers, socket, os, time +import logging.handlers as handlers +import os +import socket +import time class WebsockifySysLogHandler(handlers.SysLogHandler): diff --git a/websockify/websocketproxy.py b/websockify/websocketproxy.py index 1dc0481..40658b5 100644 --- a/websockify/websocketproxy.py +++ b/websockify/websocketproxy.py @@ -11,14 +11,25 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates ''' -import signal, socket, optparse, time, os, sys, subprocess, logging, errno, ssl, stat -from socketserver import ThreadingMixIn +import errno from http.server import HTTPServer - +import logging +import optparse +import os import select +import signal +import socket +from socketserver import ThreadingMixIn +import ssl +import stat +import subprocess +import sys +import time +from urllib.parse import parse_qs +from urllib.parse import urlparse + from websockify import websockifyserver from websockify import auth_plugins as auth -from urllib.parse import parse_qs, urlparse class ProxyRequestHandler(websockifyserver.WebSockifyRequestHandler): diff --git a/websockify/websockifyserver.py b/websockify/websockifyserver.py index fffc3be..975d011 100644 --- a/websockify/websockifyserver.py +++ b/websockify/websockifyserver.py @@ -12,9 +12,16 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates ''' -import os, sys, time, errno, signal, socket, select, logging -import multiprocessing +import errno from http.server import SimpleHTTPRequestHandler +import logging +import multiprocessing +import os +import select +import signal +import socket +import sys +import time # Degraded functionality if these imports are missing try: From c00cfc735a2fd6f67ad841db543b0ce4800e60b3 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Thu, 12 Jun 2025 22:13:58 +0900 Subject: [PATCH 16/20] Avoid nested import ... to get rid of F401 error by flake8. --- tests/test_token_plugins.py | 9 ++++++--- websockify/token_plugins.py | 13 +++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/tests/test_token_plugins.py b/tests/test_token_plugins.py index 363ae35..4e029eb 100644 --- a/tests/test_token_plugins.py +++ b/tests/test_token_plugins.py @@ -7,6 +7,11 @@ import unittest from unittest.mock import patch, MagicMock from jwcrypto import jwt, jwk +try: + import redis +except ImportError: + redis = None + from websockify.token_plugins import parse_source_args, ReadOnlyTokenFile, JWTTokenApi, TokenRedis @@ -206,9 +211,7 @@ class JWSTokenTestCase(unittest.TestCase): class TokenRedisTestCase(unittest.TestCase): def setUp(self): - try: - import redis - except ImportError: + if redis is None: patcher = patch.dict(sys.modules, {'redis': MagicMock()}) patcher.start() self.addCleanup(patcher.stop) diff --git a/websockify/token_plugins.py b/websockify/token_plugins.py index 9307356..ba6e1d3 100644 --- a/websockify/token_plugins.py +++ b/websockify/token_plugins.py @@ -5,6 +5,11 @@ import re import json from pathlib import Path +try: + import redis +except ImportError: + redis = None + logger = logging.getLogger(__name__) _SOURCE_SPLIT_REGEX = re.compile( @@ -256,9 +261,7 @@ class TokenRedis(BasePlugin): pip install redis """ def __init__(self, src): - try: - import redis - except ImportError: + if redis is None: logger.error("Unable to load redis module") sys.exit() # Default values @@ -314,9 +317,7 @@ class TokenRedis(BasePlugin): sys.exit() def lookup(self, token): - try: - import redis - except ImportError: + if redis is None: logger.error("package redis not found, are you sure you've installed them correctly?") sys.exit() From e2d2931237c80fe1c5a9ddc5d17d4d096084b9f6 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Thu, 12 Jun 2025 22:48:38 +0900 Subject: [PATCH 17/20] Ignore false flake8 error --- tests/echo.py | 2 +- websockify/__init__.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/echo.py b/tests/echo.py index 710fbff..4df6011 100755 --- a/tests/echo.py +++ b/tests/echo.py @@ -35,7 +35,7 @@ class WebSocketEcho(WebSockifyRequestHandler): cqueue = [] c_pend = 0 - cpartial = "" + cpartial = "" # noqa: F841 rlist = [self.request] while True: diff --git a/websockify/__init__.py b/websockify/__init__.py index 37a6f47..f9f8c7c 100644 --- a/websockify/__init__.py +++ b/websockify/__init__.py @@ -1,2 +1,2 @@ -from websockify.websocket import * -from websockify.websocketproxy import * +from websockify.websocket import * # noqa: F401,F403 +from websockify.websocketproxy import * # noqa: F401,F403 From c269a9debc36ab5ac692a12b048ab57f26f48317 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Thu, 12 Jun 2025 22:52:11 +0900 Subject: [PATCH 18/20] Extend max line length The existing code does not expect 79 characters limit. Extend the limit to avoid mass update. --- tox.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tox.ini b/tox.ini index 3d175eb..a7c6534 100644 --- a/tox.ini +++ b/tox.ini @@ -15,3 +15,6 @@ deps = -r{toxinidir}/test-requirements.txt [testenv:pep8] commands = flake8 deps = flake8 + +[flake8] +max-line-length = 160 From 62fdd838ef1052eaae61167ae65d10f97206b806 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Thu, 3 Jul 2025 23:03:06 +0900 Subject: [PATCH 19/20] Ignore a few controversial errors --- tox.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tox.ini b/tox.ini index a7c6534..f1a19e1 100644 --- a/tox.ini +++ b/tox.ini @@ -18,3 +18,7 @@ deps = flake8 [flake8] max-line-length = 160 +# E129 visually indented line with same indent as next logical line +# W503 line break before binary operator +# W504 line break after binary operator +ignore = E129,W503,W504 From c37fad851fe5934a9014c6d5133e77fd0898ce77 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Mon, 9 Jun 2025 21:41:53 +0900 Subject: [PATCH 20/20] Add workflow to run pep8 check --- .github/workflows/lint.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..c481949 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,19 @@ +name: Lint + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Update pip and setuptools + run: | + python -m pip install --upgrade pip + python -m pip install setuptools + - name: Install dependencies + run: | + python -m pip install flake8 + - name: Lint with flake8 + run: | + flake8