mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-06 05:01:14 -07:00
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:
parent
5d0ba8b222
commit
dab46249f2
22 changed files with 225 additions and 184 deletions
|
@ -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"
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
]
|
]
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue