summaryrefslogtreecommitdiffhomepage
path: root/libs/websocket
diff options
context:
space:
mode:
authormorpheus65535 <[email protected]>2022-01-23 23:07:52 -0500
committermorpheus65535 <[email protected]>2022-01-23 23:07:52 -0500
commit0c3c5a02a75bc61b6bf6e303de20e11741d2afac (patch)
tree30ae1d524ffe5d54172b7a4a8445d90c3461e659 /libs/websocket
parent36bf0d219d0432c20e6314e0ce752b36f4d88e3c (diff)
downloadbazarr-0c3c5a02a75bc61b6bf6e303de20e11741d2afac.tar.gz
bazarr-0c3c5a02a75bc61b6bf6e303de20e11741d2afac.zip
Upgraded vendored Python dependencies to the latest versions and removed the unused dependencies.v1.0.3-beta.16
Diffstat (limited to 'libs/websocket')
-rw-r--r--libs/websocket/__init__.py26
-rw-r--r--libs/websocket/_abnf.py77
-rw-r--r--libs/websocket/_app.py138
-rw-r--r--libs/websocket/_cookiejar.py26
-rw-r--r--libs/websocket/_core.py235
-rw-r--r--libs/websocket/_exceptions.py26
-rw-r--r--libs/websocket/_handshake.py26
-rw-r--r--libs/websocket/_http.py192
-rw-r--r--libs/websocket/_logging.py24
-rw-r--r--libs/websocket/_socket.py28
-rw-r--r--libs/websocket/_ssl_compat.py29
-rw-r--r--libs/websocket/_url.py62
-rw-r--r--libs/websocket/_utils.py26
-rwxr-xr-xlibs/websocket/_wsdump.py231
-rw-r--r--libs/websocket/tests/echo-server.py21
-rw-r--r--libs/websocket/tests/test_abnf.py29
-rw-r--r--libs/websocket/tests/test_app.py43
-rw-r--r--libs/websocket/tests/test_cookiejar.py29
-rw-r--r--libs/websocket/tests/test_http.py94
-rw-r--r--libs/websocket/tests/test_url.py30
-rw-r--r--libs/websocket/tests/test_websocket.py71
21 files changed, 870 insertions, 593 deletions
diff --git a/libs/websocket/__init__.py b/libs/websocket/__init__.py
index 38da7b30d..05aae2bd7 100644
--- a/libs/websocket/__init__.py
+++ b/libs/websocket/__init__.py
@@ -1,22 +1,20 @@
"""
+__init__.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
from ._abnf import *
from ._app import WebSocketApp
@@ -25,4 +23,4 @@ from ._exceptions import *
from ._logging import *
from ._socket import *
-__version__ = "1.0.0"
+__version__ = "1.2.3"
diff --git a/libs/websocket/_abnf.py b/libs/websocket/_abnf.py
index 554f3b085..e9909ff62 100644
--- a/libs/websocket/_abnf.py
+++ b/libs/websocket/_abnf.py
@@ -3,52 +3,51 @@
"""
"""
+_abnf.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
import array
import os
import struct
+import sys
from ._exceptions import *
from ._utils import validate_utf8
from threading import Lock
try:
- import numpy
-except ImportError:
- numpy = None
+ # If wsaccel is available, use compiled routines to mask data.
+ # wsaccel only provides around a 10% speed boost compared
+ # to the websocket-client _mask() implementation.
+ # Note that wsaccel is unmaintained.
+ from wsaccel.xormask import XorMaskerSimple
-try:
- # If wsaccel is available we use compiled routines to mask data.
- if not numpy:
- from wsaccel.xormask import XorMaskerSimple
+ def _mask(_m, _d):
+ return XorMaskerSimple(_m).process(_d)
- def _mask(_m, _d):
- return XorMaskerSimple(_m).process(_d)
except ImportError:
- # wsaccel is not available, we rely on python implementations.
- def _mask(_m, _d):
- for i in range(len(_d)):
- _d[i] ^= _m[i % 4]
+ # wsaccel is not available, use websocket-client _mask()
+ native_byteorder = sys.byteorder
- return _d.tobytes()
+ def _mask(mask_value, data_value):
+ datalen = len(data_value)
+ data_value = int.from_bytes(data_value, native_byteorder)
+ mask_value = int.from_bytes(mask_value * (datalen // 4) + mask_value[: datalen % 4], native_byteorder)
+ return (data_value ^ mask_value).to_bytes(datalen, native_byteorder)
__all__ = [
@@ -97,7 +96,7 @@ VALID_CLOSE_STATUS = (
)
-class ABNF(object):
+class ABNF:
"""
ABNF frame class.
See http://tools.ietf.org/html/rfc5234
@@ -266,22 +265,10 @@ class ABNF(object):
if isinstance(data, str):
data = data.encode('latin-1')
- if numpy:
- origlen = len(data)
- _mask_key = mask_key[3] << 24 | mask_key[2] << 16 | mask_key[1] << 8 | mask_key[0]
-
- # We need data to be a multiple of four...
- data += b' ' * (4 - (len(data) % 4))
- a = numpy.frombuffer(data, dtype="uint32")
- masked = numpy.bitwise_xor(a, [_mask_key]).astype("uint32")
- if len(data) > origlen:
- return masked.tobytes()[:origlen]
- return masked.tobytes()
- else:
- return _mask(array.array("B", mask_key), array.array("B", data))
+ return _mask(array.array("B", mask_key), array.array("B", data))
-class frame_buffer(object):
+class frame_buffer:
_HEADER_MASK_INDEX = 5
_HEADER_LENGTH_INDEX = 6
@@ -374,7 +361,7 @@ class frame_buffer(object):
return frame
def recv_strict(self, bufsize):
- shortage = bufsize - sum(len(x) for x in self.recv_buffer)
+ shortage = bufsize - sum(map(len, self.recv_buffer))
while shortage > 0:
# Limit buffer size that we pass to socket.recv() to avoid
# fragmenting the heap -- the number of bytes recv() actually
@@ -386,7 +373,7 @@ class frame_buffer(object):
self.recv_buffer.append(bytes_)
shortage -= len(bytes_)
- unified = bytes("", 'utf-8').join(self.recv_buffer)
+ unified = b"".join(self.recv_buffer)
if shortage == 0:
self.recv_buffer = []
@@ -396,7 +383,7 @@ class frame_buffer(object):
return unified[:bufsize]
-class continuous_frame(object):
+class continuous_frame:
def __init__(self, fire_cont_frame, skip_utf8_validation):
self.fire_cont_frame = fire_cont_frame
diff --git a/libs/websocket/_app.py b/libs/websocket/_app.py
index 7dbacb3c6..1afd3d20a 100644
--- a/libs/websocket/_app.py
+++ b/libs/websocket/_app.py
@@ -3,24 +3,22 @@
"""
"""
+_app.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
import selectors
import sys
@@ -88,7 +86,7 @@ class SSLDispatcher:
return r[0][0]
-class WebSocketApp(object):
+class WebSocketApp:
"""
Higher level of APIs are provided. The interface is like JavaScript WebSocket object.
"""
@@ -105,52 +103,56 @@ class WebSocketApp(object):
Parameters
----------
- url: <type>
- websocket url.
+ url: str
+ Websocket url.
header: list or dict
- custom header for websocket handshake.
- on_open: <type>
- callable object which is called at opening websocket.
- this function has one argument. The argument is this class object.
- on_message: <type>
- callable object which is called when received data.
+ Custom header for websocket handshake.
+ on_open: function
+ Callback object which is called at opening websocket.
+ on_open has one argument.
+ The 1st argument is this class object.
+ on_message: function
+ Callback object which is called when received data.
on_message has 2 arguments.
The 1st argument is this class object.
- The 2nd argument is utf-8 string which we get from the server.
- on_error: <type>
- callable object which is called when we get error.
+ The 2nd argument is utf-8 data received from the server.
+ on_error: function
+ Callback object which is called when we get error.
on_error has 2 arguments.
The 1st argument is this class object.
The 2nd argument is exception object.
- on_close: <type>
- callable object which is called when closed the connection.
- this function has one argument. The argument is this class object.
- on_cont_message: <type>
- callback object which is called when receive continued
- frame data.
+ on_close: function
+ Callback object which is called when connection is closed.
+ on_close has 3 arguments.
+ The 1st argument is this class object.
+ The 2nd argument is close_status_code.
+ The 3rd argument is close_msg.
+ on_cont_message: function
+ Callback object which is called when a continuation
+ frame is received.
on_cont_message has 3 arguments.
The 1st argument is this class object.
The 2nd argument is utf-8 string which we get from the server.
The 3rd argument is continue flag. if 0, the data continue
to next frame data
- on_data: <type>
- callback object which is called when a message received.
+ on_data: function
+ Callback object which is called when a message received.
This is called before on_message or on_cont_message,
and then on_message or on_cont_message is called.
on_data has 4 argument.
The 1st argument is this class object.
The 2nd argument is utf-8 string which we get from the server.
The 3rd argument is data type. ABNF.OPCODE_TEXT or ABNF.OPCODE_BINARY will be came.
- The 4th argument is continue flag. if 0, the data continue
- keep_running: <type>
- this parameter is obsolete and ignored.
- get_mask_key: func
- a callable to produce new mask keys,
- see the WebSocket.set_mask_key's docstring for more information
+ The 4th argument is continue flag. If 0, the data continue
+ keep_running: bool
+ This parameter is obsolete and ignored.
+ get_mask_key: function
+ A callable function to get new mask keys, see the
+ WebSocket.set_mask_key's docstring for more information.
cookie: str
- cookie value.
- subprotocols: <type>
- array of available sub protocols. default is None.
+ Cookie value.
+ subprotocols: list
+ List of available sub protocols. Default is None.
"""
self.url = url
self.header = header if header is not None else []
@@ -177,11 +179,11 @@ class WebSocketApp(object):
Parameters
----------
- data: <type>
+ data: str
Message to send. If you set opcode to OPCODE_TEXT,
data must be utf-8 string or unicode.
- opcode: <type>
- Operation code of data. default is OPCODE_TEXT.
+ opcode: int
+ Operation code of data. Default is OPCODE_TEXT.
"""
if not self.sock or self.sock.send(data, opcode) == 0:
@@ -223,32 +225,32 @@ class WebSocketApp(object):
Parameters
----------
sockopt: tuple
- values for socket.setsockopt.
+ Values for socket.setsockopt.
sockopt must be tuple
and each element is argument of sock.setsockopt.
sslopt: dict
- optional dict object for ssl socket option.
+ Optional dict object for ssl socket option.
ping_interval: int or float
- automatically send "ping" command
- every specified period (in seconds)
- if set to 0, not send automatically.
+ Automatically send "ping" command
+ every specified period (in seconds).
+ If set to 0, no ping is sent periodically.
ping_timeout: int or float
- timeout (in seconds) if the pong message is not received.
+ Timeout (in seconds) if the pong message is not received.
ping_payload: str
- payload message to send with each ping.
- http_proxy_host: <type>
- http proxy host name.
- http_proxy_port: <type>
- http proxy port. If not set, set to 80.
- http_no_proxy: <type>
- host names, which doesn't use proxy.
+ Payload message to send with each ping.
+ http_proxy_host: str
+ HTTP proxy host name.
+ http_proxy_port: int or str
+ HTTP proxy port. If not set, set to 80.
+ http_no_proxy: list
+ Whitelisted host names that don't use the proxy.
skip_utf8_validation: bool
skip utf8 validation.
host: str
update host header.
origin: str
update origin header.
- dispatcher: <type>
+ dispatcher: Dispatcher object
customize reading data from socket.
suppress_origin: bool
suppress outputting origin header.
@@ -280,8 +282,11 @@ class WebSocketApp(object):
"""
Tears down the connection.
- If close_frame is set, we will invoke the on_close handler with the
- statusCode and reason from there.
+ Parameters
+ ----------
+ close_frame: ABNF frame
+ If close_frame is set, the on_close handler is invoked
+ with the statusCode and reason from the provided frame.
"""
if thread and thread.is_alive():
@@ -292,15 +297,17 @@ class WebSocketApp(object):
self.sock.close()
close_status_code, close_reason = self._get_close_args(
close_frame if close_frame else None)
- self._callback(self.on_close, close_status_code, close_reason)
self.sock = None
+ # Finally call the callback AFTER all teardown is complete
+ self._callback(self.on_close, close_status_code, close_reason)
+
try:
self.sock = WebSocket(
self.get_mask_key, sockopt=sockopt, sslopt=sslopt,
fire_cont_frame=self.on_cont_message is not None,
skip_utf8_validation=skip_utf8_validation,
- enable_multithread=True if ping_interval else False)
+ enable_multithread=True)
self.sock.settimeout(getdefaulttimeout())
self.sock.connect(
self.url, header=self.header, cookie=self.cookie,
@@ -401,6 +408,5 @@ class WebSocketApp(object):
except Exception as e:
_logging.error("error from callback {}: {}".format(callback, e))
- if _logging.isEnabledForDebug():
- _, _, tb = sys.exc_info()
- traceback.print_tb(tb)
+ if self.on_error:
+ self.on_error(self, e)
diff --git a/libs/websocket/_cookiejar.py b/libs/websocket/_cookiejar.py
index 0359d0355..878538348 100644
--- a/libs/websocket/_cookiejar.py
+++ b/libs/websocket/_cookiejar.py
@@ -3,29 +3,27 @@
"""
"""
+_cookiejar.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
import http.cookies
-class SimpleCookieJar(object):
+class SimpleCookieJar:
def __init__(self):
self.jar = dict()
diff --git a/libs/websocket/_core.py b/libs/websocket/_core.py
index 1e36d90b2..e26c8b115 100644
--- a/libs/websocket/_core.py
+++ b/libs/websocket/_core.py
@@ -5,24 +5,22 @@ WebSocket Python client
"""
"""
+_core.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Copyright 2021 engn33r
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
import socket
import struct
@@ -42,7 +40,7 @@ from ._utils import *
__all__ = ['WebSocket', 'create_connection']
-class WebSocket(object):
+class WebSocket:
"""
Low level WebSocket interface.
@@ -62,30 +60,31 @@ class WebSocket(object):
Parameters
----------
get_mask_key: func
- a callable to produce new mask keys, see the set_mask_key
- function's docstring for more details
+ A callable function to get new mask keys, see the
+ WebSocket.set_mask_key's docstring for more information.
sockopt: tuple
- values for socket.setsockopt.
+ Values for socket.setsockopt.
sockopt must be tuple and each element is argument of sock.setsockopt.
sslopt: dict
- optional dict object for ssl socket option.
+ Optional dict object for ssl socket options. See FAQ for details.
fire_cont_frame: bool
- fire recv event for each cont frame. default is False
+ Fire recv event for each cont frame. Default is False.
enable_multithread: bool
- if set to True, lock send method.
+ If set to True, lock send method.
skip_utf8_validation: bool
- skip utf8 validation.
+ Skip utf8 validation.
"""
def __init__(self, get_mask_key=None, sockopt=None, sslopt=None,
- fire_cont_frame=False, enable_multithread=False,
+ fire_cont_frame=False, enable_multithread=True,
skip_utf8_validation=False, **_):
"""
Initialize WebSocket object.
Parameters
----------
- sslopt: specify ssl certification verification options
+ sslopt: dict
+ Optional dict object for ssl socket options. See FAQ for details.
"""
self.sock_opt = sock_opt(sockopt, sslopt)
self.handshake_response = None
@@ -194,7 +193,10 @@ class WebSocket(object):
return None
def is_ssl(self):
- return isinstance(self.sock, ssl.SSLSocket)
+ try:
+ return isinstance(self.sock, ssl.SSLSocket)
+ except:
+ return False
headers = property(getheaders)
@@ -210,40 +212,38 @@ class WebSocket(object):
... header=["User-Agent: MyProgram",
... "x-custom: header"])
- timeout: <type>
- socket timeout time. This value is an integer or float.
- if you set None for this value, it means "use default_timeout value"
-
Parameters
----------
- options:
- - header: list or dict
- custom http header list or dict.
- - cookie: str
- cookie value.
- - origin: str
- custom origin url.
- - connection: str
- custom connection header value.
- default value "Upgrade" set in _handshake.py
- - suppress_origin: bool
- suppress outputting origin header.
- - host: str
- custom host header string.
- - http_proxy_host: <type>
- http proxy host name.
- - http_proxy_port: <type>
- http proxy port. If not set, set to 80.
- - http_no_proxy: <type>
- host names, which doesn't use proxy.
- - http_proxy_auth: <type>
- http proxy auth information. tuple of username and password. default is None
- - redirect_limit: <type>
- number of redirects to follow.
- - subprotocols: <type>
- array of available sub protocols. default is None.
- - socket: <type>
- pre-initialized stream socket.
+ header: list or dict
+ Custom http header list or dict.
+ cookie: str
+ Cookie value.
+ origin: str
+ Custom origin url.
+ connection: str
+ Custom connection header value.
+ Default value "Upgrade" set in _handshake.py
+ suppress_origin: bool
+ Suppress outputting origin header.
+ host: str
+ Custom host header string.
+ timeout: int or float
+ Socket timeout time. This value is an integer or float.
+ If you set None for this value, it means "use default_timeout value"
+ http_proxy_host: str
+ HTTP proxy host name.
+ http_proxy_port: str or int
+ HTTP proxy port. Default is 80.
+ http_no_proxy: list
+ Whitelisted host names that don't use the proxy.
+ http_proxy_auth: tuple
+ HTTP proxy auth information. Tuple of username and password. Default is None.
+ redirect_limit: int
+ Number of redirects to follow.
+ subprotocols: list
+ List of available subprotocols. Default is None.
+ socket: socket
+ Pre-initialized stream socket.
"""
self.sock_opt.timeout = options.get('timeout', self.sock_opt.timeout)
self.sock, addrs = connect(url, self.sock_opt, proxy_info(**options),
@@ -271,12 +271,12 @@ class WebSocket(object):
Parameters
----------
- payload: str
- Payload must be utf-8 string or unicode,
- if the opcode is OPCODE_TEXT.
- Otherwise, it must be string(byte array)
- opcode: int
- operation code to send. Please see OPCODE_XXX.
+ payload: str
+ Payload must be utf-8 string or unicode,
+ If the opcode is OPCODE_TEXT.
+ Otherwise, it must be string(byte array).
+ opcode: int
+ Operation code (opcode) to send.
"""
frame = ABNF.create_frame(payload, opcode)
@@ -314,6 +314,14 @@ class WebSocket(object):
return length
def send_binary(self, payload):
+ """
+ Send a binary message (OPCODE_BINARY).
+
+ Parameters
+ ----------
+ payload: bytes
+ payload of message to send.
+ """
return self.send(payload, ABNF.OPCODE_BINARY)
def ping(self, payload=""):
@@ -381,6 +389,8 @@ class WebSocket(object):
"""
Receive data with operation code.
+ If a valid ping message is received, a pong response is sent.
+
Parameters
----------
control_frame: bool
@@ -434,34 +444,34 @@ class WebSocket(object):
"""
return self.frame_buffer.recv_frame()
- def send_close(self, status=STATUS_NORMAL, reason=bytes('', encoding='utf-8')):
+ def send_close(self, status=STATUS_NORMAL, reason=b""):
"""
Send close data to the server.
Parameters
----------
- status: <type>
- status code to send. see STATUS_XXX.
+ status: int
+ Status code to send. See STATUS_XXX.
reason: str or bytes
- the reason to close. This must be string or bytes.
+ The reason to close. This must be string or UTF-8 bytes.
"""
if status < 0 or status >= ABNF.LENGTH_16:
raise ValueError("code is invalid range")
self.connected = False
self.send(struct.pack('!H', status) + reason, ABNF.OPCODE_CLOSE)
- def close(self, status=STATUS_NORMAL, reason=bytes('', encoding='utf-8'), timeout=3):
+ def close(self, status=STATUS_NORMAL, reason=b"", timeout=3):
"""
Close Websocket object
Parameters
----------
status: int
- status code to send. see STATUS_XXX.
+ Status code to send. See STATUS_XXX.
reason: bytes
- the reason to close.
+ The reason to close in UTF-8.
timeout: int or float
- timeout until receive a close frame.
+ Timeout until receive a close frame.
If None, it will wait forever until receive a close frame.
"""
if self.connected:
@@ -490,10 +500,8 @@ class WebSocket(object):
break
self.sock.settimeout(sock_timeout)
self.sock.shutdown(socket.SHUT_RDWR)
- except OSError: # This happens often on Mac
- pass
except:
- raise
+ pass
self.shutdown()
@@ -544,52 +552,51 @@ def create_connection(url, timeout=None, class_=WebSocket, **options):
Parameters
----------
+ class_: class
+ class to instantiate when creating the connection. It has to implement
+ settimeout and connect. It's __init__ should be compatible with
+ WebSocket.__init__, i.e. accept all of it's kwargs.
+ header: list or dict
+ custom http header list or dict.
+ cookie: str
+ Cookie value.
+ origin: str
+ custom origin url.
+ suppress_origin: bool
+ suppress outputting origin header.
+ host: str
+ custom host header string.
timeout: int or float
- socket timeout time. This value could be either float/integer.
- if you set None for this value,
- it means "use default_timeout value"
- class_: <type>
- class to instantiate when creating the connection. It has to implement
- settimeout and connect. It's __init__ should be compatible with
- WebSocket.__init__, i.e. accept all of it's kwargs.
- options: <type>
- - header: list or dict
- custom http header list or dict.
- - cookie: str
- cookie value.
- - origin: str
- custom origin url.
- - suppress_origin: bool
- suppress outputting origin header.
- - host: <type>
- custom host header string.
- - http_proxy_host: <type>
- http proxy host name.
- - http_proxy_port: <type>
- http proxy port. If not set, set to 80.
- - http_no_proxy: <type>
- host names, which doesn't use proxy.
- - http_proxy_auth: <type>
- http proxy auth information. tuple of username and password. default is None
- - enable_multithread: bool
- enable lock for multithread.
- - redirect_limit: <type>
- number of redirects to follow.
- - sockopt: <type>
- socket options
- - sslopt: <type>
- ssl option
- - subprotocols: <type>
- array of available sub protocols. default is None.
- - skip_utf8_validation: bool
- skip utf8 validation.
- - socket: <type>
- pre-initialized stream socket.
+ socket timeout time. This value could be either float/integer.
+ If set to None, it uses the default_timeout value.
+ http_proxy_host: str
+ HTTP proxy host name.
+ http_proxy_port: str or int
+ HTTP proxy port. If not set, set to 80.
+ http_no_proxy: list
+ Whitelisted host names that don't use the proxy.
+ http_proxy_auth: tuple
+ HTTP proxy auth information. tuple of username and password. Default is None.
+ enable_multithread: bool
+ Enable lock for multithread.
+ redirect_limit: int
+ Number of redirects to follow.
+ sockopt: tuple
+ Values for socket.setsockopt.
+ sockopt must be a tuple and each element is an argument of sock.setsockopt.
+ sslopt: dict
+ Optional dict object for ssl socket options. See FAQ for details.
+ subprotocols: list
+ List of available subprotocols. Default is None.
+ skip_utf8_validation: bool
+ Skip utf8 validation.
+ socket: socket
+ Pre-initialized stream socket.
"""
sockopt = options.pop("sockopt", [])
sslopt = options.pop("sslopt", {})
fire_cont_frame = options.pop("fire_cont_frame", False)
- enable_multithread = options.pop("enable_multithread", False)
+ enable_multithread = options.pop("enable_multithread", True)
skip_utf8_validation = options.pop("skip_utf8_validation", False)
websock = class_(sockopt=sockopt, sslopt=sslopt,
fire_cont_frame=fire_cont_frame,
diff --git a/libs/websocket/_exceptions.py b/libs/websocket/_exceptions.py
index 83c6e42b7..b92b1f408 100644
--- a/libs/websocket/_exceptions.py
+++ b/libs/websocket/_exceptions.py
@@ -3,24 +3,22 @@ Define WebSocket exceptions
"""
"""
+_exceptions.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
@@ -74,7 +72,7 @@ class WebSocketBadStatusException(WebSocketException):
def __init__(self, message, status_code, status_message=None, resp_headers=None):
msg = message % (status_code, status_message)
- super(WebSocketBadStatusException, self).__init__(msg)
+ super().__init__(msg)
self.status_code = status_code
self.resp_headers = resp_headers
diff --git a/libs/websocket/_handshake.py b/libs/websocket/_handshake.py
index 74ccbaf03..f9dabb572 100644
--- a/libs/websocket/_handshake.py
+++ b/libs/websocket/_handshake.py
@@ -1,22 +1,20 @@
"""
+_handshake.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
import hashlib
import hmac
@@ -40,7 +38,7 @@ SUCCESS_STATUSES = SUPPORTED_REDIRECT_STATUSES + (HTTPStatus.SWITCHING_PROTOCOLS
CookieJar = SimpleCookieJar()
-class handshake_response(object):
+class handshake_response:
def __init__(self, status, headers, subprotocol):
self.status = status
diff --git a/libs/websocket/_http.py b/libs/websocket/_http.py
index a13667a24..603fa00fa 100644
--- a/libs/websocket/_http.py
+++ b/libs/websocket/_http.py
@@ -1,22 +1,20 @@
"""
+_http.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
import errno
import os
@@ -34,58 +32,72 @@ from base64 import encodebytes as base64encode
__all__ = ["proxy_info", "connect", "read_headers"]
try:
- import socks
- ProxyConnectionError = socks.ProxyConnectionError
- HAS_PYSOCKS = True
+ from python_socks.sync import Proxy
+ from python_socks._errors import *
+ from python_socks._types import ProxyType
+ HAVE_PYTHON_SOCKS = True
except:
- class ProxyConnectionError(BaseException):
+ HAVE_PYTHON_SOCKS = False
+
+ class ProxyError(Exception):
+ pass
+
+ class ProxyTimeoutError(Exception):
+ pass
+
+ class ProxyConnectionError(Exception):
pass
- HAS_PYSOCKS = False
-class proxy_info(object):
+class proxy_info:
def __init__(self, **options):
- self.type = options.get("proxy_type") or "http"
- if not(self.type in ['http', 'socks4', 'socks5', 'socks5h']):
- raise ValueError("proxy_type must be 'http', 'socks4', 'socks5' or 'socks5h'")
- self.host = options.get("http_proxy_host", None)
- if self.host:
- self.port = options.get("http_proxy_port", 0)
+ self.proxy_host = options.get("http_proxy_host", None)
+ if self.proxy_host:
+ self.proxy_port = options.get("http_proxy_port", 0)
self.auth = options.get("http_proxy_auth", None)
self.no_proxy = options.get("http_no_proxy", None)
+ self.proxy_protocol = options.get("proxy_type", "http")
+ # Note: If timeout not specified, default python-socks timeout is 60 seconds
+ self.proxy_timeout = options.get("timeout", None)
+ if self.proxy_protocol not in ['http', 'socks4', 'socks4a', 'socks5', 'socks5h']:
+ raise ProxyError("Only http, socks4, socks5 proxy protocols are supported")
else:
- self.port = 0
+ self.proxy_port = 0
self.auth = None
self.no_proxy = None
+ self.proxy_protocol = "http"
-def _open_proxied_socket(url, options, proxy):
- hostname, port, resource, is_secure = parse_url(url)
+def _start_proxied_socket(url, options, proxy):
+ if not HAVE_PYTHON_SOCKS:
+ raise WebSocketException("Python Socks is needed for SOCKS proxying but is not available")
- if not HAS_PYSOCKS:
- raise WebSocketException("PySocks module not found.")
+ hostname, port, resource, is_secure = parse_url(url)
- ptype = socks.SOCKS5
- rdns = False
- if proxy.type == "socks4":
- ptype = socks.SOCKS4
- if proxy.type == "http":
- ptype = socks.HTTP
- if proxy.type[-1] == "h":
+ if proxy.proxy_protocol == "socks5":
+ rdns = False
+ proxy_type = ProxyType.SOCKS5
+ if proxy.proxy_protocol == "socks4":
+ rdns = False
+ proxy_type = ProxyType.SOCKS4
+ # socks5h and socks4a send DNS through proxy
+ if proxy.proxy_protocol == "socks5h":
rdns = True
+ proxy_type = ProxyType.SOCKS5
+ if proxy.proxy_protocol == "socks4a":
+ rdns = True
+ proxy_type = ProxyType.SOCKS4
- sock = socks.create_connection(
- (hostname, port),
- proxy_type=ptype,
- proxy_addr=proxy.host,
- proxy_port=proxy.port,
- proxy_rdns=rdns,
- proxy_username=proxy.auth[0] if proxy.auth else None,
- proxy_password=proxy.auth[1] if proxy.auth else None,
- timeout=options.timeout,
- socket_options=DEFAULT_SOCKET_OPTION + options.sockopt
- )
+ ws_proxy = Proxy.create(
+ proxy_type=proxy_type,
+ host=proxy.proxy_host,
+ port=int(proxy.proxy_port),
+ username=proxy.auth[0] if proxy.auth else None,
+ password=proxy.auth[1] if proxy.auth else None,
+ rdns=rdns)
+
+ sock = ws_proxy.connect(hostname, port, timeout=proxy.proxy_timeout)
if is_secure and HAVE_SSL:
sock = _ssl_socket(sock, options.sslopt, hostname)
@@ -96,8 +108,11 @@ def _open_proxied_socket(url, options, proxy):
def connect(url, options, proxy, socket):
- if proxy.host and not socket and not (proxy.type == 'http'):
- return _open_proxied_socket(url, options, proxy)
+ # Use _start_proxied_socket() only for socks4 or socks5 proxy
+ # Use _tunnel() for http proxy
+ # TODO: Use python-socks for http protocol also, to standardize flow
+ if proxy.proxy_host and not socket and not (proxy.proxy_protocol == "http"):
+ return _start_proxied_socket(url, options, proxy)
hostname, port, resource, is_secure = parse_url(url)
@@ -131,7 +146,7 @@ def connect(url, options, proxy, socket):
def _get_addrinfo_list(hostname, port, is_secure, proxy):
phost, pport, pauth = get_proxy_info(
- hostname, is_secure, proxy.host, proxy.port, proxy.auth, proxy.no_proxy)
+ hostname, is_secure, proxy.proxy_host, proxy.proxy_port, proxy.auth, proxy.no_proxy)
try:
# when running on windows 10, getaddrinfo without socktype returns a socktype 0.
# This generates an error exception: `_on_error: exception Socket type must be stream or datagram, not 0`
@@ -168,10 +183,6 @@ def _open_socket(addrinfo_list, sockopt, timeout):
while not err:
try:
sock.connect(address)
- except ProxyConnectionError as error:
- err = WebSocketProxyException(str(error))
- err.remote_ip = str(address[0])
- continue
except socket.error as error:
error.remote_ip = str(address[0])
try:
@@ -200,33 +211,41 @@ def _open_socket(addrinfo_list, sockopt, timeout):
def _wrap_sni_socket(sock, sslopt, hostname, check_hostname):
- context = ssl.SSLContext(sslopt.get('ssl_version', ssl.PROTOCOL_SSLv23))
-
- if sslopt.get('cert_reqs', ssl.CERT_NONE) != ssl.CERT_NONE:
- cafile = sslopt.get('ca_certs', None)
- capath = sslopt.get('ca_cert_path', None)
- if cafile or capath:
- context.load_verify_locations(cafile=cafile, capath=capath)
- elif hasattr(context, 'load_default_certs'):
- context.load_default_certs(ssl.Purpose.SERVER_AUTH)
- if sslopt.get('certfile', None):
- context.load_cert_chain(
- sslopt['certfile'],
- sslopt.get('keyfile', None),
- sslopt.get('password', None),
- )
- # see
- # https://github.com/liris/websocket-client/commit/b96a2e8fa765753e82eea531adb19716b52ca3ca#commitcomment-10803153
- context.verify_mode = sslopt['cert_reqs']
- if HAVE_CONTEXT_CHECK_HOSTNAME:
- context.check_hostname = check_hostname
- if 'ciphers' in sslopt:
- context.set_ciphers(sslopt['ciphers'])
- if 'cert_chain' in sslopt:
- certfile, keyfile, password = sslopt['cert_chain']
- context.load_cert_chain(certfile, keyfile, password)
- if 'ecdh_curve' in sslopt:
- context.set_ecdh_curve(sslopt['ecdh_curve'])
+ context = sslopt.get('context', None)
+ if not context:
+ context = ssl.SSLContext(sslopt.get('ssl_version', ssl.PROTOCOL_TLS_CLIENT))
+
+ if sslopt.get('cert_reqs', ssl.CERT_NONE) != ssl.CERT_NONE:
+ cafile = sslopt.get('ca_certs', None)
+ capath = sslopt.get('ca_cert_path', None)
+ if cafile or capath:
+ context.load_verify_locations(cafile=cafile, capath=capath)
+ elif hasattr(context, 'load_default_certs'):
+ context.load_default_certs(ssl.Purpose.SERVER_AUTH)
+ if sslopt.get('certfile', None):
+ context.load_cert_chain(
+ sslopt['certfile'],
+ sslopt.get('keyfile', None),
+ sslopt.get('password', None),
+ )
+
+ # Python 3.10 switch to PROTOCOL_TLS_CLIENT defaults to "cert_reqs = ssl.CERT_REQUIRED" and "check_hostname = True"
+ # If both disabled, set check_hostname before verify_mode
+ # see https://github.com/liris/websocket-client/commit/b96a2e8fa765753e82eea531adb19716b52ca3ca#commitcomment-10803153
+ if sslopt.get('cert_reqs', ssl.CERT_NONE) == ssl.CERT_NONE and not sslopt.get('check_hostname', False):
+ context.check_hostname = False
+ context.verify_mode = ssl.CERT_NONE
+ else:
+ context.check_hostname = sslopt.get('check_hostname', True)
+ context.verify_mode = sslopt.get('cert_reqs', ssl.CERT_REQUIRED)
+
+ if 'ciphers' in sslopt:
+ context.set_ciphers(sslopt['ciphers'])
+ if 'cert_chain' in sslopt:
+ certfile, keyfile, password = sslopt['cert_chain']
+ context.load_cert_chain(certfile, keyfile, password)
+ if 'ecdh_curve' in sslopt:
+ context.set_ecdh_curve(sslopt['ecdh_curve'])
return context.wrap_socket(
sock,
@@ -248,12 +267,11 @@ def _ssl_socket(sock, user_sslopt, hostname):
and user_sslopt.get('ca_cert_path', None) is None:
sslopt['ca_cert_path'] = certPath
- check_hostname = sslopt["cert_reqs"] != ssl.CERT_NONE and sslopt.pop(
- 'check_hostname', True)
- sock = _wrap_sni_socket(sock, sslopt, hostname, check_hostname)
+ if sslopt.get('server_hostname', None):
+ hostname = sslopt['server_hostname']
- if not HAVE_CONTEXT_CHECK_HOSTNAME and check_hostname:
- match_hostname(sock.getpeercert(), hostname)
+ check_hostname = sslopt.get('check_hostname', True)
+ sock = _wrap_sni_socket(sock, sslopt, hostname, check_hostname)
return sock
diff --git a/libs/websocket/_logging.py b/libs/websocket/_logging.py
index 07d900903..480d43b09 100644
--- a/libs/websocket/_logging.py
+++ b/libs/websocket/_logging.py
@@ -3,24 +3,22 @@
"""
"""
+_logging.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
import logging
diff --git a/libs/websocket/_socket.py b/libs/websocket/_socket.py
index 70e163f27..4d9cc097e 100644
--- a/libs/websocket/_socket.py
+++ b/libs/websocket/_socket.py
@@ -3,24 +3,22 @@
"""
"""
+_socket.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
import errno
import selectors
@@ -46,7 +44,7 @@ __all__ = ["DEFAULT_SOCKET_OPTION", "sock_opt", "setdefaulttimeout", "getdefault
"recv", "recv_line", "send"]
-class sock_opt(object):
+class sock_opt:
def __init__(self, sockopt, sslopt):
if sockopt is None:
@@ -125,7 +123,7 @@ def recv(sock, bufsize):
if not bytes_:
raise WebSocketConnectionClosedException(
- "Connection is already closed.")
+ "Connection to remote host was lost.")
return bytes_
diff --git a/libs/websocket/_ssl_compat.py b/libs/websocket/_ssl_compat.py
index e0fc34917..f4af524e4 100644
--- a/libs/websocket/_ssl_compat.py
+++ b/libs/websocket/_ssl_compat.py
@@ -1,22 +1,20 @@
"""
+_ssl_compat.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
__all__ = ["HAVE_SSL", "ssl", "SSLError", "SSLWantReadError", "SSLWantWriteError"]
@@ -25,11 +23,6 @@ try:
from ssl import SSLError
from ssl import SSLWantReadError
from ssl import SSLWantWriteError
- HAVE_CONTEXT_CHECK_HOSTNAME = False
- if hasattr(ssl, 'SSLContext') and hasattr(ssl.SSLContext, 'check_hostname'):
- HAVE_CONTEXT_CHECK_HOSTNAME = True
-
- __all__.append("HAVE_CONTEXT_CHECK_HOSTNAME")
HAVE_SSL = True
except ImportError:
# dummy class of SSLError for environment without ssl support
diff --git a/libs/websocket/_url.py b/libs/websocket/_url.py
index 32aaf23f6..f2a55019a 100644
--- a/libs/websocket/_url.py
+++ b/libs/websocket/_url.py
@@ -2,31 +2,29 @@
"""
"""
+_url.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
import os
import socket
import struct
-from urllib.parse import urlparse
+from urllib.parse import unquote, urlparse
__all__ = ["parse_url", "get_proxy_info"]
@@ -109,7 +107,7 @@ def _is_address_in_network(ip, net):
def _is_no_proxy_host(hostname, no_proxy):
if not no_proxy:
- v = os.environ.get("no_proxy", "").replace(" ", "")
+ v = os.environ.get("no_proxy", os.environ.get("NO_PROXY", "")).replace(" ", "")
if v:
no_proxy = v.split(",")
if not no_proxy:
@@ -139,22 +137,22 @@ def get_proxy_info(
Parameters
----------
- hostname: <type>
- websocket server name.
- is_secure: <type>
- is the connection secure? (wss) looks for "https_proxy" in env
+ hostname: str
+ Websocket server name.
+ is_secure: bool
+ Is the connection secure? (wss) looks for "https_proxy" in env
before falling back to "http_proxy"
- options: <type>
- - http_proxy_host: <type>
- http proxy host name.
- - http_proxy_port: <type>
- http proxy port.
- - http_no_proxy: <type>
- host names, which doesn't use proxy.
- - http_proxy_auth: <type>
- http proxy auth information. tuple of username and password. default is None
- - proxy_type: <type>
- if set to "socks5" PySocks wrapper will be used in place of a http proxy. default is "http"
+ proxy_host: str
+ http proxy host name.
+ http_proxy_port: str or int
+ http proxy port.
+ http_no_proxy: list
+ Whitelisted host names that don't use the proxy.
+ http_proxy_auth: tuple
+ HTTP proxy auth information. Tuple of username and password. Default is None.
+ proxy_type: str
+ Specify the proxy protocol (http, socks4, socks4a, socks5, socks5h). Default is "http".
+ Use socks4a or socks5h if you want to send DNS requests through the proxy.
"""
if _is_no_proxy_host(hostname, no_proxy):
return None, 0, None
@@ -169,10 +167,10 @@ def get_proxy_info(
env_keys.insert(0, "https_proxy")
for key in env_keys:
- value = os.environ.get(key, None)
+ value = os.environ.get(key, os.environ.get(key.upper(), "")).replace(" ", "")
if value:
proxy = urlparse(value)
- auth = (proxy.username, proxy.password) if proxy.username else None
+ auth = (unquote(proxy.username), unquote(proxy.password)) if proxy.username else None
return proxy.hostname, proxy.port, auth
return None, 0, None
diff --git a/libs/websocket/_utils.py b/libs/websocket/_utils.py
index 0b9a93729..21fc437c5 100644
--- a/libs/websocket/_utils.py
+++ b/libs/websocket/_utils.py
@@ -1,27 +1,25 @@
"""
+_url.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
__all__ = ["NoLock", "validate_utf8", "extract_err_message", "extract_error_code"]
-class NoLock(object):
+class NoLock:
def __enter__(self):
pass
diff --git a/libs/websocket/_wsdump.py b/libs/websocket/_wsdump.py
new file mode 100755
index 000000000..4d15f4134
--- /dev/null
+++ b/libs/websocket/_wsdump.py
@@ -0,0 +1,231 @@
+#!/usr/bin/env python3
+
+"""
+wsdump.py
+websocket - WebSocket client library for Python
+
+Copyright 2021 engn33r
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+import argparse
+import code
+import sys
+import threading
+import time
+import ssl
+import gzip
+import zlib
+from urllib.parse import urlparse
+
+import websocket
+
+try:
+ import readline
+except ImportError:
+ pass
+
+
+def get_encoding():
+ encoding = getattr(sys.stdin, "encoding", "")
+ if not encoding:
+ return "utf-8"
+ else:
+ return encoding.lower()
+
+
+OPCODE_DATA = (websocket.ABNF.OPCODE_TEXT, websocket.ABNF.OPCODE_BINARY)
+ENCODING = get_encoding()
+
+
+class VAction(argparse.Action):
+
+ def __call__(self, parser, args, values, option_string=None):
+ if values is None:
+ values = "1"
+ try:
+ values = int(values)
+ except ValueError:
+ values = values.count("v") + 1
+ setattr(args, self.dest, values)
+
+
+def parse_args():
+ parser = argparse.ArgumentParser(description="WebSocket Simple Dump Tool")
+ parser.add_argument("url", metavar="ws_url",
+ help="websocket url. ex. ws://echo.websocket.org/")
+ parser.add_argument("-p", "--proxy",
+ help="proxy url. ex. http://127.0.0.1:8080")
+ parser.add_argument("-v", "--verbose", default=0, nargs='?', action=VAction,
+ dest="verbose",
+ help="set verbose mode. If set to 1, show opcode. "
+ "If set to 2, enable to trace websocket module")
+ parser.add_argument("-n", "--nocert", action='store_true',
+ help="Ignore invalid SSL cert")
+ parser.add_argument("-r", "--raw", action="store_true",
+ help="raw output")
+ parser.add_argument("-s", "--subprotocols", nargs='*',
+ help="Set subprotocols")
+ parser.add_argument("-o", "--origin",
+ help="Set origin")
+ parser.add_argument("--eof-wait", default=0, type=int,
+ help="wait time(second) after 'EOF' received.")
+ parser.add_argument("-t", "--text",
+ help="Send initial text")
+ parser.add_argument("--timings", action="store_true",
+ help="Print timings in seconds")
+ parser.add_argument("--headers",
+ help="Set custom headers. Use ',' as separator")
+
+ return parser.parse_args()
+
+
+class RawInput:
+
+ def raw_input(self, prompt):
+ line = input(prompt)
+
+ if ENCODING and ENCODING != "utf-8" and not isinstance(line, str):
+ line = line.decode(ENCODING).encode("utf-8")
+ elif isinstance(line, str):
+ line = line.encode("utf-8")
+
+ return line
+
+
+class InteractiveConsole(RawInput, code.InteractiveConsole):
+
+ def write(self, data):
+ sys.stdout.write("\033[2K\033[E")
+ # sys.stdout.write("\n")
+ sys.stdout.write("\033[34m< " + data + "\033[39m")
+ sys.stdout.write("\n> ")
+ sys.stdout.flush()
+
+ def read(self):
+ return self.raw_input("> ")
+
+
+class NonInteractive(RawInput):
+
+ def write(self, data):
+ sys.stdout.write(data)
+ sys.stdout.write("\n")
+ sys.stdout.flush()
+
+ def read(self):
+ return self.raw_input("")
+
+
+def main():
+ start_time = time.time()
+ args = parse_args()
+ if args.verbose > 1:
+ websocket.enableTrace(True)
+ options = {}
+ if args.proxy:
+ p = urlparse(args.proxy)
+ options["http_proxy_host"] = p.hostname
+ options["http_proxy_port"] = p.port
+ if args.origin:
+ options["origin"] = args.origin
+ if args.subprotocols:
+ options["subprotocols"] = args.subprotocols
+ opts = {}
+ if args.nocert:
+ opts = {"cert_reqs": ssl.CERT_NONE, "check_hostname": False}
+ if args.headers:
+ options['header'] = list(map(str.strip, args.headers.split(',')))
+ ws = websocket.create_connection(args.url, sslopt=opts, **options)
+ if args.raw:
+ console = NonInteractive()
+ else:
+ console = InteractiveConsole()
+ print("Press Ctrl+C to quit")
+
+ def recv():
+ try:
+ frame = ws.recv_frame()
+ except websocket.WebSocketException:
+ return websocket.ABNF.OPCODE_CLOSE, None
+ if not frame:
+ raise websocket.WebSocketException("Not a valid frame %s" % frame)
+ elif frame.opcode in OPCODE_DATA:
+ return frame.opcode, frame.data
+ elif frame.opcode == websocket.ABNF.OPCODE_CLOSE:
+ ws.send_close()
+ return frame.opcode, None
+ elif frame.opcode == websocket.ABNF.OPCODE_PING:
+ ws.pong(frame.data)
+ return frame.opcode, frame.data
+
+ return frame.opcode, frame.data
+
+ def recv_ws():
+ while True:
+ opcode, data = recv()
+ msg = None
+ if opcode == websocket.ABNF.OPCODE_TEXT and isinstance(data, bytes):
+ data = str(data, "utf-8")
+ if isinstance(data, bytes) and len(data) > 2 and data[:2] == b'\037\213': # gzip magick
+ try:
+ data = "[gzip] " + str(gzip.decompress(data), "utf-8")
+ except:
+ pass
+ elif isinstance(data, bytes):
+ try:
+ data = "[zlib] " + str(zlib.decompress(data, -zlib.MAX_WBITS), "utf-8")
+ except:
+ pass
+
+ if isinstance(data, bytes):
+ data = repr(data)
+
+ if args.verbose:
+ msg = "%s: %s" % (websocket.ABNF.OPCODE_MAP.get(opcode), data)
+ else:
+ msg = data
+
+ if msg is not None:
+ if args.timings:
+ console.write(str(time.time() - start_time) + ": " + msg)
+ else:
+ console.write(msg)
+
+ if opcode == websocket.ABNF.OPCODE_CLOSE:
+ break
+
+ thread = threading.Thread(target=recv_ws)
+ thread.daemon = True
+ thread.start()
+
+ if args.text:
+ ws.send(args.text)
+
+ while True:
+ try:
+ message = console.read()
+ ws.send(message)
+ except KeyboardInterrupt:
+ return
+ except EOFError:
+ time.sleep(args.eof_wait)
+ return
+
+
+if __name__ == "__main__":
+ try:
+ main()
+ except Exception as e:
+ print(e)
diff --git a/libs/websocket/tests/echo-server.py b/libs/websocket/tests/echo-server.py
new file mode 100644
index 000000000..08d108ab2
--- /dev/null
+++ b/libs/websocket/tests/echo-server.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+# From https://github.com/aaugustin/websockets/blob/main/example/echo.py
+
+import asyncio
+import websockets
+import os
+
+LOCAL_WS_SERVER_PORT = os.environ.get('LOCAL_WS_SERVER_PORT', '8765')
+
+
+async def echo(websocket, path):
+ async for message in websocket:
+ await websocket.send(message)
+
+
+async def main():
+ async with websockets.serve(echo, "localhost", LOCAL_WS_SERVER_PORT):
+ await asyncio.Future() # run forever
+
+asyncio.run(main())
diff --git a/libs/websocket/tests/test_abnf.py b/libs/websocket/tests/test_abnf.py
index d629c5c2a..7f156dc94 100644
--- a/libs/websocket/tests/test_abnf.py
+++ b/libs/websocket/tests/test_abnf.py
@@ -1,32 +1,27 @@
# -*- coding: utf-8 -*-
#
"""
+test_abnf.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
-import os
import websocket as ws
from websocket._abnf import *
-import sys
import unittest
-sys.path[0:0] = [""]
class ABNFTest(unittest.TestCase):
@@ -59,7 +54,7 @@ class ABNFTest(unittest.TestCase):
def testMask(self):
abnf_none_data = ABNF(0,0,0,0, opcode=ABNF.OPCODE_PING, mask=1, data=None)
- bytes_val = bytes("aaaa", 'utf-8')
+ bytes_val = b"aaaa"
self.assertEqual(abnf_none_data._get_masked(bytes_val), bytes_val)
abnf_str_data = ABNF(0,0,0,0, opcode=ABNF.OPCODE_PING, mask=1, data="a")
self.assertEqual(abnf_str_data._get_masked(bytes_val), b'aaaa\x00')
diff --git a/libs/websocket/tests/test_app.py b/libs/websocket/tests/test_app.py
index 2678793ab..cd1146b3a 100644
--- a/libs/websocket/tests/test_app.py
+++ b/libs/websocket/tests/test_app.py
@@ -1,42 +1,41 @@
# -*- coding: utf-8 -*-
#
"""
+test_app.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
import os
import os.path
import websocket as ws
-import sys
import ssl
import unittest
-sys.path[0:0] = [""]
-# Skip test to access the internet.
+# Skip test to access the internet unless TEST_WITH_INTERNET == 1
TEST_WITH_INTERNET = os.environ.get('TEST_WITH_INTERNET', '0') == '1'
+# Skip tests relying on local websockets server unless LOCAL_WS_SERVER_PORT != -1
+LOCAL_WS_SERVER_PORT = os.environ.get('LOCAL_WS_SERVER_PORT', '-1')
+TEST_WITH_LOCAL_SERVER = LOCAL_WS_SERVER_PORT != '-1'
TRACEABLE = True
class WebSocketAppTest(unittest.TestCase):
- class NotSetYet(object):
+ class NotSetYet:
""" A marker class for signalling that a value hasn't been set yet.
"""
@@ -52,7 +51,7 @@ class WebSocketAppTest(unittest.TestCase):
WebSocketAppTest.keep_running_close = WebSocketAppTest.NotSetYet()
WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet()
- @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+ @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
def testKeepRunning(self):
""" A WebSocketApp should keep running as long as its self.keep_running
is not False (in the boolean context).
@@ -62,16 +61,20 @@ class WebSocketAppTest(unittest.TestCase):
""" Set the keep_running flag for later inspection and immediately
close the connection.
"""
+ self.send("hello!")
WebSocketAppTest.keep_running_open = self.keep_running
+ self.keep_running = False
+
+ def on_message(wsapp, message):
+ print(message)
self.close()
def on_close(self, *args, **kwargs):
""" Set the keep_running flag for the test to use.
"""
WebSocketAppTest.keep_running_close = self.keep_running
- self.send("connection should be closed here")
- app = ws.WebSocketApp('ws://echo.websocket.org/', on_open=on_open, on_close=on_close)
+ app = ws.WebSocketApp('ws://127.0.0.1:' + LOCAL_WS_SERVER_PORT, on_open=on_open, on_close=on_close, on_message=on_message)
app.run_forever()
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
diff --git a/libs/websocket/tests/test_cookiejar.py b/libs/websocket/tests/test_cookiejar.py
index 8d2dff501..5bf1fcaeb 100644
--- a/libs/websocket/tests/test_cookiejar.py
+++ b/libs/websocket/tests/test_cookiejar.py
@@ -3,27 +3,24 @@
"""
"""
+test_cookiejar.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
import unittest
-
from websocket._cookiejar import SimpleCookieJar
@@ -116,3 +113,7 @@ class CookieJarTest(unittest.TestCase):
self.assertEqual(cookie_jar.get("x.abc.com"), "a=b; c=d")
self.assertEqual(cookie_jar.get("abc.com.es"), "")
self.assertEqual(cookie_jar.get("xabc.com"), "")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/libs/websocket/tests/test_http.py b/libs/websocket/tests/test_http.py
index 2a059eac1..fda467d7b 100644
--- a/libs/websocket/tests/test_http.py
+++ b/libs/websocket/tests/test_http.py
@@ -1,43 +1,47 @@
# -*- coding: utf-8 -*-
#
"""
+test_http.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
import os
import os.path
import websocket as ws
-from websocket._http import proxy_info, read_headers, _open_proxied_socket, _tunnel, _get_addrinfo_list, connect
-import sys
+from websocket._http import proxy_info, read_headers, _start_proxied_socket, _tunnel, _get_addrinfo_list, connect
import unittest
import ssl
import websocket
-import socks
import socket
-sys.path[0:0] = [""]
-# Skip test to access the internet.
+try:
+ from python_socks._errors import ProxyError, ProxyTimeoutError, ProxyConnectionError
+except:
+ from websocket._http import ProxyError, ProxyTimeoutError, ProxyConnectionError
+
+# Skip test to access the internet unless TEST_WITH_INTERNET == 1
TEST_WITH_INTERNET = os.environ.get('TEST_WITH_INTERNET', '0') == '1'
+TEST_WITH_PROXY = os.environ.get('TEST_WITH_PROXY', '0') == '1'
+# Skip tests relying on local websockets server unless LOCAL_WS_SERVER_PORT != -1
+LOCAL_WS_SERVER_PORT = os.environ.get('LOCAL_WS_SERVER_PORT', '-1')
+TEST_WITH_LOCAL_SERVER = LOCAL_WS_SERVER_PORT != '-1'
-class SockMock(object):
+class SockMock:
def __init__(self):
self.data = []
self.sent = []
@@ -79,6 +83,7 @@ class OptsList():
def __init__(self):
self.timeout = 1
self.sockopt = []
+ self.sslopt = {"cert_reqs": ssl.CERT_NONE}
class HttpTest(unittest.TestCase):
@@ -96,15 +101,19 @@ class HttpTest(unittest.TestCase):
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testConnect(self):
- # Not currently testing an actual proxy connection, so just check whether TypeError is raised. This requires internet for a DNS lookup
- self.assertRaises(TypeError, _open_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host=None, http_proxy_port=None, proxy_type=None))
- self.assertRaises(TypeError, _open_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="http"))
- self.assertRaises(TypeError, _open_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks4"))
- self.assertRaises(TypeError, _open_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks5h"))
- self.assertRaises(TypeError, _get_addrinfo_list, None, 80, True, proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http"))
- self.assertRaises(TypeError, _get_addrinfo_list, None, 80, True, proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http"))
- self.assertRaises(socks.ProxyConnectionError, connect, "wss://example.com", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port=8080, proxy_type="socks4"), None)
- self.assertRaises(socket.timeout, connect, "wss://google.com", OptsList(), proxy_info(http_proxy_host="8.8.8.8", http_proxy_port=8080, proxy_type="http"), None)
+ # 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:
+ # Need this check, otherwise case where python_socks is not installed triggers
+ # websocket._exceptions.WebSocketException: Python Socks is needed for SOCKS proxying but is not available
+ self.assertRaises((ProxyTimeoutError, OSError), _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks4", timeout=1))
+ self.assertRaises((ProxyTimeoutError, OSError), _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks4a", timeout=1))
+ self.assertRaises((ProxyTimeoutError, OSError), _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks5", timeout=1))
+ self.assertRaises((ProxyTimeoutError, OSError), _start_proxied_socket, "wss://example.com", OptsList(), proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="socks5h", timeout=1))
+ self.assertRaises(ProxyConnectionError, connect, "wss://example.com", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port=9999, proxy_type="socks4", timeout=1), None)
+
+ self.assertRaises(TypeError, _get_addrinfo_list, None, 80, True, proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="9999", proxy_type="http"))
+ self.assertRaises(TypeError, _get_addrinfo_list, None, 80, True, proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="9999", proxy_type="http"))
+ self.assertRaises(socket.timeout, connect, "wss://google.com", OptsList(), proxy_info(http_proxy_host="8.8.8.8", http_proxy_port=9999, proxy_type="http", timeout=1), None)
self.assertEqual(
connect("wss://google.com", OptsList(), proxy_info(http_proxy_host="8.8.8.8", http_proxy_port=8080, proxy_type="http"), True),
(True, ("google.com", 443, "/")))
@@ -112,11 +121,26 @@ class HttpTest(unittest.TestCase):
# self.assertRaises(OverflowError, connect, "wss://example.com", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port=99999, proxy_type="socks4", timeout=2), False)
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+ @unittest.skipUnless(TEST_WITH_PROXY, "This test requires a HTTP proxy to be running on port 8899")
+ @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
+ def testProxyConnect(self):
+ ws = websocket.WebSocket()
+ ws.connect("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT, http_proxy_host="127.0.0.1", http_proxy_port="8899", proxy_type="http")
+ ws.send("Hello, Server")
+ server_response = ws.recv()
+ self.assertEqual(server_response, "Hello, Server")
+ # self.assertEqual(_start_proxied_socket("wss://api.bitfinex.com/ws/2", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8899", proxy_type="http"))[1], ("api.bitfinex.com", 443, '/ws/2'))
+ self.assertEqual(_get_addrinfo_list("api.bitfinex.com", 443, True, proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8899", proxy_type="http")),
+ (socket.getaddrinfo("127.0.0.1", 8899, 0, socket.SOCK_STREAM, socket.SOL_TCP), True, None))
+ self.assertEqual(connect("wss://api.bitfinex.com/ws/2", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port=8899, proxy_type="http"), None)[1], ("api.bitfinex.com", 443, '/ws/2'))
+ # TODO: Test SOCKS4 and SOCK5 proxies with unit tests
+
+ @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testSSLopt(self):
ssloptions = {
- "cert_reqs": ssl.CERT_NONE,
"check_hostname": False,
- "ssl_version": ssl.PROTOCOL_SSLv23,
+ "server_hostname": "ServerName",
+ "ssl_version": ssl.PROTOCOL_TLS_CLIENT,
"ciphers": "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:\
TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:\
ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:\
@@ -139,11 +163,13 @@ class HttpTest(unittest.TestCase):
ws_ssl2.close
def testProxyInfo(self):
- self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http").type, "http")
- self.assertRaises(ValueError, proxy_info, http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="badval")
- self.assertEqual(proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="http").host, "example.com")
- self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http").port, "8080")
+ self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http").proxy_protocol, "http")
+ self.assertRaises(ProxyError, proxy_info, http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="badval")
+ self.assertEqual(proxy_info(http_proxy_host="example.com", http_proxy_port="8080", proxy_type="http").proxy_host, "example.com")
+ self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http").proxy_port, "8080")
self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http").auth, None)
+ self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http", http_proxy_auth=("my_username123", "my_pass321")).auth[0], "my_username123")
+ self.assertEqual(proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http", http_proxy_auth=("my_username123", "my_pass321")).auth[1], "my_pass321")
if __name__ == "__main__":
diff --git a/libs/websocket/tests/test_url.py b/libs/websocket/tests/test_url.py
index a33e93437..ad3a3b1b3 100644
--- a/libs/websocket/tests/test_url.py
+++ b/libs/websocket/tests/test_url.py
@@ -1,30 +1,26 @@
# -*- coding: utf-8 -*-
#
"""
+test_url.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
-import sys
import os
import unittest
-sys.path[0:0] = [""]
from websocket._url import get_proxy_info, parse_url, _is_address_in_network, _is_no_proxy_host
@@ -277,6 +273,10 @@ class ProxyInfoTest(unittest.TestCase):
os.environ["https_proxy"] = "http://a:b@localhost2:3128/"
self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", 3128, ("a", "b")))
+ os.environ["http_proxy"] = "http://john%40example.com:P%40SSWORD@localhost:3128/"
+ os.environ["https_proxy"] = "http://john%40example.com:P%40SSWORD@localhost2:3128/"
+ self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", 3128, ("[email protected]", "P@SSWORD")))
+
os.environ["http_proxy"] = "http://a:b@localhost/"
os.environ["https_proxy"] = "http://a:b@localhost2/"
os.environ["no_proxy"] = "example1.com,example2.com"
diff --git a/libs/websocket/tests/test_websocket.py b/libs/websocket/tests/test_websocket.py
index 35a928cdf..8b34aa51f 100644
--- a/libs/websocket/tests/test_websocket.py
+++ b/libs/websocket/tests/test_websocket.py
@@ -5,28 +5,24 @@
"""
"""
+test_websocket.py
websocket - WebSocket client library for Python
-Copyright (C) 2010 Hiroki Ohtani(liris)
+Copyright 2021 engn33r
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
"""
-import sys
-sys.path[0:0] = [""]
import os
import os.path
import socket
@@ -47,8 +43,11 @@ except ImportError:
class SSLError(Exception):
pass
-# Skip test to access the internet.
+# Skip test to access the internet unless TEST_WITH_INTERNET == 1
TEST_WITH_INTERNET = os.environ.get('TEST_WITH_INTERNET', '0') == '1'
+# Skip tests relying on local websockets server unless LOCAL_WS_SERVER_PORT != -1
+LOCAL_WS_SERVER_PORT = os.environ.get('LOCAL_WS_SERVER_PORT', '-1')
+TEST_WITH_LOCAL_SERVER = LOCAL_WS_SERVER_PORT != '-1'
TRACEABLE = True
@@ -56,7 +55,7 @@ def create_mask_key(_):
return "abcd"
-class SockMock(object):
+class SockMock:
def __init__(self):
self.data = []
self.sent = []
@@ -287,7 +286,7 @@ class WebSocketTest(unittest.TestCase):
def testClose(self):
sock = ws.WebSocket()
sock.connected = True
- self.assertRaises(ws._exceptions.WebSocketConnectionClosedException, sock.close)
+ sock.close
sock = ws.WebSocket()
s = sock.sock = SockMock()
@@ -337,12 +336,13 @@ class WebSocketTest(unittest.TestCase):
s.sent[0],
b'\x8a\x90abcd1\x0e\x06\x05\x12\x07C4.,$D\x15\n\n\x17')
- @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+ @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
def testWebSocket(self):
- s = ws.create_connection("ws://echo.websocket.org/")
+ s = ws.create_connection("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT)
self.assertNotEqual(s, None)
s.send("Hello, World")
- result = s.recv()
+ result = s.next()
+ s.fileno()
self.assertEqual(result, "Hello, World")
s.send("こにゃにゃちは、世界")
@@ -351,15 +351,21 @@ class WebSocketTest(unittest.TestCase):
self.assertRaises(ValueError, s.send_close, -1, "")
s.close()
- @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+ @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
def testPingPong(self):
- s = ws.create_connection("ws://echo.websocket.org/")
+ s = ws.create_connection("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT)
self.assertNotEqual(s, None)
s.ping("Hello")
s.pong("Hi")
s.close()
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+ def testSupportRedirect(self):
+ s = ws.WebSocket()
+ self.assertRaises(ws._exceptions.WebSocketBadStatusException, s.connect, "ws://google.com/")
+ # Need to find a URL that has a redirect code leading to a websocket
+
+ @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testSecureWebSocket(self):
import ssl
s = ws.create_connection("wss://api.bitfinex.com/ws/2")
@@ -372,9 +378,9 @@ class WebSocketTest(unittest.TestCase):
self.assertEqual(s.getsubprotocol(), None)
s.abort()
- @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+ @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
def testWebSocketWithCustomHeader(self):
- s = ws.create_connection("ws://echo.websocket.org/",
+ s = ws.create_connection("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT,
headers={"User-Agent": "PythonWebsocketClient"})
self.assertNotEqual(s, None)
s.send("Hello, World")
@@ -383,9 +389,9 @@ class WebSocketTest(unittest.TestCase):
self.assertRaises(ValueError, s.close, -1, "")
s.close()
- @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+ @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
def testAfterClose(self):
- s = ws.create_connection("ws://echo.websocket.org/")
+ s = ws.create_connection("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT)
self.assertNotEqual(s, None)
s.close()
self.assertRaises(ws.WebSocketConnectionClosedException, s.send, "Hello")
@@ -393,10 +399,10 @@ class WebSocketTest(unittest.TestCase):
class SockOptTest(unittest.TestCase):
- @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
+ @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled")
def testSockOpt(self):
sockopt = ((socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),)
- s = ws.create_connection("ws://echo.websocket.org", sockopt=sockopt)
+ s = ws.create_connection("ws://127.0.0.1:" + LOCAL_WS_SERVER_PORT, sockopt=sockopt)
self.assertNotEqual(s.sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY), 0)
s.close()
@@ -414,7 +420,7 @@ class UtilsTest(unittest.TestCase):
class HandshakeTest(unittest.TestCase):
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def test_http_SSL(self):
- websock1 = ws.WebSocket(sslopt={"cert_chain": ssl.get_default_verify_paths().capath})
+ websock1 = ws.WebSocket(sslopt={"cert_chain": ssl.get_default_verify_paths().capath}, enable_multithread=False)
self.assertRaises(ValueError,
websock1.connect, "wss://api.bitfinex.com/ws/2")
websock2 = ws.WebSocket(sslopt={"certfile": "myNonexistentCertFile"})
@@ -423,9 +429,8 @@ class HandshakeTest(unittest.TestCase):
@unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled")
def testManualHeaders(self):
- websock3 = ws.WebSocket(sslopt={"cert_reqs": ssl.CERT_NONE,
- "ca_certs": ssl.get_default_verify_paths().capath,
- "ca_cert_path": ssl.get_default_verify_paths().openssl_cafile})
+ websock3 = ws.WebSocket(sslopt={"ca_certs": ssl.get_default_verify_paths().cafile,
+ "ca_cert_path": ssl.get_default_verify_paths().capath})
self.assertRaises(ws._exceptions.WebSocketBadStatusException,
websock3.connect, "wss://api.bitfinex.com/ws/2", cookie="chocolate",
origin="testing_websockets.com",