diff --git a/lib/websocket/__init__.py b/lib/websocket/__init__.py index a5a39502..588a8f21 100644 --- a/lib/websocket/__init__.py +++ b/lib/websocket/__init__.py @@ -17,10 +17,10 @@ See the License for the specific language governing permissions and limitations under the License. """ from ._abnf import * -from ._app import WebSocketApp +from ._app import WebSocketApp, setReconnect from ._core import * from ._exceptions import * from ._logging import * from ._socket import * -__version__ = "1.3.2" +__version__ = "1.4.2" diff --git a/lib/websocket/_app.py b/lib/websocket/_app.py index da49ec78..e0a88e2f 100644 --- a/lib/websocket/_app.py +++ b/lib/websocket/_app.py @@ -1,9 +1,11 @@ +import inspect import selectors import sys import threading import time import traceback from ._abnf import ABNF +from ._url import parse_url from ._core import WebSocket, getdefaulttimeout from ._exceptions import * from . import _logging @@ -29,15 +31,40 @@ limitations under the License. __all__ = ["WebSocketApp"] +RECONNECT = 0 -class Dispatcher: + +def setReconnect(reconnectInterval): + global RECONNECT + RECONNECT = reconnectInterval + + +class DispatcherBase: """ - Dispatcher + DispatcherBase """ def __init__(self, app, ping_timeout): self.app = app self.ping_timeout = ping_timeout + def timeout(self, seconds, callback): + time.sleep(seconds) + callback() + + def reconnect(self, seconds, reconnector): + try: + while True: + _logging.info("reconnect() - retrying in %s seconds [%s frames in stack]" % (seconds, len(inspect.stack()))) + time.sleep(seconds) + reconnector(reconnecting=True) + except KeyboardInterrupt as e: + _logging.info("User exited %s" % (e,)) + + +class Dispatcher(DispatcherBase): + """ + Dispatcher + """ def read(self, sock, read_callback, check_callback): while self.app.keep_running: sel = selectors.DefaultSelector() @@ -51,14 +78,10 @@ class Dispatcher: sel.close() -class SSLDispatcher: +class SSLDispatcher(DispatcherBase): """ SSLDispatcher """ - def __init__(self, app, ping_timeout): - self.app = app - self.ping_timeout = ping_timeout - def read(self, sock, read_callback, check_callback): while self.app.keep_running: r = self.select() @@ -90,10 +113,17 @@ class WrappedDispatcher: self.app = app self.ping_timeout = ping_timeout self.dispatcher = dispatcher + dispatcher.signal(2, dispatcher.abort) # keyboard interrupt def read(self, sock, read_callback, check_callback): self.dispatcher.read(sock, read_callback) - self.ping_timeout and self.dispatcher.timeout(self.ping_timeout, check_callback) + self.ping_timeout and self.timeout(self.ping_timeout, check_callback) + + def timeout(self, seconds, callback): + self.dispatcher.timeout(seconds, callback) + + def reconnect(self, seconds, reconnector): + self.timeout(seconds, reconnector) class WebSocketApp: @@ -186,6 +216,7 @@ class WebSocketApp: self.last_pong_tm = 0 self.subprotocols = subprotocols self.prepared_socket = socket + self.has_errored = False def send(self, data, opcode=ABNF.OPCODE_TEXT): """ @@ -228,9 +259,10 @@ class WebSocketApp: ping_payload="", http_proxy_host=None, http_proxy_port=None, http_no_proxy=None, http_proxy_auth=None, + http_proxy_timeout=None, skip_utf8_validation=False, host=None, origin=None, dispatcher=None, - suppress_origin=False, proxy_type=None): + suppress_origin=False, proxy_type=None, reconnect=None): """ Run event loop for WebSocket framework. @@ -258,6 +290,10 @@ class WebSocketApp: HTTP proxy port. If not set, set to 80. http_no_proxy: list Whitelisted host names that don't use the proxy. + http_proxy_timeout: int or float + HTTP proxy timeout, default is 60 sec as per python-socks. + http_proxy_auth: tuple + HTTP proxy auth information. tuple of username and password. Default is None. skip_utf8_validation: bool skip utf8 validation. host: str @@ -268,6 +304,10 @@ class WebSocketApp: customize reading data from socket. suppress_origin: bool suppress outputting origin header. + proxy_type: str + type of proxy from: http, socks4, socks4a, socks5, socks5h + reconnect: int + delay interval when reconnecting Returns ------- @@ -276,6 +316,9 @@ class WebSocketApp: True if any other exception was raised during a loop. """ + if reconnect is None: + reconnect = RECONNECT + if ping_timeout is not None and ping_timeout <= 0: raise WebSocketException("Ensure ping_timeout > 0") if ping_interval is not None and ping_interval < 0: @@ -317,84 +360,105 @@ class WebSocketApp: # Finally call the callback AFTER all teardown is complete self._callback(self.on_close, close_status_code, close_reason) - try: + def setSock(reconnecting=False): self.sock = WebSocket( self.get_mask_key, sockopt=sockopt, sslopt=sslopt, fire_cont_frame=self.on_cont_message is not None, skip_utf8_validation=skip_utf8_validation, enable_multithread=True) self.sock.settimeout(getdefaulttimeout()) - self.sock.connect( - self.url, header=self.header, cookie=self.cookie, - http_proxy_host=http_proxy_host, - http_proxy_port=http_proxy_port, http_no_proxy=http_no_proxy, - http_proxy_auth=http_proxy_auth, subprotocols=self.subprotocols, - host=host, origin=origin, suppress_origin=suppress_origin, - proxy_type=proxy_type, socket=self.prepared_socket) - dispatcher = self.create_dispatcher(ping_timeout, dispatcher) + try: + self.sock.connect( + self.url, header=self.header, cookie=self.cookie, + http_proxy_host=http_proxy_host, + http_proxy_port=http_proxy_port, http_no_proxy=http_no_proxy, + http_proxy_auth=http_proxy_auth, http_proxy_timeout=http_proxy_timeout, + subprotocols=self.subprotocols, + host=host, origin=origin, suppress_origin=suppress_origin, + proxy_type=proxy_type, socket=self.prepared_socket) - self._callback(self.on_open) + self._callback(self.on_open) - if ping_interval: - event = threading.Event() - thread = threading.Thread( - target=self._send_ping, args=(ping_interval, event, ping_payload)) - thread.daemon = True - thread.start() + _logging.warning("websocket connected") + dispatcher.read(self.sock.sock, read, check) + except (WebSocketConnectionClosedException, ConnectionRefusedError, KeyboardInterrupt, SystemExit, Exception) as e: + _logging.error("%s - %s" % (e, reconnect and "reconnecting" or "goodbye")) + reconnecting or handleDisconnect(e) - def read(): - if not self.keep_running: - return teardown() + def read(): + if not self.keep_running: + return teardown() + try: op_code, frame = self.sock.recv_data_frame(True) - if op_code == ABNF.OPCODE_CLOSE: - return teardown(frame) - elif op_code == ABNF.OPCODE_PING: - self._callback(self.on_ping, frame.data) - elif op_code == ABNF.OPCODE_PONG: - self.last_pong_tm = time.time() - self._callback(self.on_pong, frame.data) - elif op_code == ABNF.OPCODE_CONT and self.on_cont_message: - self._callback(self.on_data, frame.data, - frame.opcode, frame.fin) - self._callback(self.on_cont_message, - frame.data, frame.fin) + except (WebSocketConnectionClosedException, KeyboardInterrupt) as e: + if custom_dispatcher: + return handleDisconnect(e) else: - data = frame.data - if op_code == ABNF.OPCODE_TEXT: - data = data.decode("utf-8") - self._callback(self.on_data, data, frame.opcode, True) - self._callback(self.on_message, data) + raise e + if op_code == ABNF.OPCODE_CLOSE: + return teardown(frame) + elif op_code == ABNF.OPCODE_PING: + self._callback(self.on_ping, frame.data) + elif op_code == ABNF.OPCODE_PONG: + self.last_pong_tm = time.time() + self._callback(self.on_pong, frame.data) + elif op_code == ABNF.OPCODE_CONT and self.on_cont_message: + self._callback(self.on_data, frame.data, + frame.opcode, frame.fin) + self._callback(self.on_cont_message, + frame.data, frame.fin) + else: + data = frame.data + if op_code == ABNF.OPCODE_TEXT: + data = data.decode("utf-8") + self._callback(self.on_data, data, frame.opcode, True) + self._callback(self.on_message, data) - return True + return True - def check(): - if (ping_timeout): - has_timeout_expired = time.time() - self.last_ping_tm > ping_timeout - has_pong_not_arrived_after_last_ping = self.last_pong_tm - self.last_ping_tm < 0 - has_pong_arrived_too_late = self.last_pong_tm - self.last_ping_tm > ping_timeout + def check(): + if (ping_timeout): + has_timeout_expired = time.time() - self.last_ping_tm > ping_timeout + has_pong_not_arrived_after_last_ping = self.last_pong_tm - self.last_ping_tm < 0 + has_pong_arrived_too_late = self.last_pong_tm - self.last_ping_tm > ping_timeout - if (self.last_ping_tm and - has_timeout_expired and - (has_pong_not_arrived_after_last_ping or has_pong_arrived_too_late)): - raise WebSocketTimeoutException("ping/pong timed out") - return True + if (self.last_ping_tm and + has_timeout_expired and + (has_pong_not_arrived_after_last_ping or has_pong_arrived_too_late)): + raise WebSocketTimeoutException("ping/pong timed out") + return True - dispatcher.read(self.sock.sock, read, check) - return False - except (Exception, KeyboardInterrupt, SystemExit) as e: + def handleDisconnect(e): + self.has_errored = True self._callback(self.on_error, e) if isinstance(e, SystemExit): # propagate SystemExit further raise - teardown() - return not isinstance(e, KeyboardInterrupt) + if reconnect and not isinstance(e, KeyboardInterrupt): + _logging.info("websocket disconnected (retrying in %s seconds) [%s frames in stack]" % (reconnect, len(inspect.stack()))) + dispatcher.reconnect(reconnect, setSock) + else: + teardown() - def create_dispatcher(self, ping_timeout, dispatcher=None): + custom_dispatcher = bool(dispatcher) + dispatcher = self.create_dispatcher(ping_timeout, dispatcher, parse_url(self.url)[3]) + + if ping_interval: + event = threading.Event() + thread = threading.Thread( + target=self._send_ping, args=(ping_interval, event, ping_payload)) + thread.daemon = True + thread.start() + + setSock() + return self.has_errored + + def create_dispatcher(self, ping_timeout, dispatcher=None, is_ssl=False): if dispatcher: # If custom dispatcher is set, use WrappedDispatcher return WrappedDispatcher(self, ping_timeout, dispatcher) timeout = ping_timeout or 10 - if self.sock.is_ssl(): + if is_ssl: return SSLDispatcher(self, timeout) return Dispatcher(self, timeout) diff --git a/lib/websocket/_core.py b/lib/websocket/_core.py index c36b7800..1d688294 100644 --- a/lib/websocket/_core.py +++ b/lib/websocket/_core.py @@ -46,8 +46,11 @@ class WebSocket: >>> import websocket >>> ws = websocket.WebSocket() - >>> ws.connect("ws://echo.websocket.org") + >>> ws.connect("ws://echo.websocket.events") + >>> ws.recv() + 'echo.websocket.events sponsored by Lob.com' >>> ws.send("Hello, Server") + 19 >>> ws.recv() 'Hello, Server' >>> ws.close() @@ -203,7 +206,7 @@ class WebSocket: If you set "header" list object, you can set your own custom header. >>> ws = WebSocket() - >>> ws.connect("ws://echo.websocket.org/", + >>> ws.connect("ws://echo.websocket.events", ... header=["User-Agent: MyProgram", ... "x-custom: header"]) @@ -233,6 +236,8 @@ class WebSocket: Whitelisted host names that don't use the proxy. http_proxy_auth: tuple HTTP proxy auth information. Tuple of username and password. Default is None. + http_proxy_timeout: int or float + HTTP proxy timeout, default is 60 sec as per python-socks. redirect_limit: int Number of redirects to follow. subprotocols: list @@ -281,7 +286,7 @@ class WebSocket: """ Send the data frame. - >>> ws = create_connection("ws://echo.websocket.org/") + >>> ws = create_connection("ws://echo.websocket.events") >>> frame = ABNF.create_frame("Hello", ABNF.OPCODE_TEXT) >>> ws.send_frame(frame) >>> cont_frame = ABNF.create_frame("My name is ", ABNF.OPCODE_CONT, 0) @@ -541,7 +546,7 @@ def create_connection(url, timeout=None, class_=WebSocket, **options): You can customize using 'options'. If you set "header" list object, you can set your own custom header. - >>> conn = create_connection("ws://echo.websocket.org/", + >>> conn = create_connection("ws://echo.websocket.events", ... header=["User-Agent: MyProgram", ... "x-custom: header"]) @@ -572,6 +577,8 @@ def create_connection(url, timeout=None, class_=WebSocket, **options): Whitelisted host names that don't use the proxy. http_proxy_auth: tuple HTTP proxy auth information. tuple of username and password. Default is None. + http_proxy_timeout: int or float + HTTP proxy timeout, default is 60 sec as per python-socks. enable_multithread: bool Enable lock for multithread. redirect_limit: int diff --git a/lib/websocket/_handshake.py b/lib/websocket/_handshake.py index f032c4b5..07a4cfb2 100644 --- a/lib/websocket/_handshake.py +++ b/lib/websocket/_handshake.py @@ -81,7 +81,7 @@ def _get_handshake_headers(resource, url, host, port, options): hostport = _pack_hostname(host) else: hostport = "%s:%d" % (_pack_hostname(host), port) - if "host" in options and options["host"] is not None: + if options.get("host"): headers.append("Host: %s" % options["host"]) else: headers.append("Host: %s" % hostport) @@ -89,7 +89,7 @@ def _get_handshake_headers(resource, url, host, port, options): # scheme indicates whether http or https is used in Origin # The same approach is used in parse_url of _url.py to set default port scheme, url = url.split(":", 1) - if "suppress_origin" not in options or not options["suppress_origin"]: + if not options.get("suppress_origin"): if "origin" in options and options["origin"] is not None: headers.append("Origin: %s" % options["origin"]) elif scheme == "wss": @@ -100,16 +100,15 @@ def _get_handshake_headers(resource, url, host, port, options): key = _create_sec_websocket_key() # Append Sec-WebSocket-Key & Sec-WebSocket-Version if not manually specified - if 'header' not in options or 'Sec-WebSocket-Key' not in options['header']: - key = _create_sec_websocket_key() + if not options.get('header') or 'Sec-WebSocket-Key' not in options['header']: headers.append("Sec-WebSocket-Key: %s" % key) else: key = options['header']['Sec-WebSocket-Key'] - if 'header' not in options or 'Sec-WebSocket-Version' not in options['header']: + if not options.get('header') or 'Sec-WebSocket-Version' not in options['header']: headers.append("Sec-WebSocket-Version: %s" % VERSION) - if 'connection' not in options or options['connection'] is None: + if not options.get('connection'): headers.append('Connection: Upgrade') else: headers.append(options['connection']) @@ -118,8 +117,8 @@ def _get_handshake_headers(resource, url, host, port, options): if subprotocols: headers.append("Sec-WebSocket-Protocol: %s" % ",".join(subprotocols)) - if "header" in options: - header = options["header"] + header = options.get("header") + if header: if isinstance(header, dict): header = [ ": ".join([k, v]) diff --git a/lib/websocket/_http.py b/lib/websocket/_http.py index 04f32f5a..17d3f8aa 100644 --- a/lib/websocket/_http.py +++ b/lib/websocket/_http.py @@ -23,7 +23,7 @@ import sys from ._exceptions import * from ._logging import * -from ._socket import* +from ._socket import * from ._ssl_compat import * from ._url import * @@ -59,7 +59,7 @@ class proxy_info: self.no_proxy = options.get("http_no_proxy", None) self.proxy_protocol = options.get("proxy_type", "http") # Note: If timeout not specified, default python-socks timeout is 60 seconds - self.proxy_timeout = options.get("timeout", None) + self.proxy_timeout = options.get("http_proxy_timeout", None) if self.proxy_protocol not in ['http', 'socks4', 'socks4a', 'socks5', 'socks5h']: raise ProxyError("Only http, socks4, socks5 proxy protocols are supported") else: @@ -114,22 +114,22 @@ def connect(url, options, proxy, socket): if proxy.proxy_host and not socket and not (proxy.proxy_protocol == "http"): return _start_proxied_socket(url, options, proxy) - hostname, port, resource, is_secure = parse_url(url) + hostname, port_from_url, resource, is_secure = parse_url(url) if socket: - return socket, (hostname, port, resource) + return socket, (hostname, port_from_url, resource) addrinfo_list, need_tunnel, auth = _get_addrinfo_list( - hostname, port, is_secure, proxy) + hostname, port_from_url, is_secure, proxy) if not addrinfo_list: raise WebSocketException( - "Host not found.: " + hostname + ":" + str(port)) + "Host not found.: " + hostname + ":" + str(port_from_url)) sock = None try: sock = _open_socket(addrinfo_list, options.sockopt, options.timeout) if need_tunnel: - sock = _tunnel(sock, hostname, port, auth) + sock = _tunnel(sock, hostname, port_from_url, auth) if is_secure: if HAVE_SSL: @@ -137,7 +137,7 @@ def connect(url, options, proxy, socket): else: raise WebSocketException("SSL not available.") - return sock, (hostname, port, resource) + return sock, (hostname, port_from_url, resource) except: if sock: sock.close() @@ -184,17 +184,16 @@ def _open_socket(addrinfo_list, sockopt, timeout): try: sock.connect(address) except socket.error as error: + sock.close() error.remote_ip = str(address[0]) try: eConnRefused = (errno.ECONNREFUSED, errno.WSAECONNREFUSED, errno.ENETUNREACH) - except: + except AttributeError: eConnRefused = (errno.ECONNREFUSED, errno.ENETUNREACH) if error.errno in eConnRefused: err = error continue else: - if sock: - sock.close() raise error else: break diff --git a/lib/websocket/_logging.py b/lib/websocket/_logging.py index df690dcc..3921111d 100644 --- a/lib/websocket/_logging.py +++ b/lib/websocket/_logging.py @@ -35,7 +35,7 @@ __all__ = ["enableTrace", "dump", "error", "warning", "debug", "trace", "isEnabledForError", "isEnabledForDebug", "isEnabledForTrace"] -def enableTrace(traceable, handler=logging.StreamHandler()): +def enableTrace(traceable, handler=logging.StreamHandler(), level="DEBUG"): """ Turn on/off the traceability. @@ -48,7 +48,7 @@ def enableTrace(traceable, handler=logging.StreamHandler()): _traceEnabled = traceable if traceable: _logger.addHandler(handler) - _logger.setLevel(logging.DEBUG) + _logger.setLevel(getattr(logging, level)) def dump(title, message): @@ -70,6 +70,10 @@ def debug(msg): _logger.debug(msg) +def info(msg): + _logger.info(msg) + + def trace(msg): if _traceEnabled: _logger.debug(msg) diff --git a/lib/websocket/tests/test_app.py b/lib/websocket/tests/test_app.py index ac2a7dd5..5526d3ec 100644 --- a/lib/websocket/tests/test_app.py +++ b/lib/websocket/tests/test_app.py @@ -80,7 +80,8 @@ class WebSocketAppTest(unittest.TestCase): app = ws.WebSocketApp('ws://127.0.0.1:' + LOCAL_WS_SERVER_PORT, on_open=on_open, on_close=on_close, on_message=on_message) app.run_forever() - @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled") +# @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled") + @unittest.skipUnless(False, "Test disabled for now (requires rel)") def testRunForeverDispatcher(self): """ A WebSocketApp should keep running as long as its self.keep_running is not False (in the boolean context). @@ -98,7 +99,9 @@ class WebSocketAppTest(unittest.TestCase): self.close() app = ws.WebSocketApp('ws://127.0.0.1:' + LOCAL_WS_SERVER_PORT, on_open=on_open, on_message=on_message) - app.run_forever(dispatcher="Dispatcher") + app.run_forever(dispatcher="Dispatcher") # doesn't work +# app.run_forever(dispatcher=rel) # would work +# rel.dispatch() @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled") def testRunForeverTeardownCleanExit(self): @@ -188,7 +191,7 @@ class WebSocketAppTest(unittest.TestCase): """ Test WebSocketApp binary opcode """ # The lack of wss:// in the URL below is on purpose - app = ws.WebSocketApp('streaming.vn.teslamotors.com/streaming/') + app = ws.WebSocketApp('wss://streaming.vn.teslamotors.com/streaming/') app.run_forever(ping_interval=2, ping_timeout=1, ping_payload="Ping payload") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") diff --git a/lib/websocket/tests/test_http.py b/lib/websocket/tests/test_http.py index 649e0fe6..ffcbde25 100644 --- a/lib/websocket/tests/test_http.py +++ b/lib/websocket/tests/test_http.py @@ -105,15 +105,15 @@ class HttpTest(unittest.TestCase): if ws._http.HAVE_PYTHON_SOCKS: # Need this check, otherwise case where python_socks is not installed triggers # websocket._exceptions.WebSocketException: Python Socks is needed for SOCKS proxying but is not available - self.assertRaises((ProxyTimeoutError, OSError), _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks4", timeout=1)) - self.assertRaises((ProxyTimeoutError, OSError), _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks4a", timeout=1)) - self.assertRaises((ProxyTimeoutError, OSError), _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks5", timeout=1)) - self.assertRaises((ProxyTimeoutError, OSError), _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks5h", timeout=1)) - self.assertRaises(ProxyConnectionError, connect, "wss://example.com", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port=9999, proxy_type="socks4", timeout=1), None) + self.assertRaises((ProxyTimeoutError, OSError), _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks4", http_proxy_timeout=1)) + self.assertRaises((ProxyTimeoutError, OSError), _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks4a", http_proxy_timeout=1)) + self.assertRaises((ProxyTimeoutError, OSError), _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks5", http_proxy_timeout=1)) + self.assertRaises((ProxyTimeoutError, OSError), _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks5h", http_proxy_timeout=1)) + self.assertRaises(ProxyConnectionError, connect, "wss://example.com", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port=9999, proxy_type="socks4", http_proxy_timeout=1), None) self.assertRaises(TypeError, _get_addrinfo_list, None, 80, True, proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="9999", proxy_type="http")) self.assertRaises(TypeError, _get_addrinfo_list, None, 80, True, proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="9999", proxy_type="http")) - self.assertRaises(socket.timeout, connect, "wss://google.com", OptsList(), proxy_info(http_proxy_host="8.8.8.8", http_proxy_port=9999, proxy_type="http", timeout=1), None) + self.assertRaises(socket.timeout, connect, "wss://google.com", OptsList(), proxy_info(http_proxy_host="8.8.8.8", http_proxy_port=9999, proxy_type="http", http_proxy_timeout=1), None) self.assertEqual( connect("wss://google.com", OptsList(), proxy_info(http_proxy_host="8.8.8.8", http_proxy_port=8080, proxy_type="http"), True), (True, ("google.com", 443, "/"))) diff --git a/lib/websocket/tests/test_websocket.py b/lib/websocket/tests/test_websocket.py index ae42ab54..d47d73e5 100644 --- a/lib/websocket/tests/test_websocket.py +++ b/lib/websocket/tests/test_websocket.py @@ -432,7 +432,7 @@ class HandshakeTest(unittest.TestCase): self.assertRaises(ws._exceptions.WebSocketBadStatusException, websock3.connect, "wss://api.bitfinex.com/ws/2", cookie="chocolate", origin="testing_websockets.com", - host="echo.websocket.org/websocket-client-test", + host="echo.websocket.events/websocket-client-test", subprotocols=["testproto"], connection="Upgrade", header={"CustomHeader1":"123", diff --git a/requirements.txt b/requirements.txt index 877a8aca..c505d8ae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -47,7 +47,7 @@ tzdata==2022.6 tzlocal==4.2 urllib3==1.26.12 webencodings==0.5.1 -websocket-client==1.3.2 +websocket-client==1.4.2 xmltodict==0.13.0 zipp==3.10.0