Bump websocket-client from 1.5.1 to 1.6.2 (#2122)

* Bump websocket-client from 1.5.1 to 1.6.2

Bumps [websocket-client](https://github.com/websocket-client/websocket-client) from 1.5.1 to 1.6.2.
- [Release notes](https://github.com/websocket-client/websocket-client/releases)
- [Changelog](https://github.com/websocket-client/websocket-client/blob/master/ChangeLog)
- [Commits](https://github.com/websocket-client/websocket-client/compare/v1.5.1...v1.6.2)

---
updated-dependencies:
- dependency-name: websocket-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update websocket-client==1.6.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
This commit is contained in:
dependabot[bot] 2023-08-23 21:45:28 -07:00 committed by GitHub
parent c93f470371
commit eac78a3047
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 295 additions and 260 deletions

View file

@ -2,7 +2,7 @@
__init__.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -23,4 +23,4 @@ from ._exceptions import *
from ._logging import *
from ._socket import *
__version__ = "1.5.1"
__version__ = "1.6.2"

View file

@ -11,7 +11,7 @@ from threading import Lock
_abnf.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -33,14 +33,14 @@ try:
# Note that wsaccel is unmaintained.
from wsaccel.xormask import XorMaskerSimple
def _mask(_m, _d):
def _mask(_m, _d) -> bytes:
return XorMaskerSimple(_m).process(_d)
except ImportError:
# wsaccel is not available, use websocket-client _mask()
native_byteorder = sys.byteorder
def _mask(mask_value, data_value):
def _mask(mask_value: array.array, data_value: array.array) -> bytes:
datalen = len(data_value)
data_value = int.from_bytes(data_value, native_byteorder)
mask_value = int.from_bytes(mask_value * (datalen // 4) + mask_value[: datalen % 4], native_byteorder)
@ -131,8 +131,8 @@ class ABNF:
LENGTH_16 = 1 << 16
LENGTH_63 = 1 << 63
def __init__(self, fin=0, rsv1=0, rsv2=0, rsv3=0,
opcode=OPCODE_TEXT, mask=1, data=""):
def __init__(self, fin: int = 0, rsv1: int = 0, rsv2: int = 0, rsv3: int = 0,
opcode: int = OPCODE_TEXT, mask: int = 1, data: str or bytes = "") -> None:
"""
Constructor for ABNF. Please check RFC for arguments.
"""
@ -147,7 +147,7 @@ class ABNF:
self.data = data
self.get_mask_key = os.urandom
def validate(self, skip_utf8_validation=False) -> None:
def validate(self, skip_utf8_validation: bool = False) -> None:
"""
Validate the ABNF frame.
@ -187,19 +187,19 @@ class ABNF:
+ " data=" + str(self.data)
@staticmethod
def create_frame(data, opcode, fin=1):
def create_frame(data: str, opcode: int, fin: int = 1) -> 'ABNF':
"""
Create frame to send text, binary and other data.
Parameters
----------
data: <type>
data: str
data to send. This is string value(byte array).
If opcode is OPCODE_TEXT and this value is unicode,
data value is converted into unicode string, automatically.
opcode: <type>
operation code. please see OPCODE_XXX.
fin: <type>
opcode: int
operation code. please see OPCODE_MAP.
fin: int
fin flag. if set to 0, create continue fragmentation.
"""
if opcode == ABNF.OPCODE_TEXT and isinstance(data, str):
@ -237,7 +237,7 @@ class ABNF:
mask_key = self.get_mask_key(4)
return frame_header + self._get_masked(mask_key)
def _get_masked(self, mask_key):
def _get_masked(self, mask_key: str or bytes) -> bytes:
s = ABNF.mask(mask_key, self.data)
if isinstance(mask_key, str):
@ -246,7 +246,7 @@ class ABNF:
return mask_key + s
@staticmethod
def mask(mask_key, data):
def mask(mask_key: str or bytes, data: str or bytes) -> bytes:
"""
Mask or unmask data. Just do xor for each byte
@ -273,7 +273,7 @@ class frame_buffer:
_HEADER_MASK_INDEX = 5
_HEADER_LENGTH_INDEX = 6
def __init__(self, recv_fn, skip_utf8_validation):
def __init__(self, recv_fn: int, skip_utf8_validation: bool) -> None:
self.recv = recv_fn
self.skip_utf8_validation = skip_utf8_validation
# Buffers over the packets from the layer beneath until desired amount
@ -282,7 +282,7 @@ class frame_buffer:
self.clear()
self.lock = Lock()
def clear(self):
def clear(self) -> None:
self.header = None
self.length = None
self.mask = None
@ -290,7 +290,7 @@ class frame_buffer:
def has_received_header(self) -> bool:
return self.header is None
def recv_header(self):
def recv_header(self) -> None:
header = self.recv_strict(2)
b1 = header[0]
fin = b1 >> 7 & 1
@ -304,7 +304,7 @@ class frame_buffer:
self.header = (fin, rsv1, rsv2, rsv3, opcode, has_mask, length_bits)
def has_mask(self):
def has_mask(self) -> bool or int:
if not self.header:
return False
return self.header[frame_buffer._HEADER_MASK_INDEX]
@ -312,7 +312,7 @@ class frame_buffer:
def has_received_length(self) -> bool:
return self.length is None
def recv_length(self):
def recv_length(self) -> None:
bits = self.header[frame_buffer._HEADER_LENGTH_INDEX]
length_bits = bits & 0x7f
if length_bits == 0x7e:
@ -327,10 +327,10 @@ class frame_buffer:
def has_received_mask(self) -> bool:
return self.mask is None
def recv_mask(self):
def recv_mask(self) -> None:
self.mask = self.recv_strict(4) if self.has_mask() else ""
def recv_frame(self):
def recv_frame(self) -> ABNF:
with self.lock:
# Header
@ -386,20 +386,20 @@ class frame_buffer:
class continuous_frame:
def __init__(self, fire_cont_frame, skip_utf8_validation):
def __init__(self, fire_cont_frame: bool, skip_utf8_validation: bool) -> None:
self.fire_cont_frame = fire_cont_frame
self.skip_utf8_validation = skip_utf8_validation
self.cont_data = None
self.recving_frames = None
def validate(self, frame):
def validate(self, frame: ABNF) -> None:
if not self.recving_frames and frame.opcode == ABNF.OPCODE_CONT:
raise WebSocketProtocolException("Illegal frame")
if self.recving_frames and \
frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY):
raise WebSocketProtocolException("Illegal frame")
def add(self, frame):
def add(self, frame: ABNF) -> None:
if self.cont_data:
self.cont_data[1] += frame.data
else:
@ -410,10 +410,10 @@ class continuous_frame:
if frame.fin:
self.recving_frames = None
def is_fire(self, frame):
def is_fire(self, frame: ABNF) -> bool or int:
return frame.fin or self.fire_cont_frame
def extract(self, frame):
def extract(self, frame: ABNF) -> list:
data = self.cont_data
self.cont_data = None
frame.data = data[1]

View file

@ -4,6 +4,9 @@ import sys
import threading
import time
import traceback
import socket
from typing import Callable, Any
from . import _logging
from ._abnf import ABNF
@ -15,7 +18,7 @@ from ._exceptions import *
_app.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -35,7 +38,7 @@ __all__ = ["WebSocketApp"]
RECONNECT = 0
def setReconnect(reconnectInterval):
def setReconnect(reconnectInterval: int) -> None:
global RECONNECT
RECONNECT = reconnectInterval
@ -44,37 +47,40 @@ class DispatcherBase:
"""
DispatcherBase
"""
def __init__(self, app, ping_timeout):
def __init__(self, app: Any, ping_timeout: float) -> None:
self.app = app
self.ping_timeout = ping_timeout
def timeout(self, seconds, callback):
def timeout(self, seconds: int, callback: Callable) -> None:
time.sleep(seconds)
callback()
def reconnect(self, seconds, reconnector):
def reconnect(self, seconds: int, reconnector: Callable) -> None:
try:
_logging.info("reconnect() - retrying in %s seconds [%s frames in stack]" % (seconds, len(inspect.stack())))
_logging.info("reconnect() - retrying in {seconds_count} seconds [{frame_count} frames in stack]".format(
seconds_count=seconds, frame_count=len(inspect.stack())))
time.sleep(seconds)
reconnector(reconnecting=True)
except KeyboardInterrupt as e:
_logging.info("User exited %s" % (e,))
_logging.info("User exited {err}".format(err=e))
raise e
class Dispatcher(DispatcherBase):
"""
Dispatcher
"""
def read(self, sock, read_callback, check_callback):
while self.app.keep_running:
sel = selectors.DefaultSelector()
sel.register(self.app.sock.sock, selectors.EVENT_READ)
r = sel.select(self.ping_timeout)
if r:
if not read_callback():
break
check_callback()
def read(self, sock: socket.socket, read_callback: Callable, check_callback: Callable) -> None:
sel = selectors.DefaultSelector()
sel.register(self.app.sock.sock, selectors.EVENT_READ)
try:
while self.app.keep_running:
r = sel.select(self.ping_timeout)
if r:
if not read_callback():
break
check_callback()
finally:
sel.close()
@ -82,24 +88,26 @@ class SSLDispatcher(DispatcherBase):
"""
SSLDispatcher
"""
def read(self, sock, read_callback, check_callback):
while self.app.keep_running:
r = self.select()
if r:
if not read_callback():
break
check_callback()
def read(self, sock: socket.socket, read_callback: Callable, check_callback: Callable) -> None:
sock = self.app.sock.sock
sel = selectors.DefaultSelector()
sel.register(sock, selectors.EVENT_READ)
try:
while self.app.keep_running:
r = self.select(sock, sel)
if r:
if not read_callback():
break
check_callback()
finally:
sel.close()
def select(self):
def select(self, sock, sel:selectors.DefaultSelector):
sock = self.app.sock.sock
if sock.pending():
return [sock,]
sel = selectors.DefaultSelector()
sel.register(sock, selectors.EVENT_READ)
r = sel.select(self.ping_timeout)
sel.close()
if len(r) > 0:
return r[0][0]
@ -109,20 +117,20 @@ class WrappedDispatcher:
"""
WrappedDispatcher
"""
def __init__(self, app, ping_timeout, dispatcher):
def __init__(self, app, ping_timeout: float, dispatcher: Dispatcher) -> None:
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):
def read(self, sock: socket.socket, read_callback: Callable, check_callback: Callable) -> None:
self.dispatcher.read(sock, read_callback)
self.ping_timeout and self.timeout(self.ping_timeout, check_callback)
def timeout(self, seconds, callback):
def timeout(self, seconds: int, callback: Callable) -> None:
self.dispatcher.timeout(seconds, callback)
def reconnect(self, seconds, reconnector):
def reconnect(self, seconds: int, reconnector: Callable) -> None:
self.timeout(seconds, reconnector)
@ -131,14 +139,14 @@ class WebSocketApp:
Higher level of APIs are provided. The interface is like JavaScript WebSocket object.
"""
def __init__(self, url, header=None,
on_open=None, on_message=None, on_error=None,
on_close=None, on_ping=None, on_pong=None,
on_cont_message=None,
keep_running=True, get_mask_key=None, cookie=None,
subprotocols=None,
on_data=None,
socket=None):
def __init__(self, url: str, header: list or dict or Callable = None,
on_open: Callable = None, on_message: Callable = None, on_error: Callable = None,
on_close: Callable = None, on_ping: Callable = None, on_pong: Callable = None,
on_cont_message: Callable = None,
keep_running: bool = True, get_mask_key: Callable = None, cookie: str = None,
subprotocols: list = None,
on_data: Callable = None,
socket: socket.socket = None) -> None:
"""
WebSocketApp initialization
@ -146,8 +154,11 @@ class WebSocketApp:
----------
url: str
Websocket url.
header: list or dict
header: list or dict or Callable
Custom header for websocket handshake.
If the parameter is a callable object, it is called just before the connection attempt.
The returned dict or list is used as custom header value.
This could be useful in order to properly setup timestamp dependent headers.
on_open: function
Callback object which is called at opening websocket.
on_open has one argument.
@ -222,8 +233,10 @@ class WebSocketApp:
self.subprotocols = subprotocols
self.prepared_socket = socket
self.has_errored = False
self.has_done_teardown = False
self.has_done_teardown_lock = threading.Lock()
def send(self, data, opcode=ABNF.OPCODE_TEXT):
def send(self, data: str, opcode: int = ABNF.OPCODE_TEXT) -> None:
"""
send message
@ -240,7 +253,7 @@ class WebSocketApp:
raise WebSocketConnectionClosedException(
"Connection is already closed.")
def close(self, **kwargs):
def close(self, **kwargs) -> None:
"""
Close websocket connection.
"""
@ -249,41 +262,41 @@ class WebSocketApp:
self.sock.close(**kwargs)
self.sock = None
def _start_ping_thread(self):
def _start_ping_thread(self) -> None:
self.last_ping_tm = self.last_pong_tm = 0
self.stop_ping = threading.Event()
self.ping_thread = threading.Thread(target=self._send_ping)
self.ping_thread.daemon = True
self.ping_thread.start()
def _stop_ping_thread(self):
def _stop_ping_thread(self) -> None:
if self.stop_ping:
self.stop_ping.set()
if self.ping_thread and self.ping_thread.is_alive():
self.ping_thread.join(3)
self.last_ping_tm = self.last_pong_tm = 0
def _send_ping(self):
if self.stop_ping.wait(self.ping_interval):
def _send_ping(self) -> None:
if self.stop_ping.wait(self.ping_interval) or self.keep_running is False:
return
while not self.stop_ping.wait(self.ping_interval):
while not self.stop_ping.wait(self.ping_interval) and self.keep_running is True:
if self.sock:
self.last_ping_tm = time.time()
try:
_logging.debug("Sending ping")
self.sock.ping(self.ping_payload)
except Exception as ex:
_logging.debug("Failed to send ping: %s", ex)
except Exception as e:
_logging.debug("Failed to send ping: {err}".format(err=e))
def run_forever(self, sockopt=None, sslopt=None,
ping_interval=0, ping_timeout=None,
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, reconnect=None):
def run_forever(self, sockopt: tuple = None, sslopt: dict = None,
ping_interval: float = 0, ping_timeout: float or None = None,
ping_payload: str = "",
http_proxy_host: str = None, http_proxy_port: int or str = None,
http_no_proxy: list = None, http_proxy_auth: tuple = None,
http_proxy_timeout: float = None,
skip_utf8_validation: bool = False,
host: str = None, origin: str = None, dispatcher: Dispatcher = None,
suppress_origin: bool = False, proxy_type: str = None, reconnect: int = None) -> bool:
"""
Run event loop for WebSocket framework.
@ -358,7 +371,7 @@ class WebSocketApp:
self.ping_payload = ping_payload
self.keep_running = True
def teardown(close_frame=None):
def teardown(close_frame: ABNF = None):
"""
Tears down the connection.
@ -369,6 +382,13 @@ class WebSocketApp:
with the statusCode and reason from the provided frame.
"""
# teardown() is called in many code paths to ensure resources are cleaned up and on_close is fired.
# To ensure the work is only done once, we use this bool and lock.
with self.has_done_teardown_lock:
if self.has_done_teardown:
return
self.has_done_teardown = True
self._stop_ping_thread()
self.keep_running = False
if self.sock:
@ -380,7 +400,7 @@ class WebSocketApp:
# Finally call the callback AFTER all teardown is complete
self._callback(self.on_close, close_status_code, close_reason)
def setSock(reconnecting=False):
def setSock(reconnecting: bool = False) -> None:
if reconnecting and self.sock:
self.sock.shutdown()
@ -392,8 +412,11 @@ class WebSocketApp:
self.sock.settimeout(getdefaulttimeout())
try:
header = self.header() if callable(self.header) else self.header
self.sock.connect(
self.url, header=self.header, cookie=self.cookie,
self.url, header=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,
@ -412,7 +435,7 @@ class WebSocketApp:
except (WebSocketConnectionClosedException, ConnectionRefusedError, KeyboardInterrupt, SystemExit, Exception) as e:
handleDisconnect(e, reconnecting)
def read():
def read() -> bool:
if not self.keep_running:
return teardown()
@ -445,7 +468,7 @@ class WebSocketApp:
return True
def check():
def check() -> bool:
if (self.ping_timeout):
has_timeout_expired = time.time() - self.last_ping_tm > self.ping_timeout
has_pong_not_arrived_after_last_ping = self.last_pong_tm - self.last_ping_tm < 0
@ -457,7 +480,7 @@ class WebSocketApp:
raise WebSocketTimeoutException("ping/pong timed out")
return True
def handleDisconnect(e, reconnecting=False):
def handleDisconnect(e: Exception, reconnecting: bool = False) -> bool:
self.has_errored = True
self._stop_ping_thread()
if not reconnecting:
@ -469,26 +492,34 @@ class WebSocketApp:
raise
if reconnect:
_logging.info("%s - reconnect" % e)
_logging.info("{err} - reconnect".format(err=e))
if custom_dispatcher:
_logging.debug("Calling custom dispatcher reconnect [%s frames in stack]" % len(inspect.stack()))
_logging.debug("Calling custom dispatcher reconnect [{frame_count} frames in stack]".format(frame_count=len(inspect.stack())))
dispatcher.reconnect(reconnect, setSock)
else:
_logging.error("%s - goodbye" % e)
_logging.error("{err} - goodbye".format(err=e))
teardown()
custom_dispatcher = bool(dispatcher)
dispatcher = self.create_dispatcher(ping_timeout, dispatcher, parse_url(self.url)[3])
setSock()
if not custom_dispatcher and reconnect:
while self.keep_running:
_logging.debug("Calling dispatcher reconnect [%s frames in stack]" % len(inspect.stack()))
dispatcher.reconnect(reconnect, setSock)
try:
setSock()
if not custom_dispatcher and reconnect:
while self.keep_running:
_logging.debug("Calling dispatcher reconnect [{frame_count} frames in stack]".format(frame_count=len(inspect.stack())))
dispatcher.reconnect(reconnect, setSock)
except (KeyboardInterrupt, Exception) as e:
_logging.info("tearing down on exception {err}".format(err=e))
teardown()
finally:
if not custom_dispatcher:
# Ensure teardown was called before returning from run_forever
teardown()
return self.has_errored
def create_dispatcher(self, ping_timeout, dispatcher=None, is_ssl=False):
def create_dispatcher(self, ping_timeout: int, dispatcher: Dispatcher = None, is_ssl: bool = False) -> DispatcherBase:
if dispatcher: # If custom dispatcher is set, use WrappedDispatcher
return WrappedDispatcher(self, ping_timeout, dispatcher)
timeout = ping_timeout or 10
@ -497,7 +528,7 @@ class WebSocketApp:
return Dispatcher(self, timeout)
def _get_close_args(self, close_frame):
def _get_close_args(self, close_frame: ABNF) -> list:
"""
_get_close_args extracts the close code and reason from the close body
if it exists (RFC6455 says WebSocket Connection Close Code is optional)
@ -516,12 +547,12 @@ class WebSocketApp:
# Most likely reached this because len(close_frame_data.data) < 2
return [None, None]
def _callback(self, callback, *args):
def _callback(self, callback, *args) -> None:
if callback:
try:
callback(self, *args)
except Exception as e:
_logging.error("error from callback {}: {}".format(callback, e))
_logging.error("error from callback {callback}: {err}".format(callback=callback, err=e))
if self.on_error:
self.on_error(self, e)

View file

@ -4,7 +4,7 @@ import http.cookies
_cookiejar.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -21,10 +21,10 @@ limitations under the License.
class SimpleCookieJar:
def __init__(self):
def __init__(self) -> None:
self.jar = dict()
def add(self, set_cookie):
def add(self, set_cookie: str) -> None:
if set_cookie:
simpleCookie = http.cookies.SimpleCookie(set_cookie)
@ -37,7 +37,7 @@ class SimpleCookieJar:
cookie.update(simpleCookie)
self.jar[domain.lower()] = cookie
def set(self, set_cookie):
def set(self, set_cookie: str) -> None:
if set_cookie:
simpleCookie = http.cookies.SimpleCookie(set_cookie)
@ -48,7 +48,7 @@ class SimpleCookieJar:
domain = "." + domain
self.jar[domain.lower()] = simpleCookie
def get(self, host):
def get(self, host: str) -> str:
if not host:
return ""

View file

@ -17,7 +17,7 @@ from ._utils import *
_core.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -74,8 +74,8 @@ class WebSocket:
"""
def __init__(self, get_mask_key=None, sockopt=None, sslopt=None,
fire_cont_frame=False, enable_multithread=True,
skip_utf8_validation=False, **_):
fire_cont_frame: bool = False, enable_multithread: bool = True,
skip_utf8_validation: bool = False, **_):
"""
Initialize WebSocket object.
@ -133,7 +133,7 @@ class WebSocket:
"""
self.get_mask_key = func
def gettimeout(self):
def gettimeout(self) -> float:
"""
Get the websocket timeout (in seconds) as an int or float
@ -144,7 +144,7 @@ class WebSocket:
"""
return self.sock_opt.timeout
def settimeout(self, timeout):
def settimeout(self, timeout: float):
"""
Set the timeout to the websocket.
@ -265,7 +265,7 @@ class WebSocket:
self.sock = None
raise
def send(self, payload, opcode=ABNF.OPCODE_TEXT):
def send(self, payload: bytes or str, opcode: int = ABNF.OPCODE_TEXT) -> int:
"""
Send the data as string.
@ -282,7 +282,7 @@ class WebSocket:
frame = ABNF.create_frame(payload, opcode)
return self.send_frame(frame)
def send_frame(self, frame):
def send_frame(self, frame) -> int:
"""
Send the data frame.
@ -313,7 +313,7 @@ class WebSocket:
return length
def send_binary(self, payload):
def send_binary(self, payload: bytes) -> int:
"""
Send a binary message (OPCODE_BINARY).
@ -324,7 +324,7 @@ class WebSocket:
"""
return self.send(payload, ABNF.OPCODE_BINARY)
def ping(self, payload=""):
def ping(self, payload: str or bytes = ""):
"""
Send ping data.
@ -337,7 +337,7 @@ class WebSocket:
payload = payload.encode("utf-8")
self.send(payload, ABNF.OPCODE_PING)
def pong(self, payload=""):
def pong(self, payload: str or bytes = ""):
"""
Send pong data.
@ -350,7 +350,7 @@ class WebSocket:
payload = payload.encode("utf-8")
self.send(payload, ABNF.OPCODE_PONG)
def recv(self):
def recv(self) -> str or bytes:
"""
Receive string data(byte array) from the server.
@ -367,7 +367,7 @@ class WebSocket:
else:
return ''
def recv_data(self, control_frame=False):
def recv_data(self, control_frame: bool = False) -> tuple:
"""
Receive data with operation code.
@ -385,7 +385,7 @@ class WebSocket:
opcode, frame = self.recv_data_frame(control_frame)
return opcode, frame.data
def recv_data_frame(self, control_frame=False):
def recv_data_frame(self, control_frame: bool = False):
"""
Receive data with operation code.
@ -411,7 +411,7 @@ class WebSocket:
# handle error:
# 'NoneType' object has no attribute 'opcode'
raise WebSocketProtocolException(
"Not a valid frame %s" % frame)
"Not a valid frame {frame}".format(frame=frame))
elif frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY, ABNF.OPCODE_CONT):
self.cont_frame.validate(frame)
self.cont_frame.add(frame)
@ -444,7 +444,7 @@ class WebSocket:
"""
return self.frame_buffer.recv_frame()
def send_close(self, status=STATUS_NORMAL, reason=b""):
def send_close(self, status: int = STATUS_NORMAL, reason: bytes = b""):
"""
Send close data to the server.
@ -460,14 +460,14 @@ class WebSocket:
self.connected = False
self.send(struct.pack('!H', status) + reason, ABNF.OPCODE_CLOSE)
def close(self, status=STATUS_NORMAL, reason=b"", timeout=3):
def close(self, status: int = STATUS_NORMAL, reason: bytes = b"", timeout: float = 3):
"""
Close Websocket object
Parameters
----------
status: int
Status code to send. See STATUS_XXX.
Status code to send. See VALID_CLOSE_STATUS in ABNF.
reason: bytes
The reason to close in UTF-8.
timeout: int or float
@ -521,7 +521,7 @@ class WebSocket:
self.sock = None
self.connected = False
def _send(self, data):
def _send(self, data: str or bytes):
return send(self.sock, data)
def _recv(self, bufsize):
@ -535,7 +535,7 @@ class WebSocket:
raise
def create_connection(url, timeout=None, class_=WebSocket, **options):
def create_connection(url: str, timeout=None, class_=WebSocket, **options):
"""
Connect to url and return websocket object.

View file

@ -2,7 +2,7 @@
_exceptions.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -66,11 +66,11 @@ class WebSocketBadStatusException(WebSocketException):
WebSocketBadStatusException will be raised when we get bad handshake status code.
"""
def __init__(self, message, status_code, status_message=None, resp_headers=None):
msg = message % (status_code, status_message)
super().__init__(msg)
def __init__(self, message: str, status_code: int, status_message=None, resp_headers=None, resp_body=None):
super().__init__(message)
self.status_code = status_code
self.resp_headers = resp_headers
self.resp_body = resp_body
class WebSocketAddressException(WebSocketException):

View file

@ -2,7 +2,7 @@
_handshake.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -40,14 +40,14 @@ CookieJar = SimpleCookieJar()
class handshake_response:
def __init__(self, status, headers, subprotocol):
def __init__(self, status: int, headers: dict, subprotocol):
self.status = status
self.headers = headers
self.subprotocol = subprotocol
CookieJar.add(headers.get("set-cookie"))
def handshake(sock, url, hostname, port, resource, **options):
def handshake(sock, url: str, hostname: str, port: int, resource: str, **options):
headers, key = _get_handshake_headers(resource, url, hostname, port, options)
header_str = "\r\n".join(headers)
@ -64,7 +64,7 @@ def handshake(sock, url, hostname, port, resource, **options):
return handshake_response(status, resp, subproto)
def _pack_hostname(hostname):
def _pack_hostname(hostname: str) -> str:
# IPv6 address
if ':' in hostname:
return '[' + hostname + ']'
@ -72,41 +72,41 @@ def _pack_hostname(hostname):
return hostname
def _get_handshake_headers(resource, url, host, port, options):
def _get_handshake_headers(resource: str, url: str, host: str, port: int, options: dict):
headers = [
"GET %s HTTP/1.1" % resource,
"GET {resource} HTTP/1.1".format(resource=resource),
"Upgrade: websocket"
]
if port == 80 or port == 443:
hostport = _pack_hostname(host)
else:
hostport = "%s:%d" % (_pack_hostname(host), port)
hostport = "{h}:{p}".format(h=_pack_hostname(host), p=port)
if options.get("host"):
headers.append("Host: %s" % options["host"])
headers.append("Host: {h}".format(h=options["host"]))
else:
headers.append("Host: %s" % hostport)
headers.append("Host: {hp}".format(hp=hostport))
# 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 not options.get("suppress_origin"):
if "origin" in options and options["origin"] is not None:
headers.append("Origin: %s" % options["origin"])
headers.append("Origin: {origin}".format(origin=options["origin"]))
elif scheme == "wss":
headers.append("Origin: https://%s" % hostport)
headers.append("Origin: https://{hp}".format(hp=hostport))
else:
headers.append("Origin: http://%s" % hostport)
headers.append("Origin: http://{hp}".format(hp=hostport))
key = _create_sec_websocket_key()
# Append Sec-WebSocket-Key & Sec-WebSocket-Version if not manually specified
if not options.get('header') or 'Sec-WebSocket-Key' not in options['header']:
headers.append("Sec-WebSocket-Key: %s" % key)
headers.append("Sec-WebSocket-Key: {key}".format(key=key))
else:
key = options['header']['Sec-WebSocket-Key']
if not options.get('header') or 'Sec-WebSocket-Version' not in options['header']:
headers.append("Sec-WebSocket-Version: %s" % VERSION)
headers.append("Sec-WebSocket-Version: {version}".format(version=VERSION))
if not options.get('connection'):
headers.append('Connection: Upgrade')
@ -115,7 +115,7 @@ def _get_handshake_headers(resource, url, host, port, options):
subprotocols = options.get("subprotocols")
if subprotocols:
headers.append("Sec-WebSocket-Protocol: %s" % ",".join(subprotocols))
headers.append("Sec-WebSocket-Protocol: {protocols}".format(protocols=",".join(subprotocols)))
header = options.get("header")
if header:
@ -133,18 +133,21 @@ def _get_handshake_headers(resource, url, host, port, options):
cookie = "; ".join(filter(None, [server_cookie, client_cookie]))
if cookie:
headers.append("Cookie: %s" % cookie)
headers.append("")
headers.append("")
headers.append("Cookie: {cookie}".format(cookie=cookie))
headers.extend(("", ""))
return headers, key
def _get_resp_headers(sock, success_statuses=SUCCESS_STATUSES):
def _get_resp_headers(sock, success_statuses: tuple = SUCCESS_STATUSES) -> tuple:
status, resp_headers, status_message = read_headers(sock)
if status not in success_statuses:
raise WebSocketBadStatusException("Handshake status %d %s", status, status_message, resp_headers)
content_len = resp_headers.get('content-length')
if content_len:
response_body = sock.recv(int(content_len)) # read the body of the HTTP error message response and include it in the exception
else:
response_body = None
raise WebSocketBadStatusException("Handshake status {status} {message} -+-+- {headers} -+-+- {body}".format(status=status, message=status_message, headers=resp_headers, body=response_body), status, status_message, resp_headers, response_body)
return status, resp_headers
@ -154,7 +157,7 @@ _HEADERS_TO_CHECK = {
}
def _validate(headers, key, subprotocols):
def _validate(headers, key: str, subprotocols):
subproto = None
for k, v in _HEADERS_TO_CHECK.items():
r = headers.get(k, None)
@ -189,6 +192,6 @@ def _validate(headers, key, subprotocols):
return False, None
def _create_sec_websocket_key():
def _create_sec_websocket_key() -> str:
randomness = os.urandom(16)
return base64encode(randomness).decode('utf-8').strip()

View file

@ -2,7 +2,7 @@
_http.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -19,7 +19,6 @@ limitations under the License.
import errno
import os
import socket
import sys
from ._exceptions import *
from ._logging import *
@ -69,7 +68,7 @@ class proxy_info:
self.proxy_protocol = "http"
def _start_proxied_socket(url, options, proxy):
def _start_proxied_socket(url: str, options, proxy):
if not HAVE_PYTHON_SOCKS:
raise WebSocketException("Python Socks is needed for SOCKS proxying but is not available")
@ -107,7 +106,7 @@ def _start_proxied_socket(url, options, proxy):
return sock, (hostname, port, resource)
def connect(url, options, proxy, socket):
def connect(url: str, options, proxy, socket):
# Use _start_proxied_socket() only for socks4 or socks5 proxy
# Use _tunnel() for http proxy
# TODO: Use python-socks for http protocol also, to standardize flow
@ -211,6 +210,11 @@ def _wrap_sni_socket(sock, sslopt, hostname, check_hostname):
context = sslopt.get('context', None)
if not context:
context = ssl.SSLContext(sslopt.get('ssl_version', ssl.PROTOCOL_TLS_CLIENT))
# Non default context need to manually enable SSLKEYLOGFILE support by setting the keylog_filename attribute.
# For more details see also:
# * https://docs.python.org/3.8/library/ssl.html?highlight=sslkeylogfile#context-creation
# * https://docs.python.org/3.8/library/ssl.html?highlight=sslkeylogfile#ssl.SSLContext.keylog_filename
context.keylog_filename = os.environ.get("SSLKEYLOGFILE", None)
if sslopt.get('cert_reqs', ssl.CERT_NONE) != ssl.CERT_NONE:
cafile = sslopt.get('ca_certs', None)
@ -275,8 +279,8 @@ def _ssl_socket(sock, user_sslopt, hostname):
def _tunnel(sock, host, port, auth):
debug("Connecting proxy...")
connect_header = "CONNECT %s:%d HTTP/1.1\r\n" % (host, port)
connect_header += "Host: %s:%d\r\n" % (host, port)
connect_header = "CONNECT {h}:{p} HTTP/1.1\r\n".format(h=host, p=port)
connect_header += "Host: {h}:{p}\r\n".format(h=host, p=port)
# TODO: support digest auth.
if auth and auth[0]:
@ -284,7 +288,7 @@ def _tunnel(sock, host, port, auth):
if auth[1]:
auth_str += ":" + auth[1]
encoded_str = base64encode(auth_str.encode()).strip().decode().replace('\n', '')
connect_header += "Proxy-Authorization: Basic %s\r\n" % encoded_str
connect_header += "Proxy-Authorization: Basic {str}\r\n".format(str=encoded_str)
connect_header += "\r\n"
dump("request header", connect_header)
@ -297,7 +301,7 @@ def _tunnel(sock, host, port, auth):
if status != 200:
raise WebSocketProxyException(
"failed CONNECT via proxy status: %r" % status)
"failed CONNECT via proxy status: {status}".format(status=status))
return sock

View file

@ -4,7 +4,7 @@ import logging
_logging.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -24,7 +24,7 @@ try:
from logging import NullHandler
except ImportError:
class NullHandler(logging.Handler):
def emit(self, record):
def emit(self, record) -> None:
pass
_logger.addHandler(NullHandler())
@ -35,7 +35,9 @@ __all__ = ["enableTrace", "dump", "error", "warning", "debug", "trace",
"isEnabledForError", "isEnabledForDebug", "isEnabledForTrace"]
def enableTrace(traceable, handler=logging.StreamHandler(), level="DEBUG"):
def enableTrace(traceable: bool,
handler: logging.StreamHandler = logging.StreamHandler(),
level: str = "DEBUG") -> None:
"""
Turn on/off the traceability.
@ -51,41 +53,41 @@ def enableTrace(traceable, handler=logging.StreamHandler(), level="DEBUG"):
_logger.setLevel(getattr(logging, level))
def dump(title, message):
def dump(title: str, message: str) -> None:
if _traceEnabled:
_logger.debug("--- " + title + " ---")
_logger.debug(message)
_logger.debug("-----------------------")
def error(msg):
def error(msg: str) -> None:
_logger.error(msg)
def warning(msg):
def warning(msg: str) -> None:
_logger.warning(msg)
def debug(msg):
def debug(msg: str) -> None:
_logger.debug(msg)
def info(msg):
def info(msg: str) -> None:
_logger.info(msg)
def trace(msg):
def trace(msg: str) -> None:
if _traceEnabled:
_logger.debug(msg)
def isEnabledForError():
def isEnabledForError() -> bool:
return _logger.isEnabledFor(logging.ERROR)
def isEnabledForDebug():
def isEnabledForDebug() -> bool:
return _logger.isEnabledFor(logging.DEBUG)
def isEnabledForTrace():
def isEnabledForTrace() -> bool:
return _traceEnabled

View file

@ -10,7 +10,7 @@ from ._utils import *
_socket.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -43,7 +43,7 @@ __all__ = ["DEFAULT_SOCKET_OPTION", "sock_opt", "setdefaulttimeout", "getdefault
class sock_opt:
def __init__(self, sockopt, sslopt):
def __init__(self, sockopt: list, sslopt: dict) -> None:
if sockopt is None:
sockopt = []
if sslopt is None:
@ -53,7 +53,7 @@ class sock_opt:
self.timeout = None
def setdefaulttimeout(timeout):
def setdefaulttimeout(timeout: int or float) -> None:
"""
Set the global timeout setting to connect.
@ -66,7 +66,7 @@ def setdefaulttimeout(timeout):
_default_timeout = timeout
def getdefaulttimeout():
def getdefaulttimeout() -> int or float:
"""
Get default timeout
@ -78,7 +78,7 @@ def getdefaulttimeout():
return _default_timeout
def recv(sock, bufsize):
def recv(sock: socket.socket, bufsize: int) -> bytes:
if not sock:
raise WebSocketConnectionClosedException("socket is already closed.")
@ -125,7 +125,7 @@ def recv(sock, bufsize):
return bytes_
def recv_line(sock):
def recv_line(sock: socket.socket) -> bytes:
line = []
while True:
c = recv(sock, 1)
@ -135,7 +135,7 @@ def recv_line(sock):
return b''.join(line)
def send(sock, data):
def send(sock: socket.socket, data: bytes) -> int:
if isinstance(data, str):
data = data.encode('utf-8')

View file

@ -2,7 +2,7 @@
_ssl_compat.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -8,7 +8,7 @@ from urllib.parse import unquote, urlparse
_url.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -26,7 +26,7 @@ limitations under the License.
__all__ = ["parse_url", "get_proxy_info"]
def parse_url(url):
def parse_url(url: str) -> tuple:
"""
parse url and the result is tuple of
(hostname, port, resource path and the flag of secure mode)
@ -75,7 +75,7 @@ def parse_url(url):
DEFAULT_NO_PROXY_HOST = ["localhost", "127.0.0.1"]
def _is_ip_address(addr):
def _is_ip_address(addr: str) -> bool:
try:
socket.inet_aton(addr)
except socket.error:
@ -84,7 +84,7 @@ def _is_ip_address(addr):
return True
def _is_subnet_address(hostname):
def _is_subnet_address(hostname: str) -> bool:
try:
addr, netmask = hostname.split("/")
return _is_ip_address(addr) and 0 <= int(netmask) < 32
@ -92,7 +92,7 @@ def _is_subnet_address(hostname):
return False
def _is_address_in_network(ip, net):
def _is_address_in_network(ip: str, net: str) -> bool:
ipaddr = struct.unpack('!I', socket.inet_aton(ip))[0]
netaddr, netmask = net.split('/')
netaddr = struct.unpack('!I', socket.inet_aton(netaddr))[0]
@ -101,7 +101,7 @@ def _is_address_in_network(ip, net):
return ipaddr & netmask == netaddr
def _is_no_proxy_host(hostname, no_proxy):
def _is_no_proxy_host(hostname: str, no_proxy: list) -> bool:
if not no_proxy:
v = os.environ.get("no_proxy", os.environ.get("NO_PROXY", "")).replace(" ", "")
if v:
@ -122,8 +122,8 @@ def _is_no_proxy_host(hostname, no_proxy):
def get_proxy_info(
hostname, is_secure, proxy_host=None, proxy_port=0, proxy_auth=None,
no_proxy=None, proxy_type='http'):
hostname: str, is_secure: bool, proxy_host: str = None, proxy_port: int = 0, proxy_auth: tuple = None,
no_proxy: list = None, proxy_type: str = 'http') -> tuple:
"""
Try to retrieve proxy host and port from environment
if not provided in options.
@ -137,14 +137,14 @@ def get_proxy_info(
Websocket server name.
is_secure: bool
Is the connection secure? (wss) looks for "https_proxy" in env
before falling back to "http_proxy"
instead of "http_proxy"
proxy_host: str
http proxy host name.
http_proxy_port: str or int
proxy_port: str or int
http proxy port.
http_no_proxy: list
no_proxy: list
Whitelisted host names that don't use the proxy.
http_proxy_auth: tuple
proxy_auth: tuple
HTTP proxy auth information. Tuple of username and password. Default is None.
proxy_type: str
Specify the proxy protocol (http, socks4, socks4a, socks5, socks5h). Default is "http".
@ -158,15 +158,11 @@ def get_proxy_info(
auth = proxy_auth
return proxy_host, port, auth
env_keys = ["http_proxy"]
if is_secure:
env_keys.insert(0, "https_proxy")
for key in env_keys:
value = os.environ.get(key, os.environ.get(key.upper(), "")).replace(" ", "")
if value:
proxy = urlparse(value)
auth = (unquote(proxy.username), unquote(proxy.password)) if proxy.username else None
return proxy.hostname, proxy.port, auth
env_key = "https_proxy" if is_secure else "http_proxy"
value = os.environ.get(env_key, os.environ.get(env_key.upper(), "")).replace(" ", "")
if value:
proxy = urlparse(value)
auth = (unquote(proxy.username), unquote(proxy.password)) if proxy.username else None
return proxy.hostname, proxy.port, auth
return None, 0, None

View file

@ -2,7 +2,7 @@
_url.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -21,10 +21,10 @@ __all__ = ["NoLock", "validate_utf8", "extract_err_message", "extract_error_code
class NoLock:
def __enter__(self):
def __enter__(self) -> None:
pass
def __exit__(self, exc_type, exc_value, traceback):
def __exit__(self, exc_type, exc_value, traceback) -> None:
pass
@ -33,7 +33,7 @@ try:
# strings.
from wsaccel.utf8validator import Utf8Validator
def _validate_utf8(utfbytes):
def _validate_utf8(utfbytes: bytes) -> bool:
return Utf8Validator().validate(utfbytes)[0]
except ImportError:
@ -63,7 +63,7 @@ except ImportError:
12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
12,36,12,12,12,12,12,12,12,12,12,12, ]
def _decode(state, codep, ch):
def _decode(state: int, codep: int, ch: int) -> tuple:
tp = _UTF8D[ch]
codep = (ch & 0x3f) | (codep << 6) if (
@ -72,7 +72,7 @@ except ImportError:
return state, codep
def _validate_utf8(utfbytes):
def _validate_utf8(utfbytes: str or bytes) -> bool:
state = _UTF8_ACCEPT
codep = 0
for i in utfbytes:
@ -83,7 +83,7 @@ except ImportError:
return True
def validate_utf8(utfbytes):
def validate_utf8(utfbytes: str or bytes) -> bool:
"""
validate utf8 byte string.
utfbytes: utf byte string to check.
@ -92,13 +92,13 @@ def validate_utf8(utfbytes):
return _validate_utf8(utfbytes)
def extract_err_message(exception):
def extract_err_message(exception: Exception) -> str or None:
if exception.args:
return exception.args[0]
else:
return None
def extract_error_code(exception):
def extract_error_code(exception: Exception) -> int or None:
if exception.args and len(exception.args) > 1:
return exception.args[0] if isinstance(exception.args[0], int) else None

View file

@ -4,7 +4,7 @@
wsdump.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -37,7 +37,7 @@ except ImportError:
pass
def get_encoding():
def get_encoding() -> str:
encoding = getattr(sys.stdin, "encoding", "")
if not encoding:
return "utf-8"
@ -51,7 +51,7 @@ ENCODING = get_encoding()
class VAction(argparse.Action):
def __call__(self, parser, args, values, option_string=None):
def __call__(self, parser: argparse.Namespace, args: tuple, values: str, option_string: str = None) -> None:
if values is None:
values = "1"
try:
@ -61,7 +61,7 @@ class VAction(argparse.Action):
setattr(args, self.dest, values)
def parse_args():
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="WebSocket Simple Dump Tool")
parser.add_argument("url", metavar="ws_url",
help="websocket url. ex. ws://echo.websocket.events/")
@ -93,7 +93,7 @@ def parse_args():
class RawInput:
def raw_input(self, prompt):
def raw_input(self, prompt: str = "") -> str:
line = input(prompt)
if ENCODING and ENCODING != "utf-8" and not isinstance(line, str):
@ -106,29 +106,29 @@ class RawInput:
class InteractiveConsole(RawInput, code.InteractiveConsole):
def write(self, data):
def write(self, data: str) -> None:
sys.stdout.write("\033[2K\033[E")
# sys.stdout.write("\n")
sys.stdout.write("\033[34m< " + data + "\033[39m")
sys.stdout.write("\n> ")
sys.stdout.flush()
def read(self):
def read(self) -> str:
return self.raw_input("> ")
class NonInteractive(RawInput):
def write(self, data):
def write(self, data: str) -> None:
sys.stdout.write(data)
sys.stdout.write("\n")
sys.stdout.flush()
def read(self):
def read(self) -> str:
return self.raw_input("")
def main():
def main() -> None:
start_time = time.time()
args = parse_args()
if args.verbose > 1:
@ -154,25 +154,25 @@ def main():
console = InteractiveConsole()
print("Press Ctrl+C to quit")
def recv():
def recv() -> tuple:
try:
frame = ws.recv_frame()
except websocket.WebSocketException:
return websocket.ABNF.OPCODE_CLOSE, None
return websocket.ABNF.OPCODE_CLOSE, ""
if not frame:
raise websocket.WebSocketException("Not a valid frame %s" % frame)
raise websocket.WebSocketException("Not a valid frame {frame}".format(frame=frame))
elif frame.opcode in OPCODE_DATA:
return frame.opcode, frame.data
elif frame.opcode == websocket.ABNF.OPCODE_CLOSE:
ws.send_close()
return frame.opcode, None
return frame.opcode, ""
elif frame.opcode == websocket.ABNF.OPCODE_PING:
ws.pong(frame.data)
return frame.opcode, frame.data
return frame.opcode, frame.data
def recv_ws():
def recv_ws() -> None:
while True:
opcode, data = recv()
msg = None
@ -193,7 +193,7 @@ def main():
data = repr(data)
if args.verbose:
msg = "%s: %s" % (websocket.ABNF.OPCODE_MAP.get(opcode), data)
msg = "{opcode}: {data}".format(opcode=websocket.ABNF.OPCODE_MAP.get(opcode), data=data)
else:
msg = data

View file

@ -6,7 +6,7 @@ import asyncio
import websockets
import os
LOCAL_WS_SERVER_PORT = os.environ.get('LOCAL_WS_SERVER_PORT', '8765')
LOCAL_WS_SERVER_PORT = int(os.environ.get('LOCAL_WS_SERVER_PORT', '8765'))
async def echo(websocket, path):

View file

@ -8,7 +8,7 @@ import unittest
test_abnf.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -11,7 +11,7 @@ import unittest
test_app.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -112,25 +112,6 @@ class WebSocketAppTest(unittest.TestCase):
teardown = app.run_forever()
self.assertEqual(teardown, False)
@unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
def testRunForeverTeardownExceptionalExit(self):
""" The WebSocketApp.run_forever() method should return `True` when the application ends with an exception.
It should also invoke the `on_error` callback before exiting.
"""
def break_it():
# Deliberately break the WebSocketApp by closing the inner socket.
app.sock.close()
def on_error(_, err):
WebSocketAppTest.on_error_data = str(err)
app = ws.WebSocketApp('ws://127.0.0.1:' + LOCAL_WS_SERVER_PORT, on_error=on_error)
threading.Timer(interval=0.2, function=break_it).start()
teardown = app.run_forever(ping_timeout=0.1)
self.assertEqual(teardown, True)
self.assertTrue(len(WebSocketAppTest.on_error_data) > 0)
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testSockMaskKey(self):
""" A WebSocketApp should forward the received mask_key function down
@ -310,8 +291,8 @@ class WebSocketAppTest(unittest.TestCase):
app.run_forever(ping_interval=2, ping_timeout=1, reconnect=3)
self.assertEqual(pong_count, 2)
self.assertIsInstance(exc, ValueError)
self.assertEqual(str(exc), "Invalid file object: None")
self.assertIsInstance(exc, ws.WebSocketTimeoutException)
self.assertEqual(str(exc), "ping/pong timed out")
if __name__ == "__main__":

View file

@ -5,7 +5,7 @@ from websocket._cookiejar import SimpleCookieJar
test_cookiejar.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -13,7 +13,7 @@ import socket
test_http.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -8,7 +8,7 @@ from websocket._url import get_proxy_info, parse_url, _is_address_in_network, _i
test_url.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -254,6 +254,24 @@ class ProxyInfoTest(unittest.TestCase):
os.environ["https_proxy"] = "http://localhost2:3128/"
self.assertEqual(get_proxy_info("echo.websocket.events", True), ("localhost2", 3128, None))
os.environ["http_proxy"] = ""
os.environ["https_proxy"] = "http://localhost2/"
self.assertEqual(get_proxy_info("echo.websocket.events", True), ("localhost2", None, None))
self.assertEqual(get_proxy_info("echo.websocket.events", False), (None, 0, None))
os.environ["http_proxy"] = ""
os.environ["https_proxy"] = "http://localhost2:3128/"
self.assertEqual(get_proxy_info("echo.websocket.events", True), ("localhost2", 3128, None))
self.assertEqual(get_proxy_info("echo.websocket.events", False), (None, 0, None))
os.environ["http_proxy"] = "http://localhost/"
os.environ["https_proxy"] = ""
self.assertEqual(get_proxy_info("echo.websocket.events", True), (None, 0, None))
self.assertEqual(get_proxy_info("echo.websocket.events", False), ("localhost", None, None))
os.environ["http_proxy"] = "http://localhost:3128/"
os.environ["https_proxy"] = ""
self.assertEqual(get_proxy_info("echo.websocket.events", True), (None, 0, None))
self.assertEqual(get_proxy_info("echo.websocket.events", False), ("localhost", 3128, None))
os.environ["http_proxy"] = "http://a:b@localhost/"
self.assertEqual(get_proxy_info("echo.websocket.events", False), ("localhost", None, ("a", "b")))
os.environ["http_proxy"] = "http://a:b@localhost:3128/"

View file

@ -15,7 +15,7 @@ from base64 import decodebytes as base64decode
test_websocket.py
websocket - WebSocket client library for Python
Copyright 2022 engn33r
Copyright 2023 engn33r
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -47,7 +47,7 @@ tzdata==2023.3
tzlocal==4.2
urllib3==2.0.4
webencodings==0.5.1
websocket-client==1.5.1
websocket-client==1.6.2
xmltodict==0.13.0
zipp==3.15.0