Bump websocket-client from 1.7.0 to 1.8.0 (#2313)

* Bump websocket-client from 1.7.0 to 1.8.0

Bumps [websocket-client](https://github.com/websocket-client/websocket-client) from 1.7.0 to 1.8.0.
- [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.7.0...v1.8.0)

---
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.8.0

---------

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] 2024-05-09 22:28:14 -07:00 committed by GitHub
parent 5d0ba8b222
commit dab46249f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 225 additions and 184 deletions

View file

@ -2,7 +2,7 @@
__init__.py __init__.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -17,10 +17,10 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
""" """
from ._abnf import * from ._abnf import *
from ._app import WebSocketApp, setReconnect from ._app import WebSocketApp as WebSocketApp, setReconnect as setReconnect
from ._core import * from ._core import *
from ._exceptions import * from ._exceptions import *
from ._logging import * from ._logging import *
from ._socket import * from ._socket import *
__version__ = "1.7.0" __version__ = "1.8.0"

View file

@ -5,14 +5,14 @@ import sys
from threading import Lock from threading import Lock
from typing import Callable, Optional, Union from typing import Callable, Optional, Union
from ._exceptions import * from ._exceptions import WebSocketPayloadException, WebSocketProtocolException
from ._utils import validate_utf8 from ._utils import validate_utf8
""" """
_abnf.py _abnf.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View file

@ -13,13 +13,14 @@ from ._exceptions import (
WebSocketException, WebSocketException,
WebSocketTimeoutException, WebSocketTimeoutException,
) )
from ._ssl_compat import SSLEOFError
from ._url import parse_url from ._url import parse_url
""" """
_app.py _app.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -165,6 +166,7 @@ class WebSocketApp:
url: str, url: str,
header: Union[list, dict, Callable, None] = None, header: Union[list, dict, Callable, None] = None,
on_open: Optional[Callable[[WebSocket], None]] = None, on_open: Optional[Callable[[WebSocket], None]] = None,
on_reconnect: Optional[Callable[[WebSocket], None]] = None,
on_message: Optional[Callable[[WebSocket, Any], None]] = None, on_message: Optional[Callable[[WebSocket, Any], None]] = None,
on_error: Optional[Callable[[WebSocket, Any], None]] = None, on_error: Optional[Callable[[WebSocket, Any], None]] = None,
on_close: Optional[Callable[[WebSocket, Any, Any], None]] = None, on_close: Optional[Callable[[WebSocket, Any, Any], None]] = None,
@ -194,6 +196,10 @@ class WebSocketApp:
Callback object which is called at opening websocket. Callback object which is called at opening websocket.
on_open has one argument. on_open has one argument.
The 1st argument is this class object. The 1st argument is this class object.
on_reconnect: function
Callback object which is called at reconnecting websocket.
on_reconnect has one argument.
The 1st argument is this class object.
on_message: function on_message: function
Callback object which is called when received data. Callback object which is called when received data.
on_message has 2 arguments. on_message has 2 arguments.
@ -244,6 +250,7 @@ class WebSocketApp:
self.cookie = cookie self.cookie = cookie
self.on_open = on_open self.on_open = on_open
self.on_reconnect = on_reconnect
self.on_message = on_message self.on_message = on_message
self.on_data = on_data self.on_data = on_data
self.on_error = on_error self.on_error = on_error
@ -424,6 +431,7 @@ class WebSocketApp:
self.ping_interval = ping_interval self.ping_interval = ping_interval
self.ping_timeout = ping_timeout self.ping_timeout = ping_timeout
self.ping_payload = ping_payload self.ping_payload = ping_payload
self.has_done_teardown = False
self.keep_running = True self.keep_running = True
def teardown(close_frame: ABNF = None): def teardown(close_frame: ABNF = None):
@ -495,6 +503,9 @@ class WebSocketApp:
if self.ping_interval: if self.ping_interval:
self._start_ping_thread() self._start_ping_thread()
if reconnecting and self.on_reconnect:
self._callback(self.on_reconnect)
else:
self._callback(self.on_open) self._callback(self.on_open)
dispatcher.read(self.sock.sock, read, check) dispatcher.read(self.sock.sock, read, check)
@ -516,9 +527,10 @@ class WebSocketApp:
except ( except (
WebSocketConnectionClosedException, WebSocketConnectionClosedException,
KeyboardInterrupt, KeyboardInterrupt,
SSLEOFError,
) as e: ) as e:
if custom_dispatcher: if custom_dispatcher:
return handleDisconnect(e) return handleDisconnect(e, bool(reconnect))
else: else:
raise e raise e

View file

@ -5,7 +5,7 @@ from typing import Optional
_cookiejar.py _cookiejar.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -23,13 +23,13 @@ limitations under the License.
class SimpleCookieJar: class SimpleCookieJar:
def __init__(self) -> None: def __init__(self) -> None:
self.jar: dict = dict() self.jar: dict = {}
def add(self, set_cookie: Optional[str]) -> None: def add(self, set_cookie: Optional[str]) -> None:
if set_cookie: if set_cookie:
simpleCookie = http.cookies.SimpleCookie(set_cookie) simple_cookie = http.cookies.SimpleCookie(set_cookie)
for k, v in simpleCookie.items(): for v in simple_cookie.values():
if domain := v.get("domain"): if domain := v.get("domain"):
if not domain.startswith("."): if not domain.startswith("."):
domain = f".{domain}" domain = f".{domain}"
@ -38,25 +38,25 @@ class SimpleCookieJar:
if self.jar.get(domain) if self.jar.get(domain)
else http.cookies.SimpleCookie() else http.cookies.SimpleCookie()
) )
cookie.update(simpleCookie) cookie.update(simple_cookie)
self.jar[domain.lower()] = cookie self.jar[domain.lower()] = cookie
def set(self, set_cookie: str) -> None: def set(self, set_cookie: str) -> None:
if set_cookie: if set_cookie:
simpleCookie = http.cookies.SimpleCookie(set_cookie) simple_cookie = http.cookies.SimpleCookie(set_cookie)
for k, v in simpleCookie.items(): for v in simple_cookie.values():
if domain := v.get("domain"): if domain := v.get("domain"):
if not domain.startswith("."): if not domain.startswith("."):
domain = f".{domain}" domain = f".{domain}"
self.jar[domain.lower()] = simpleCookie self.jar[domain.lower()] = simple_cookie
def get(self, host: str) -> str: def get(self, host: str) -> str:
if not host: if not host:
return "" return ""
cookies = [] cookies = []
for domain, simpleCookie in self.jar.items(): for domain, _ in self.jar.items():
host = host.lower() host = host.lower()
if host.endswith(domain) or host == domain[1:]: if host.endswith(domain) or host == domain[1:]:
cookies.append(self.jar.get(domain)) cookies.append(self.jar.get(domain))
@ -66,7 +66,7 @@ class SimpleCookieJar:
None, None,
sorted( sorted(
[ [
"%s=%s" % (k, v.value) f"{k}={v.value}"
for cookie in filter(None, cookies) for cookie in filter(None, cookies)
for k, v in cookie.items() for k, v in cookie.items()
] ]

View file

@ -5,20 +5,20 @@ import time
from typing import Optional, Union from typing import Optional, Union
# websocket modules # websocket modules
from ._abnf import * from ._abnf import ABNF, STATUS_NORMAL, continuous_frame, frame_buffer
from ._exceptions import * from ._exceptions import WebSocketProtocolException, WebSocketConnectionClosedException
from ._handshake import * from ._handshake import SUPPORTED_REDIRECT_STATUSES, handshake
from ._http import * from ._http import connect, proxy_info
from ._logging import * from ._logging import debug, error, trace, isEnabledForError, isEnabledForTrace
from ._socket import * from ._socket import getdefaulttimeout, recv, send, sock_opt
from ._ssl_compat import * from ._ssl_compat import ssl
from ._utils import * from ._utils import NoLock
""" """
_core.py _core.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -259,7 +259,7 @@ class WebSocket:
try: try:
self.handshake_response = handshake(self.sock, url, *addrs, **options) self.handshake_response = handshake(self.sock, url, *addrs, **options)
for attempt in range(options.pop("redirect_limit", 3)): for _ in range(options.pop("redirect_limit", 3)):
if self.handshake_response.status in SUPPORTED_REDIRECT_STATUSES: if self.handshake_response.status in SUPPORTED_REDIRECT_STATUSES:
url = self.handshake_response.headers["location"] url = self.handshake_response.headers["location"]
self.sock.close() self.sock.close()

View file

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

View file

@ -2,7 +2,7 @@
_handshake.py _handshake.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -23,10 +23,10 @@ from base64 import encodebytes as base64encode
from http import HTTPStatus from http import HTTPStatus
from ._cookiejar import SimpleCookieJar from ._cookiejar import SimpleCookieJar
from ._exceptions import * from ._exceptions import WebSocketException, WebSocketBadStatusException
from ._http import * from ._http import read_headers
from ._logging import * from ._logging import dump, error
from ._socket import * from ._socket import send
__all__ = ["handshake_response", "handshake", "SUPPORTED_REDIRECT_STATUSES"] __all__ = ["handshake_response", "handshake", "SUPPORTED_REDIRECT_STATUSES"]

View file

@ -2,7 +2,7 @@
_http.py _http.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -21,11 +21,15 @@ import os
import socket import socket
from base64 import encodebytes as base64encode from base64 import encodebytes as base64encode
from ._exceptions import * from ._exceptions import (
from ._logging import * WebSocketAddressException,
from ._socket import * WebSocketException,
from ._ssl_compat import * WebSocketProxyException,
from ._url import * )
from ._logging import debug, dump, trace
from ._socket import DEFAULT_SOCKET_OPTION, recv_line, send
from ._ssl_compat import HAVE_SSL, ssl
from ._url import get_proxy_info, parse_url
__all__ = ["proxy_info", "connect", "read_headers"] __all__ = ["proxy_info", "connect", "read_headers"]
@ -283,22 +287,22 @@ def _wrap_sni_socket(sock: socket.socket, sslopt: dict, hostname, check_hostname
def _ssl_socket(sock: socket.socket, user_sslopt: dict, hostname): def _ssl_socket(sock: socket.socket, user_sslopt: dict, hostname):
sslopt: dict = dict(cert_reqs=ssl.CERT_REQUIRED) sslopt: dict = {"cert_reqs": ssl.CERT_REQUIRED}
sslopt.update(user_sslopt) sslopt.update(user_sslopt)
certPath = os.environ.get("WEBSOCKET_CLIENT_CA_BUNDLE") cert_path = os.environ.get("WEBSOCKET_CLIENT_CA_BUNDLE")
if ( if (
certPath cert_path
and os.path.isfile(certPath) and os.path.isfile(cert_path)
and user_sslopt.get("ca_certs", None) is None and user_sslopt.get("ca_certs", None) is None
): ):
sslopt["ca_certs"] = certPath sslopt["ca_certs"] = cert_path
elif ( elif (
certPath cert_path
and os.path.isdir(certPath) and os.path.isdir(cert_path)
and user_sslopt.get("ca_cert_path", None) is None and user_sslopt.get("ca_cert_path", None) is None
): ):
sslopt["ca_cert_path"] = certPath sslopt["ca_cert_path"] = cert_path
if sslopt.get("server_hostname", None): if sslopt.get("server_hostname", None):
hostname = sslopt["server_hostname"] hostname = sslopt["server_hostname"]
@ -327,7 +331,7 @@ def _tunnel(sock: socket.socket, host, port: int, auth) -> socket.socket:
send(sock, connect_header) send(sock, connect_header)
try: try:
status, resp_headers, status_message = read_headers(sock) status, _, _ = read_headers(sock)
except Exception as e: except Exception as e:
raise WebSocketProxyException(str(e)) raise WebSocketProxyException(str(e))

View file

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

View file

@ -3,15 +3,18 @@ import selectors
import socket import socket
from typing import Union from typing import Union
from ._exceptions import * from ._exceptions import (
from ._ssl_compat import * WebSocketConnectionClosedException,
from ._utils import * WebSocketTimeoutException,
)
from ._ssl_compat import SSLError, SSLWantReadError, SSLWantWriteError
from ._utils import extract_error_code, extract_err_message
""" """
_socket.py _socket.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View file

@ -2,7 +2,7 @@
_ssl_compat.py _ssl_compat.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -16,11 +16,18 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
""" """
__all__ = ["HAVE_SSL", "ssl", "SSLError", "SSLWantReadError", "SSLWantWriteError"] __all__ = [
"HAVE_SSL",
"ssl",
"SSLError",
"SSLEOFError",
"SSLWantReadError",
"SSLWantWriteError",
]
try: try:
import ssl import ssl
from ssl import SSLError, SSLWantReadError, SSLWantWriteError from ssl import SSLError, SSLEOFError, SSLWantReadError, SSLWantWriteError
HAVE_SSL = True HAVE_SSL = True
except ImportError: except ImportError:
@ -28,6 +35,9 @@ except ImportError:
class SSLError(Exception): class SSLError(Exception):
pass pass
class SSLEOFError(Exception):
pass
class SSLWantReadError(Exception): class SSLWantReadError(Exception):
pass pass

View file

@ -3,12 +3,13 @@ import socket
import struct import struct
from typing import Optional from typing import Optional
from urllib.parse import unquote, urlparse from urllib.parse import unquote, urlparse
from ._exceptions import WebSocketProxyException
""" """
_url.py _url.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -167,6 +168,8 @@ def get_proxy_info(
return None, 0, None return None, 0, None
if proxy_host: if proxy_host:
if not proxy_port:
raise WebSocketProxyException("Cannot use port 0 when proxy_host specified")
port = proxy_port port = proxy_port
auth = proxy_auth auth = proxy_auth
return proxy_host, port, auth return proxy_host, port, auth

View file

@ -4,7 +4,7 @@ from typing import Union
_url.py _url.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View file

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

View file

@ -10,7 +10,7 @@ import websockets
LOCAL_WS_SERVER_PORT = int(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): async def echo(websocket):
async for message in websocket: async for message in websocket:
await websocket.send(message) await websocket.send(message)

View file

@ -2,14 +2,14 @@
# #
import unittest import unittest
import websocket as ws from websocket._abnf import ABNF, frame_buffer
from websocket._abnf import * from websocket._exceptions import WebSocketProtocolException
""" """
test_abnf.py test_abnf.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -26,7 +26,7 @@ limitations under the License.
class ABNFTest(unittest.TestCase): class ABNFTest(unittest.TestCase):
def testInit(self): def test_init(self):
a = ABNF(0, 0, 0, 0, opcode=ABNF.OPCODE_PING) a = ABNF(0, 0, 0, 0, opcode=ABNF.OPCODE_PING)
self.assertEqual(a.fin, 0) self.assertEqual(a.fin, 0)
self.assertEqual(a.rsv1, 0) self.assertEqual(a.rsv1, 0)
@ -38,28 +38,28 @@ class ABNFTest(unittest.TestCase):
self.assertEqual(a_bad.rsv1, 1) self.assertEqual(a_bad.rsv1, 1)
self.assertEqual(a_bad.opcode, 77) self.assertEqual(a_bad.opcode, 77)
def testValidate(self): def test_validate(self):
a_invalid_ping = ABNF(0, 0, 0, 0, opcode=ABNF.OPCODE_PING) a_invalid_ping = ABNF(0, 0, 0, 0, opcode=ABNF.OPCODE_PING)
self.assertRaises( self.assertRaises(
ws._exceptions.WebSocketProtocolException, WebSocketProtocolException,
a_invalid_ping.validate, a_invalid_ping.validate,
skip_utf8_validation=False, skip_utf8_validation=False,
) )
a_bad_rsv_value = ABNF(0, 1, 0, 0, opcode=ABNF.OPCODE_TEXT) a_bad_rsv_value = ABNF(0, 1, 0, 0, opcode=ABNF.OPCODE_TEXT)
self.assertRaises( self.assertRaises(
ws._exceptions.WebSocketProtocolException, WebSocketProtocolException,
a_bad_rsv_value.validate, a_bad_rsv_value.validate,
skip_utf8_validation=False, skip_utf8_validation=False,
) )
a_bad_opcode = ABNF(0, 0, 0, 0, opcode=77) a_bad_opcode = ABNF(0, 0, 0, 0, opcode=77)
self.assertRaises( self.assertRaises(
ws._exceptions.WebSocketProtocolException, WebSocketProtocolException,
a_bad_opcode.validate, a_bad_opcode.validate,
skip_utf8_validation=False, skip_utf8_validation=False,
) )
a_bad_close_frame = ABNF(0, 0, 0, 0, opcode=ABNF.OPCODE_CLOSE, data=b"\x01") a_bad_close_frame = ABNF(0, 0, 0, 0, opcode=ABNF.OPCODE_CLOSE, data=b"\x01")
self.assertRaises( self.assertRaises(
ws._exceptions.WebSocketProtocolException, WebSocketProtocolException,
a_bad_close_frame.validate, a_bad_close_frame.validate,
skip_utf8_validation=False, skip_utf8_validation=False,
) )
@ -67,7 +67,7 @@ class ABNFTest(unittest.TestCase):
0, 0, 0, 0, opcode=ABNF.OPCODE_CLOSE, data=b"\x01\x8a\xaa\xff\xdd" 0, 0, 0, 0, opcode=ABNF.OPCODE_CLOSE, data=b"\x01\x8a\xaa\xff\xdd"
) )
self.assertRaises( self.assertRaises(
ws._exceptions.WebSocketProtocolException, WebSocketProtocolException,
a_bad_close_frame_2.validate, a_bad_close_frame_2.validate,
skip_utf8_validation=False, skip_utf8_validation=False,
) )
@ -75,12 +75,12 @@ class ABNFTest(unittest.TestCase):
0, 0, 0, 0, opcode=ABNF.OPCODE_CLOSE, data=b"\x03\xe7" 0, 0, 0, 0, opcode=ABNF.OPCODE_CLOSE, data=b"\x03\xe7"
) )
self.assertRaises( self.assertRaises(
ws._exceptions.WebSocketProtocolException, WebSocketProtocolException,
a_bad_close_frame_3.validate, a_bad_close_frame_3.validate,
skip_utf8_validation=True, skip_utf8_validation=True,
) )
def testMask(self): def test_mask(self):
abnf_none_data = ABNF( abnf_none_data = ABNF(
0, 0, 0, 0, opcode=ABNF.OPCODE_PING, mask_value=1, data=None 0, 0, 0, 0, opcode=ABNF.OPCODE_PING, mask_value=1, data=None
) )
@ -91,7 +91,7 @@ class ABNFTest(unittest.TestCase):
) )
self.assertEqual(abnf_str_data._get_masked(bytes_val), b"aaaa\x00") self.assertEqual(abnf_str_data._get_masked(bytes_val), b"aaaa\x00")
def testFormat(self): def test_format(self):
abnf_bad_rsv_bits = ABNF(2, 0, 0, 0, opcode=ABNF.OPCODE_TEXT) abnf_bad_rsv_bits = ABNF(2, 0, 0, 0, opcode=ABNF.OPCODE_TEXT)
self.assertRaises(ValueError, abnf_bad_rsv_bits.format) self.assertRaises(ValueError, abnf_bad_rsv_bits.format)
abnf_bad_opcode = ABNF(0, 0, 0, 0, opcode=5) abnf_bad_opcode = ABNF(0, 0, 0, 0, opcode=5)
@ -110,7 +110,7 @@ class ABNFTest(unittest.TestCase):
) )
self.assertEqual(b"\x01\x03\x01\x8a\xcc", abnf_no_mask.format()) self.assertEqual(b"\x01\x03\x01\x8a\xcc", abnf_no_mask.format())
def testFrameBuffer(self): def test_frame_buffer(self):
fb = frame_buffer(0, True) fb = frame_buffer(0, True)
self.assertEqual(fb.recv, 0) self.assertEqual(fb.recv, 0)
self.assertEqual(fb.skip_utf8_validation, True) self.assertEqual(fb.skip_utf8_validation, True)

View file

@ -12,7 +12,7 @@ import websocket as ws
test_app.py test_app.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -53,10 +53,13 @@ class WebSocketAppTest(unittest.TestCase):
WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet() WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet()
WebSocketAppTest.on_error_data = WebSocketAppTest.NotSetYet() WebSocketAppTest.on_error_data = WebSocketAppTest.NotSetYet()
def close(self):
pass
@unittest.skipUnless( @unittest.skipUnless(
TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled"
) )
def testKeepRunning(self): def test_keep_running(self):
"""A WebSocketApp should keep running as long as its self.keep_running """A WebSocketApp should keep running as long as its self.keep_running
is not False (in the boolean context). is not False (in the boolean context).
""" """
@ -69,7 +72,7 @@ class WebSocketAppTest(unittest.TestCase):
WebSocketAppTest.keep_running_open = self.keep_running WebSocketAppTest.keep_running_open = self.keep_running
self.keep_running = False self.keep_running = False
def on_message(wsapp, message): def on_message(_, message):
print(message) print(message)
self.close() self.close()
@ -87,7 +90,7 @@ class WebSocketAppTest(unittest.TestCase):
# @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)") @unittest.skipUnless(False, "Test disabled for now (requires rel)")
def testRunForeverDispatcher(self): def test_run_forever_dispatcher(self):
"""A WebSocketApp should keep running as long as its self.keep_running """A WebSocketApp should keep running as long as its self.keep_running
is not False (in the boolean context). is not False (in the boolean context).
""" """
@ -98,7 +101,7 @@ class WebSocketAppTest(unittest.TestCase):
self.recv() self.recv()
self.send("goodbye!") self.send("goodbye!")
def on_message(wsapp, message): def on_message(_, message):
print(message) print(message)
self.close() self.close()
@ -115,7 +118,7 @@ class WebSocketAppTest(unittest.TestCase):
@unittest.skipUnless( @unittest.skipUnless(
TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled"
) )
def testRunForeverTeardownCleanExit(self): def test_run_forever_teardown_clean_exit(self):
"""The WebSocketApp.run_forever() method should return `False` when the application ends gracefully.""" """The WebSocketApp.run_forever() method should return `False` when the application ends gracefully."""
app = ws.WebSocketApp(f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}") app = ws.WebSocketApp(f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}")
threading.Timer(interval=0.2, function=app.close).start() threading.Timer(interval=0.2, function=app.close).start()
@ -123,7 +126,7 @@ class WebSocketAppTest(unittest.TestCase):
self.assertEqual(teardown, False) self.assertEqual(teardown, False)
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testSockMaskKey(self): def test_sock_mask_key(self):
"""A WebSocketApp should forward the received mask_key function down """A WebSocketApp should forward the received mask_key function down
to the actual socket. to the actual socket.
""" """
@ -140,14 +143,14 @@ class WebSocketAppTest(unittest.TestCase):
self.assertEqual(id(app.get_mask_key), id(my_mask_key_func)) self.assertEqual(id(app.get_mask_key), id(my_mask_key_func))
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testInvalidPingIntervalPingTimeout(self): def test_invalid_ping_interval_ping_timeout(self):
"""Test exception handling if ping_interval < ping_timeout""" """Test exception handling if ping_interval < ping_timeout"""
def on_ping(app, msg): def on_ping(app, _):
print("Got a ping!") print("Got a ping!")
app.close() app.close()
def on_pong(app, msg): def on_pong(app, _):
print("Got a pong! No need to respond") print("Got a pong! No need to respond")
app.close() app.close()
@ -163,14 +166,14 @@ class WebSocketAppTest(unittest.TestCase):
) )
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testPingInterval(self): def test_ping_interval(self):
"""Test WebSocketApp proper ping functionality""" """Test WebSocketApp proper ping functionality"""
def on_ping(app, msg): def on_ping(app, _):
print("Got a ping!") print("Got a ping!")
app.close() app.close()
def on_pong(app, msg): def on_pong(app, _):
print("Got a pong! No need to respond") print("Got a pong! No need to respond")
app.close() app.close()
@ -182,7 +185,7 @@ class WebSocketAppTest(unittest.TestCase):
) )
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testOpcodeClose(self): def test_opcode_close(self):
"""Test WebSocketApp close opcode""" """Test WebSocketApp close opcode"""
app = ws.WebSocketApp("wss://tsock.us1.twilio.com/v3/wsconnect") app = ws.WebSocketApp("wss://tsock.us1.twilio.com/v3/wsconnect")
@ -197,7 +200,7 @@ class WebSocketAppTest(unittest.TestCase):
# app.run_forever(ping_interval=2, ping_timeout=1, ping_payload="Ping payload") # app.run_forever(ping_interval=2, ping_timeout=1, ping_payload="Ping payload")
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testBadPingInterval(self): def test_bad_ping_interval(self):
"""A WebSocketApp handling of negative ping_interval""" """A WebSocketApp handling of negative ping_interval"""
app = ws.WebSocketApp("wss://api-pub.bitfinex.com/ws/1") app = ws.WebSocketApp("wss://api-pub.bitfinex.com/ws/1")
self.assertRaises( self.assertRaises(
@ -208,7 +211,7 @@ class WebSocketAppTest(unittest.TestCase):
) )
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testBadPingTimeout(self): def test_bad_ping_timeout(self):
"""A WebSocketApp handling of negative ping_timeout""" """A WebSocketApp handling of negative ping_timeout"""
app = ws.WebSocketApp("wss://api-pub.bitfinex.com/ws/1") app = ws.WebSocketApp("wss://api-pub.bitfinex.com/ws/1")
self.assertRaises( self.assertRaises(
@ -219,7 +222,7 @@ class WebSocketAppTest(unittest.TestCase):
) )
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testCloseStatusCode(self): def test_close_status_code(self):
"""Test extraction of close frame status code and close reason in WebSocketApp""" """Test extraction of close frame status code and close reason in WebSocketApp"""
def on_close(wsapp, close_status_code, close_msg): def on_close(wsapp, close_status_code, close_msg):
@ -249,7 +252,7 @@ class WebSocketAppTest(unittest.TestCase):
@unittest.skipUnless( @unittest.skipUnless(
TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled"
) )
def testCallbackFunctionException(self): def test_callback_function_exception(self):
"""Test callback function exception handling""" """Test callback function exception handling"""
exc = None exc = None
@ -264,7 +267,7 @@ class WebSocketAppTest(unittest.TestCase):
nonlocal exc nonlocal exc
exc = err exc = err
def on_pong(app, msg): def on_pong(app, _):
app.close() app.close()
app = ws.WebSocketApp( app = ws.WebSocketApp(
@ -282,7 +285,7 @@ class WebSocketAppTest(unittest.TestCase):
@unittest.skipUnless( @unittest.skipUnless(
TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled"
) )
def testCallbackMethodException(self): def test_callback_method_exception(self):
"""Test callback method exception handling""" """Test callback method exception handling"""
class Callbacks: class Callbacks:
@ -297,14 +300,14 @@ class WebSocketAppTest(unittest.TestCase):
) )
self.app.run_forever(ping_interval=2, ping_timeout=1) self.app.run_forever(ping_interval=2, ping_timeout=1)
def on_open(self, app): def on_open(self, _):
raise RuntimeError("Callback failed") raise RuntimeError("Callback failed")
def on_error(self, app, err): def on_error(self, app, err):
self.passed_app = app self.passed_app = app
self.exc = err self.exc = err
def on_pong(self, app, msg): def on_pong(self, app, _):
app.close() app.close()
callbacks = Callbacks() callbacks = Callbacks()
@ -316,16 +319,16 @@ class WebSocketAppTest(unittest.TestCase):
@unittest.skipUnless( @unittest.skipUnless(
TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled"
) )
def testReconnect(self): def test_reconnect(self):
"""Test reconnect""" """Test reconnect"""
pong_count = 0 pong_count = 0
exc = None exc = None
def on_error(app, err): def on_error(_, err):
nonlocal exc nonlocal exc
exc = err exc = err
def on_pong(app, msg): def on_pong(app, _):
nonlocal pong_count nonlocal pong_count
pong_count += 1 pong_count += 1
if pong_count == 1: if pong_count == 1:

View file

@ -6,7 +6,7 @@ from websocket._cookiejar import SimpleCookieJar
test_cookiejar.py test_cookiejar.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -23,7 +23,7 @@ limitations under the License.
class CookieJarTest(unittest.TestCase): class CookieJarTest(unittest.TestCase):
def testAdd(self): def test_add(self):
cookie_jar = SimpleCookieJar() cookie_jar = SimpleCookieJar()
cookie_jar.add("") cookie_jar.add("")
self.assertFalse( self.assertFalse(
@ -67,7 +67,7 @@ class CookieJarTest(unittest.TestCase):
self.assertEqual(cookie_jar.get("xyz"), "e=f") self.assertEqual(cookie_jar.get("xyz"), "e=f")
self.assertEqual(cookie_jar.get("something"), "") self.assertEqual(cookie_jar.get("something"), "")
def testSet(self): def test_set(self):
cookie_jar = SimpleCookieJar() cookie_jar = SimpleCookieJar()
cookie_jar.set("a=b") cookie_jar.set("a=b")
self.assertFalse( self.assertFalse(
@ -104,7 +104,7 @@ class CookieJarTest(unittest.TestCase):
self.assertEqual(cookie_jar.get("xyz"), "e=f") self.assertEqual(cookie_jar.get("xyz"), "e=f")
self.assertEqual(cookie_jar.get("something"), "") self.assertEqual(cookie_jar.get("something"), "")
def testGet(self): def test_get(self):
cookie_jar = SimpleCookieJar() cookie_jar = SimpleCookieJar()
cookie_jar.set("a=b; c=d; domain=abc.com") cookie_jar.set("a=b; c=d; domain=abc.com")
self.assertEqual(cookie_jar.get("abc.com"), "a=b; c=d") self.assertEqual(cookie_jar.get("abc.com"), "a=b; c=d")

View file

@ -7,7 +7,7 @@ import ssl
import unittest import unittest
import websocket import websocket
import websocket as ws from websocket._exceptions import WebSocketProxyException, WebSocketException
from websocket._http import ( from websocket._http import (
_get_addrinfo_list, _get_addrinfo_list,
_start_proxied_socket, _start_proxied_socket,
@ -15,13 +15,14 @@ from websocket._http import (
connect, connect,
proxy_info, proxy_info,
read_headers, read_headers,
HAVE_PYTHON_SOCKS,
) )
""" """
test_http.py test_http.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -93,20 +94,18 @@ class OptsList:
class HttpTest(unittest.TestCase): class HttpTest(unittest.TestCase):
def testReadHeader(self): def test_read_header(self):
status, header, status_message = read_headers( status, header, _ = read_headers(HeaderSockMock("data/header01.txt"))
HeaderSockMock("data/header01.txt")
)
self.assertEqual(status, 101) self.assertEqual(status, 101)
self.assertEqual(header["connection"], "Upgrade") self.assertEqual(header["connection"], "Upgrade")
# header02.txt is intentionally malformed # header02.txt is intentionally malformed
self.assertRaises( self.assertRaises(
ws.WebSocketException, read_headers, HeaderSockMock("data/header02.txt") WebSocketException, read_headers, HeaderSockMock("data/header02.txt")
) )
def testTunnel(self): def test_tunnel(self):
self.assertRaises( self.assertRaises(
ws.WebSocketProxyException, WebSocketProxyException,
_tunnel, _tunnel,
HeaderSockMock("data/header01.txt"), HeaderSockMock("data/header01.txt"),
"example.com", "example.com",
@ -114,7 +113,7 @@ class HttpTest(unittest.TestCase):
("username", "password"), ("username", "password"),
) )
self.assertRaises( self.assertRaises(
ws.WebSocketProxyException, WebSocketProxyException,
_tunnel, _tunnel,
HeaderSockMock("data/header02.txt"), HeaderSockMock("data/header02.txt"),
"example.com", "example.com",
@ -123,9 +122,9 @@ class HttpTest(unittest.TestCase):
) )
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testConnect(self): def test_connect(self):
# Not currently testing an actual proxy connection, so just check whether proxy errors are raised. This requires internet for a DNS lookup # Not currently testing an actual proxy connection, so just check whether proxy errors are raised. This requires internet for a DNS lookup
if ws._http.HAVE_PYTHON_SOCKS: if HAVE_PYTHON_SOCKS:
# Need this check, otherwise case where python_socks is not installed triggers # 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 # websocket._exceptions.WebSocketException: Python Socks is needed for SOCKS proxying but is not available
self.assertRaises( self.assertRaises(
@ -244,7 +243,7 @@ class HttpTest(unittest.TestCase):
@unittest.skipUnless( @unittest.skipUnless(
TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled"
) )
def testProxyConnect(self): def test_proxy_connect(self):
ws = websocket.WebSocket() ws = websocket.WebSocket()
ws.connect( ws.connect(
f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}", f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}",
@ -289,7 +288,7 @@ class HttpTest(unittest.TestCase):
# TODO: Test SOCKS4 and SOCK5 proxies with unit tests # TODO: Test SOCKS4 and SOCK5 proxies with unit tests
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testSSLopt(self): def test_sslopt(self):
ssloptions = { ssloptions = {
"check_hostname": False, "check_hostname": False,
"server_hostname": "ServerName", "server_hostname": "ServerName",
@ -315,7 +314,7 @@ class HttpTest(unittest.TestCase):
ws_ssl2.connect("wss://api.bitfinex.com/ws/2") ws_ssl2.connect("wss://api.bitfinex.com/ws/2")
ws_ssl2.close ws_ssl2.close
def testProxyInfo(self): def test_proxy_info(self):
self.assertEqual( self.assertEqual(
proxy_info( proxy_info(
http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http" http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http"

View file

@ -9,12 +9,13 @@ from websocket._url import (
get_proxy_info, get_proxy_info,
parse_url, parse_url,
) )
from websocket._exceptions import WebSocketProxyException
""" """
test_url.py test_url.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -36,7 +37,7 @@ class UrlTest(unittest.TestCase):
self.assertTrue(_is_address_in_network("127.1.0.1", "127.0.0.0/8")) self.assertTrue(_is_address_in_network("127.1.0.1", "127.0.0.0/8"))
self.assertFalse(_is_address_in_network("127.1.0.1", "127.0.0.0/24")) self.assertFalse(_is_address_in_network("127.1.0.1", "127.0.0.0/24"))
def testParseUrl(self): def test_parse_url(self):
p = parse_url("ws://www.example.com/r") p = parse_url("ws://www.example.com/r")
self.assertEqual(p[0], "www.example.com") self.assertEqual(p[0], "www.example.com")
self.assertEqual(p[1], 80) self.assertEqual(p[1], 80)
@ -130,9 +131,13 @@ class IsNoProxyHostTest(unittest.TestCase):
elif "no_proxy" in os.environ: elif "no_proxy" in os.environ:
del os.environ["no_proxy"] del os.environ["no_proxy"]
def testMatchAll(self): def test_match_all(self):
self.assertTrue(_is_no_proxy_host("any.websocket.org", ["*"])) self.assertTrue(_is_no_proxy_host("any.websocket.org", ["*"]))
self.assertTrue(_is_no_proxy_host("192.168.0.1", ["*"])) self.assertTrue(_is_no_proxy_host("192.168.0.1", ["*"]))
self.assertFalse(_is_no_proxy_host("192.168.0.1", ["192.168.1.1"]))
self.assertFalse(
_is_no_proxy_host("any.websocket.org", ["other.websocket.org"])
)
self.assertTrue( self.assertTrue(
_is_no_proxy_host("any.websocket.org", ["other.websocket.org", "*"]) _is_no_proxy_host("any.websocket.org", ["other.websocket.org", "*"])
) )
@ -142,7 +147,7 @@ class IsNoProxyHostTest(unittest.TestCase):
os.environ["no_proxy"] = "other.websocket.org, *" os.environ["no_proxy"] = "other.websocket.org, *"
self.assertTrue(_is_no_proxy_host("any.websocket.org", None)) self.assertTrue(_is_no_proxy_host("any.websocket.org", None))
def testIpAddress(self): def test_ip_address(self):
self.assertTrue(_is_no_proxy_host("127.0.0.1", ["127.0.0.1"])) self.assertTrue(_is_no_proxy_host("127.0.0.1", ["127.0.0.1"]))
self.assertFalse(_is_no_proxy_host("127.0.0.2", ["127.0.0.1"])) self.assertFalse(_is_no_proxy_host("127.0.0.2", ["127.0.0.1"]))
self.assertTrue( self.assertTrue(
@ -158,7 +163,7 @@ class IsNoProxyHostTest(unittest.TestCase):
self.assertTrue(_is_no_proxy_host("127.0.0.1", None)) self.assertTrue(_is_no_proxy_host("127.0.0.1", None))
self.assertFalse(_is_no_proxy_host("127.0.0.2", None)) self.assertFalse(_is_no_proxy_host("127.0.0.2", None))
def testIpAddressInRange(self): def test_ip_address_in_range(self):
self.assertTrue(_is_no_proxy_host("127.0.0.1", ["127.0.0.0/8"])) self.assertTrue(_is_no_proxy_host("127.0.0.1", ["127.0.0.0/8"]))
self.assertTrue(_is_no_proxy_host("127.0.0.2", ["127.0.0.0/8"])) self.assertTrue(_is_no_proxy_host("127.0.0.2", ["127.0.0.0/8"]))
self.assertFalse(_is_no_proxy_host("127.1.0.1", ["127.0.0.0/24"])) self.assertFalse(_is_no_proxy_host("127.1.0.1", ["127.0.0.0/24"]))
@ -168,7 +173,7 @@ class IsNoProxyHostTest(unittest.TestCase):
os.environ["no_proxy"] = "127.0.0.0/24" os.environ["no_proxy"] = "127.0.0.0/24"
self.assertFalse(_is_no_proxy_host("127.1.0.1", None)) self.assertFalse(_is_no_proxy_host("127.1.0.1", None))
def testHostnameMatch(self): def test_hostname_match(self):
self.assertTrue(_is_no_proxy_host("my.websocket.org", ["my.websocket.org"])) self.assertTrue(_is_no_proxy_host("my.websocket.org", ["my.websocket.org"]))
self.assertTrue( self.assertTrue(
_is_no_proxy_host( _is_no_proxy_host(
@ -182,7 +187,7 @@ class IsNoProxyHostTest(unittest.TestCase):
os.environ["no_proxy"] = "other.websocket.org, my.websocket.org" os.environ["no_proxy"] = "other.websocket.org, my.websocket.org"
self.assertTrue(_is_no_proxy_host("my.websocket.org", None)) self.assertTrue(_is_no_proxy_host("my.websocket.org", None))
def testHostnameMatchDomain(self): def test_hostname_match_domain(self):
self.assertTrue(_is_no_proxy_host("any.websocket.org", [".websocket.org"])) self.assertTrue(_is_no_proxy_host("any.websocket.org", [".websocket.org"]))
self.assertTrue(_is_no_proxy_host("my.other.websocket.org", [".websocket.org"])) self.assertTrue(_is_no_proxy_host("my.other.websocket.org", [".websocket.org"]))
self.assertTrue( self.assertTrue(
@ -227,10 +232,13 @@ class ProxyInfoTest(unittest.TestCase):
elif "no_proxy" in os.environ: elif "no_proxy" in os.environ:
del os.environ["no_proxy"] del os.environ["no_proxy"]
def testProxyFromArgs(self): def test_proxy_from_args(self):
self.assertEqual( self.assertRaises(
get_proxy_info("echo.websocket.events", False, proxy_host="localhost"), WebSocketProxyException,
("localhost", 0, None), get_proxy_info,
"echo.websocket.events",
False,
proxy_host="localhost",
) )
self.assertEqual( self.assertEqual(
get_proxy_info( get_proxy_info(
@ -238,10 +246,6 @@ class ProxyInfoTest(unittest.TestCase):
), ),
("localhost", 3128, None), ("localhost", 3128, None),
) )
self.assertEqual(
get_proxy_info("echo.websocket.events", True, proxy_host="localhost"),
("localhost", 0, None),
)
self.assertEqual( self.assertEqual(
get_proxy_info( get_proxy_info(
"echo.websocket.events", True, proxy_host="localhost", proxy_port=3128 "echo.websocket.events", True, proxy_host="localhost", proxy_port=3128
@ -254,9 +258,10 @@ class ProxyInfoTest(unittest.TestCase):
"echo.websocket.events", "echo.websocket.events",
False, False,
proxy_host="localhost", proxy_host="localhost",
proxy_port=9001,
proxy_auth=("a", "b"), proxy_auth=("a", "b"),
), ),
("localhost", 0, ("a", "b")), ("localhost", 9001, ("a", "b")),
) )
self.assertEqual( self.assertEqual(
get_proxy_info( get_proxy_info(
@ -273,9 +278,10 @@ class ProxyInfoTest(unittest.TestCase):
"echo.websocket.events", "echo.websocket.events",
True, True,
proxy_host="localhost", proxy_host="localhost",
proxy_port=8765,
proxy_auth=("a", "b"), proxy_auth=("a", "b"),
), ),
("localhost", 0, ("a", "b")), ("localhost", 8765, ("a", "b")),
) )
self.assertEqual( self.assertEqual(
get_proxy_info( get_proxy_info(
@ -311,7 +317,18 @@ class ProxyInfoTest(unittest.TestCase):
(None, 0, None), (None, 0, None),
) )
def testProxyFromEnv(self): self.assertEqual(
get_proxy_info(
"echo.websocket.events",
True,
proxy_host="localhost",
proxy_port=3128,
no_proxy=[".websocket.events"],
),
(None, 0, None),
)
def test_proxy_from_env(self):
os.environ["http_proxy"] = "http://localhost/" os.environ["http_proxy"] = "http://localhost/"
self.assertEqual( self.assertEqual(
get_proxy_info("echo.websocket.events", False), ("localhost", None, None) get_proxy_info("echo.websocket.events", False), ("localhost", None, None)

View file

@ -7,6 +7,7 @@ import unittest
from base64 import decodebytes as base64decode from base64 import decodebytes as base64decode
import websocket as ws import websocket as ws
from websocket._exceptions import WebSocketBadStatusException, WebSocketAddressException
from websocket._handshake import _create_sec_websocket_key from websocket._handshake import _create_sec_websocket_key
from websocket._handshake import _validate as _validate_header from websocket._handshake import _validate as _validate_header
from websocket._http import read_headers from websocket._http import read_headers
@ -16,7 +17,7 @@ from websocket._utils import validate_utf8
test_websocket.py test_websocket.py
websocket - WebSocket client library for Python websocket - WebSocket client library for Python
Copyright 2023 engn33r Copyright 2024 engn33r
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -33,7 +34,6 @@ limitations under the License.
try: try:
import ssl import ssl
from ssl import SSLError
except ImportError: except ImportError:
# dummy class of SSLError for ssl none-support environment. # dummy class of SSLError for ssl none-support environment.
class SSLError(Exception): class SSLError(Exception):
@ -95,24 +95,24 @@ class WebSocketTest(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
def testDefaultTimeout(self): def test_default_timeout(self):
self.assertEqual(ws.getdefaulttimeout(), None) self.assertEqual(ws.getdefaulttimeout(), None)
ws.setdefaulttimeout(10) ws.setdefaulttimeout(10)
self.assertEqual(ws.getdefaulttimeout(), 10) self.assertEqual(ws.getdefaulttimeout(), 10)
ws.setdefaulttimeout(None) ws.setdefaulttimeout(None)
def testWSKey(self): def test_ws_key(self):
key = _create_sec_websocket_key() key = _create_sec_websocket_key()
self.assertTrue(key != 24) self.assertTrue(key != 24)
self.assertTrue("¥n" not in key) self.assertTrue("¥n" not in key)
def testNonce(self): def test_nonce(self):
"""WebSocket key should be a random 16-byte nonce.""" """WebSocket key should be a random 16-byte nonce."""
key = _create_sec_websocket_key() key = _create_sec_websocket_key()
nonce = base64decode(key.encode("utf-8")) nonce = base64decode(key.encode("utf-8"))
self.assertEqual(16, len(nonce)) self.assertEqual(16, len(nonce))
def testWsUtils(self): def test_ws_utils(self):
key = "c6b8hTg4EeGb2gQMztV1/g==" key = "c6b8hTg4EeGb2gQMztV1/g=="
required_header = { required_header = {
"upgrade": "websocket", "upgrade": "websocket",
@ -157,16 +157,12 @@ class WebSocketTest(unittest.TestCase):
# This case will print out a logging error using the error() function, but that is expected # This case will print out a logging error using the error() function, but that is expected
self.assertEqual(_validate_header(header, key, ["Sub1", "suB2"]), (False, None)) self.assertEqual(_validate_header(header, key, ["Sub1", "suB2"]), (False, None))
def testReadHeader(self): def test_read_header(self):
status, header, status_message = read_headers( status, header, _ = read_headers(HeaderSockMock("data/header01.txt"))
HeaderSockMock("data/header01.txt")
)
self.assertEqual(status, 101) self.assertEqual(status, 101)
self.assertEqual(header["connection"], "Upgrade") self.assertEqual(header["connection"], "Upgrade")
status, header, status_message = read_headers( status, header, _ = read_headers(HeaderSockMock("data/header03.txt"))
HeaderSockMock("data/header03.txt")
)
self.assertEqual(status, 101) self.assertEqual(status, 101)
self.assertEqual(header["connection"], "Upgrade, Keep-Alive") self.assertEqual(header["connection"], "Upgrade, Keep-Alive")
@ -175,7 +171,7 @@ class WebSocketTest(unittest.TestCase):
ws.WebSocketException, read_headers, HeaderSockMock("data/header02.txt") ws.WebSocketException, read_headers, HeaderSockMock("data/header02.txt")
) )
def testSend(self): def test_send(self):
# TODO: add longer frame data # TODO: add longer frame data
sock = ws.WebSocket() sock = ws.WebSocket()
sock.set_mask_key(create_mask_key) sock.set_mask_key(create_mask_key)
@ -194,7 +190,7 @@ class WebSocketTest(unittest.TestCase):
self.assertEqual(sock.send_binary(b"1111111111101"), 19) self.assertEqual(sock.send_binary(b"1111111111101"), 19)
def testRecv(self): def test_recv(self):
# TODO: add longer frame data # TODO: add longer frame data
sock = ws.WebSocket() sock = ws.WebSocket()
s = sock.sock = SockMock() s = sock.sock = SockMock()
@ -210,7 +206,7 @@ class WebSocketTest(unittest.TestCase):
self.assertEqual(data, "Hello") self.assertEqual(data, "Hello")
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testIter(self): def test_iter(self):
count = 2 count = 2
s = ws.create_connection("wss://api.bitfinex.com/ws/2") s = ws.create_connection("wss://api.bitfinex.com/ws/2")
s.send('{"event": "subscribe", "channel": "ticker"}') s.send('{"event": "subscribe", "channel": "ticker"}')
@ -220,11 +216,11 @@ class WebSocketTest(unittest.TestCase):
break break
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testNext(self): def test_next(self):
sock = ws.create_connection("wss://api.bitfinex.com/ws/2") sock = ws.create_connection("wss://api.bitfinex.com/ws/2")
self.assertEqual(str, type(next(sock))) self.assertEqual(str, type(next(sock)))
def testInternalRecvStrict(self): def test_internal_recv_strict(self):
sock = ws.WebSocket() sock = ws.WebSocket()
s = sock.sock = SockMock() s = sock.sock = SockMock()
s.add_packet(b"foo") s.add_packet(b"foo")
@ -241,7 +237,7 @@ class WebSocketTest(unittest.TestCase):
with self.assertRaises(ws.WebSocketConnectionClosedException): with self.assertRaises(ws.WebSocketConnectionClosedException):
sock.frame_buffer.recv_strict(1) sock.frame_buffer.recv_strict(1)
def testRecvTimeout(self): def test_recv_timeout(self):
sock = ws.WebSocket() sock = ws.WebSocket()
s = sock.sock = SockMock() s = sock.sock = SockMock()
s.add_packet(b"\x81") s.add_packet(b"\x81")
@ -258,7 +254,7 @@ class WebSocketTest(unittest.TestCase):
with self.assertRaises(ws.WebSocketConnectionClosedException): with self.assertRaises(ws.WebSocketConnectionClosedException):
sock.recv() sock.recv()
def testRecvWithSimpleFragmentation(self): def test_recv_with_simple_fragmentation(self):
sock = ws.WebSocket() sock = ws.WebSocket()
s = sock.sock = SockMock() s = sock.sock = SockMock()
# OPCODE=TEXT, FIN=0, MSG="Brevity is " # OPCODE=TEXT, FIN=0, MSG="Brevity is "
@ -270,7 +266,7 @@ class WebSocketTest(unittest.TestCase):
with self.assertRaises(ws.WebSocketConnectionClosedException): with self.assertRaises(ws.WebSocketConnectionClosedException):
sock.recv() sock.recv()
def testRecvWithFireEventOfFragmentation(self): def test_recv_with_fire_event_of_fragmentation(self):
sock = ws.WebSocket(fire_cont_frame=True) sock = ws.WebSocket(fire_cont_frame=True)
s = sock.sock = SockMock() s = sock.sock = SockMock()
# OPCODE=TEXT, FIN=0, MSG="Brevity is " # OPCODE=TEXT, FIN=0, MSG="Brevity is "
@ -296,7 +292,7 @@ class WebSocketTest(unittest.TestCase):
with self.assertRaises(ws.WebSocketConnectionClosedException): with self.assertRaises(ws.WebSocketConnectionClosedException):
sock.recv() sock.recv()
def testClose(self): def test_close(self):
sock = ws.WebSocket() sock = ws.WebSocket()
sock.connected = True sock.connected = True
sock.close sock.close
@ -308,14 +304,14 @@ class WebSocketTest(unittest.TestCase):
sock.recv() sock.recv()
self.assertEqual(sock.connected, False) self.assertEqual(sock.connected, False)
def testRecvContFragmentation(self): def test_recv_cont_fragmentation(self):
sock = ws.WebSocket() sock = ws.WebSocket()
s = sock.sock = SockMock() s = sock.sock = SockMock()
# OPCODE=CONT, FIN=1, MSG="the soul of wit" # OPCODE=CONT, FIN=1, MSG="the soul of wit"
s.add_packet(b"\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17") s.add_packet(b"\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17")
self.assertRaises(ws.WebSocketException, sock.recv) self.assertRaises(ws.WebSocketException, sock.recv)
def testRecvWithProlongedFragmentation(self): def test_recv_with_prolonged_fragmentation(self):
sock = ws.WebSocket() sock = ws.WebSocket()
s = sock.sock = SockMock() s = sock.sock = SockMock()
# OPCODE=TEXT, FIN=0, MSG="Once more unto the breach, " # OPCODE=TEXT, FIN=0, MSG="Once more unto the breach, "
@ -331,7 +327,7 @@ class WebSocketTest(unittest.TestCase):
with self.assertRaises(ws.WebSocketConnectionClosedException): with self.assertRaises(ws.WebSocketConnectionClosedException):
sock.recv() sock.recv()
def testRecvWithFragmentationAndControlFrame(self): def test_recv_with_fragmentation_and_control_frame(self):
sock = ws.WebSocket() sock = ws.WebSocket()
sock.set_mask_key(create_mask_key) sock.set_mask_key(create_mask_key)
s = sock.sock = SockMock() s = sock.sock = SockMock()
@ -352,7 +348,7 @@ class WebSocketTest(unittest.TestCase):
@unittest.skipUnless( @unittest.skipUnless(
TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled"
) )
def testWebSocket(self): def test_websocket(self):
s = ws.create_connection(f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}") s = ws.create_connection(f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}")
self.assertNotEqual(s, None) self.assertNotEqual(s, None)
s.send("Hello, World") s.send("Hello, World")
@ -369,7 +365,7 @@ class WebSocketTest(unittest.TestCase):
@unittest.skipUnless( @unittest.skipUnless(
TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled"
) )
def testPingPong(self): def test_ping_pong(self):
s = ws.create_connection(f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}") s = ws.create_connection(f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}")
self.assertNotEqual(s, None) self.assertNotEqual(s, None)
s.ping("Hello") s.ping("Hello")
@ -377,17 +373,13 @@ class WebSocketTest(unittest.TestCase):
s.close() s.close()
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testSupportRedirect(self): def test_support_redirect(self):
s = ws.WebSocket() s = ws.WebSocket()
self.assertRaises( self.assertRaises(WebSocketBadStatusException, s.connect, "ws://google.com/")
ws._exceptions.WebSocketBadStatusException, s.connect, "ws://google.com/"
)
# Need to find a URL that has a redirect code leading to a websocket # Need to find a URL that has a redirect code leading to a websocket
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testSecureWebSocket(self): def test_secure_websocket(self):
import ssl
s = ws.create_connection("wss://api.bitfinex.com/ws/2") s = ws.create_connection("wss://api.bitfinex.com/ws/2")
self.assertNotEqual(s, None) self.assertNotEqual(s, None)
self.assertTrue(isinstance(s.sock, ssl.SSLSocket)) self.assertTrue(isinstance(s.sock, ssl.SSLSocket))
@ -401,7 +393,7 @@ class WebSocketTest(unittest.TestCase):
@unittest.skipUnless( @unittest.skipUnless(
TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled"
) )
def testWebSocketWithCustomHeader(self): def test_websocket_with_custom_header(self):
s = ws.create_connection( s = ws.create_connection(
f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}", f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}",
headers={"User-Agent": "PythonWebsocketClient"}, headers={"User-Agent": "PythonWebsocketClient"},
@ -417,7 +409,7 @@ class WebSocketTest(unittest.TestCase):
@unittest.skipUnless( @unittest.skipUnless(
TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled"
) )
def testAfterClose(self): def test_after_close(self):
s = ws.create_connection(f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}") s = ws.create_connection(f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}")
self.assertNotEqual(s, None) self.assertNotEqual(s, None)
s.close() s.close()
@ -429,7 +421,7 @@ class SockOptTest(unittest.TestCase):
@unittest.skipUnless( @unittest.skipUnless(
TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled"
) )
def testSockOpt(self): def test_sockopt(self):
sockopt = ((socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),) sockopt = ((socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),)
s = ws.create_connection( s = ws.create_connection(
f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}", sockopt=sockopt f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}", sockopt=sockopt
@ -441,7 +433,7 @@ class SockOptTest(unittest.TestCase):
class UtilsTest(unittest.TestCase): class UtilsTest(unittest.TestCase):
def testUtf8Validator(self): def test_utf8_validator(self):
state = validate_utf8(b"\xf0\x90\x80\x80") state = validate_utf8(b"\xf0\x90\x80\x80")
self.assertEqual(state, True) self.assertEqual(state, True)
state = validate_utf8( state = validate_utf8(
@ -454,7 +446,7 @@ class UtilsTest(unittest.TestCase):
class HandshakeTest(unittest.TestCase): class HandshakeTest(unittest.TestCase):
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def test_http_SSL(self): def test_http_ssl(self):
websock1 = ws.WebSocket( websock1 = ws.WebSocket(
sslopt={"cert_chain": ssl.get_default_verify_paths().capath}, sslopt={"cert_chain": ssl.get_default_verify_paths().capath},
enable_multithread=False, enable_multithread=False,
@ -466,7 +458,7 @@ class HandshakeTest(unittest.TestCase):
) )
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testManualHeaders(self): def test_manual_headers(self):
websock3 = ws.WebSocket( websock3 = ws.WebSocket(
sslopt={ sslopt={
"ca_certs": ssl.get_default_verify_paths().cafile, "ca_certs": ssl.get_default_verify_paths().cafile,
@ -474,7 +466,7 @@ class HandshakeTest(unittest.TestCase):
} }
) )
self.assertRaises( self.assertRaises(
ws._exceptions.WebSocketBadStatusException, WebSocketBadStatusException,
websock3.connect, websock3.connect,
"wss://api.bitfinex.com/ws/2", "wss://api.bitfinex.com/ws/2",
cookie="chocolate", cookie="chocolate",
@ -490,16 +482,14 @@ class HandshakeTest(unittest.TestCase):
}, },
) )
def testIPv6(self): def test_ipv6(self):
websock2 = ws.WebSocket() websock2 = ws.WebSocket()
self.assertRaises(ValueError, websock2.connect, "2001:4860:4860::8888") self.assertRaises(ValueError, websock2.connect, "2001:4860:4860::8888")
def testBadURLs(self): def test_bad_urls(self):
websock3 = ws.WebSocket() websock3 = ws.WebSocket()
self.assertRaises(ValueError, websock3.connect, "ws//example.com") self.assertRaises(ValueError, websock3.connect, "ws//example.com")
self.assertRaises( self.assertRaises(WebSocketAddressException, websock3.connect, "ws://example")
ws.WebSocketAddressException, websock3.connect, "ws://example"
)
self.assertRaises(ValueError, websock3.connect, "example.com") self.assertRaises(ValueError, websock3.connect, "example.com")

View file

@ -45,7 +45,7 @@ tzdata==2024.1
tzlocal==5.0.1 tzlocal==5.0.1
urllib3<2 urllib3<2
webencodings==0.5.1 webencodings==0.5.1
websocket-client==1.7.0 websocket-client==1.8.0
xmltodict==0.13.0 xmltodict==0.13.0
zipp==3.18.1 zipp==3.18.1