diff --git a/libs/version.txt b/libs/version.txt index 18a4d3f02..e43c62fb5 100644 --- a/libs/version.txt +++ b/libs/version.txt @@ -40,7 +40,7 @@ subliminal=2.1.0dev tzlocal=2.1b1 twine=3.4.1 urllib3=1.23 -websocket-client=0.54.0 +websocket-client=0.59.0 <-- Modified to work with SignalRCore: https://github.com/websocket-client/websocket-client/commit/3112b7d75b1e5d65cb8fdfca7801606649044ed1#commitcomment-50947250 ## indirect dependencies auditok=0.1.5 # Required-by: ffsubsync diff --git a/libs/websocket/__init__.py b/libs/websocket/__init__.py index b7c6fba73..f2c7b44c1 100644 --- a/libs/websocket/__init__.py +++ b/libs/websocket/__init__.py @@ -15,8 +15,7 @@ Copyright (C) 2010 Hiroki Ohtani(liris) 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-1335 USA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ from ._abnf import * @@ -26,4 +25,4 @@ from ._exceptions import * from ._logging import * from ._socket import * -__version__ = "0.54.0" +__version__ = "0.59.0" diff --git a/libs/websocket/_abnf.py b/libs/websocket/_abnf.py index a0000fa1c..80fbe1f9b 100644 --- a/libs/websocket/_abnf.py +++ b/libs/websocket/_abnf.py @@ -1,3 +1,7 @@ +""" + +""" + """ websocket - WebSocket client library for Python @@ -15,8 +19,7 @@ Copyright (C) 2010 Hiroki Ohtani(liris) 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-1335 USA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ import array @@ -105,7 +108,7 @@ VALID_CLOSE_STATUS = ( class ABNF(object): """ ABNF frame class. - see http://tools.ietf.org/html/rfc5234 + See http://tools.ietf.org/html/rfc5234 and http://tools.ietf.org/html/rfc6455#section-5.2 """ @@ -139,8 +142,7 @@ class ABNF(object): def __init__(self, fin=0, rsv1=0, rsv2=0, rsv3=0, opcode=OPCODE_TEXT, mask=1, data=""): """ - Constructor for ABNF. - please check RFC for arguments. + Constructor for ABNF. Please check RFC for arguments. """ self.fin = fin self.rsv1 = rsv1 @@ -155,7 +157,10 @@ class ABNF(object): def validate(self, skip_utf8_validation=False): """ - validate the ABNF frame. + Validate the ABNF frame. + + Parameters + ---------- skip_utf8_validation: skip utf8 validation. """ if self.rsv1 or self.rsv2 or self.rsv3: @@ -193,15 +198,18 @@ class ABNF(object): @staticmethod def create_frame(data, opcode, fin=1): """ - create frame to send text, binary and other data. + Create frame to send text, binary and other data. - data: data to send. This is string value(byte array). - if opcode is OPCODE_TEXT and this value is unicode, + Parameters + ---------- + data: + data to send. This is string value(byte array). + If opcode is OPCODE_TEXT and this value is unicode, data value is converted into unicode string, automatically. - - opcode: operation code. please see OPCODE_XXX. - - fin: fin flag. if set to 0, create continue fragmentation. + opcode: + operation code. please see OPCODE_XXX. + fin: + fin flag. if set to 0, create continue fragmentation. """ if opcode == ABNF.OPCODE_TEXT and isinstance(data, six.text_type): data = data.encode("utf-8") @@ -210,7 +218,7 @@ class ABNF(object): def format(self): """ - format this object to string(byte array) to send data to server. + Format this object to string(byte array) to send data to server. """ if any(x not in (0, 1) for x in [self.fin, self.rsv1, self.rsv2, self.rsv3]): raise ValueError("not 0 or 1") @@ -220,9 +228,9 @@ class ABNF(object): if length >= ABNF.LENGTH_63: raise ValueError("data is too long") - frame_header = chr(self.fin << 7 - | self.rsv1 << 6 | self.rsv2 << 5 | self.rsv3 << 4 - | self.opcode) + frame_header = chr(self.fin << 7 | + self.rsv1 << 6 | self.rsv2 << 5 | self.rsv3 << 4 | + self.opcode) if length < ABNF.LENGTH_7: frame_header += chr(self.mask << 7 | length) frame_header = six.b(frame_header) @@ -252,11 +260,14 @@ class ABNF(object): @staticmethod def mask(mask_key, data): """ - mask or unmask data. Just do xor for each byte - - mask_key: 4 byte string(byte). - - data: data to mask/unmask. + Mask or unmask data. Just do xor for each byte + + Parameters + ---------- + mask_key: + 4 byte string(byte). + data: + data to mask/unmask. """ if data is None: data = "" @@ -276,7 +287,7 @@ class ABNF(object): 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()[:origlen] return masked.tobytes() else: _m = array.array("B", mask_key) diff --git a/libs/websocket/_app.py b/libs/websocket/_app.py index 81aa1fcd9..d65ec7fb2 100644 --- a/libs/websocket/_app.py +++ b/libs/websocket/_app.py @@ -1,3 +1,7 @@ +""" + +""" + """ websocket - WebSocket client library for Python @@ -15,13 +19,8 @@ Copyright (C) 2010 Hiroki Ohtani(liris) 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-1335 USA - -""" + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -""" -WebSocketApp provides higher level APIs. """ import inspect import select @@ -40,27 +39,35 @@ from . import _logging __all__ = ["WebSocketApp"] + class Dispatcher: + """ + Dispatcher + """ def __init__(self, app, ping_timeout): - self.app = app + self.app = app self.ping_timeout = ping_timeout def read(self, sock, read_callback, check_callback): - while self.app.sock.connected: + while self.app.keep_running: r, w, e = select.select( - (self.app.sock.sock, ), (), (), self.ping_timeout) + (self.app.sock.sock, ), (), (), self.ping_timeout) if r: if not read_callback(): break check_callback() -class SSLDispacther: + +class SSLDispatcher: + """ + SSLDispatcher + """ def __init__(self, app, ping_timeout): - self.app = app + self.app = app self.ping_timeout = ping_timeout def read(self, sock, read_callback, check_callback): - while self.app.sock.connected: + while self.app.keep_running: r = self.select() if r: if not read_callback(): @@ -75,10 +82,10 @@ class SSLDispacther: r, w, e = select.select((sock, ), (), (), self.ping_timeout) return r + class WebSocketApp(object): """ - Higher level of APIs are provided. - The interface is like JavaScript WebSocket object. + Higher level of APIs are provided. The interface is like JavaScript WebSocket object. """ def __init__(self, url, header=None, @@ -89,39 +96,56 @@ class WebSocketApp(object): subprotocols=None, on_data=None): """ - url: websocket url. - header: custom header for websocket handshake. - on_open: callable object which is called at opening websocket. - this function has one argument. The argument is this class object. - on_message: callable 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: callable 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: callable object which is called when closed the connection. - this function has one argument. The argument is this class object. - on_cont_message: callback object which is called when receive continued - frame data. - 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: 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: this parameter is obsolete and ignored. - get_mask_key: a callable to produce new mask keys, - see the WebSocket.set_mask_key's docstring for more information - subprotocols: array of available sub protocols. default is None. + WebSocketApp initialization + + Parameters + ---------- + url: + websocket url. + header: list or dict + custom header for websocket handshake. + on_open: + callable object which is called at opening websocket. + this function has one argument. The argument is this class object. + on_message: + callable 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: + callable 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: + callable object which is called when closed the connection. + this function has one argument. The argument is this class object. + on_cont_message: + callback object which is called when receive continued + frame data. + 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: + 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: + 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 + cookie: str + cookie value. + subprotocols: + array of available sub protocols. default is None. """ self.url = url self.header = header if header is not None else [] @@ -144,10 +168,15 @@ class WebSocketApp(object): def send(self, data, opcode=ABNF.OPCODE_TEXT): """ - send message. - data: message to send. If you set opcode to OPCODE_TEXT, - data must be utf-8 string or unicode. - opcode: operation code of data. default is OPCODE_TEXT. + send message + + Parameters + ---------- + data: + Message to send. If you set opcode to OPCODE_TEXT, + data must be utf-8 string or unicode. + opcode: + Operation code of data. default is OPCODE_TEXT. """ if not self.sock or self.sock.send(data, opcode) == 0: @@ -156,54 +185,73 @@ class WebSocketApp(object): def close(self, **kwargs): """ - close websocket connection. + Close websocket connection. """ self.keep_running = False if self.sock: self.sock.close(**kwargs) self.sock = None - def _send_ping(self, interval, event): + def _send_ping(self, interval, event, payload): while not event.wait(interval): self.last_ping_tm = time.time() if self.sock: try: - self.sock.ping() + self.sock.ping(payload) except Exception as ex: _logging.warning("send_ping routine terminated: {}".format(ex)) break def run_forever(self, sockopt=None, sslopt=None, ping_interval=0, ping_timeout=None, + ping_payload="", http_proxy_host=None, http_proxy_port=None, http_no_proxy=None, http_proxy_auth=None, skip_utf8_validation=False, host=None, origin=None, dispatcher=None, - suppress_origin = False, proxy_type=None): + suppress_origin=False, proxy_type=None): """ - run event loop for WebSocket framework. - This loop is infinite loop and is alive during websocket is available. - sockopt: values for socket.setsockopt. + Run event loop for WebSocket framework. + + This loop is an infinite loop and is alive while websocket is available. + + Parameters + ---------- + sockopt: tuple + values for socket.setsockopt. sockopt must be tuple and each element is argument of sock.setsockopt. - sslopt: ssl socket optional dict. - ping_interval: automatically send "ping" command - every specified period(second) + sslopt: dict + 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. - ping_timeout: timeout(second) if the pong message is not received. - http_proxy_host: http proxy host name. - http_proxy_port: http proxy port. If not set, set to 80. - http_no_proxy: host names, which doesn't use proxy. - skip_utf8_validation: skip utf8 validation. - host: update host header. - origin: update origin header. - dispatcher: customize reading data from socket. - suppress_origin: suppress outputting origin header. + ping_timeout: int or float + timeout (in seconds) if the pong message is not received. + ping_payload: str + payload message to send with each ping. + http_proxy_host: + http proxy host name. + http_proxy_port: + http proxy port. If not set, set to 80. + http_no_proxy: + host names, which doesn't use proxy. + skip_utf8_validation: bool + skip utf8 validation. + host: str + update host header. + origin: str + update origin header. + dispatcher: + customize reading data from socket. + suppress_origin: bool + suppress outputting origin header. Returns ------- - False if caught KeyboardInterrupt - True if other exception was raised during a loop + teardown: bool + False if caught KeyboardInterrupt, True if other exception was raised during a loop """ if ping_timeout is not None and ping_timeout <= 0: @@ -224,10 +272,11 @@ class WebSocketApp(object): def teardown(close_frame=None): """ Tears down the connection. + If close_frame is set, we will invoke the on_close handler with the statusCode and reason from there. """ - if thread and thread.isAlive(): + if thread and thread.is_alive(): event.set() thread.join() self.keep_running = False @@ -260,8 +309,8 @@ class WebSocketApp(object): if ping_interval: event = threading.Event() thread = threading.Thread( - target=self._send_ping, args=(ping_interval, event)) - thread.setDaemon(True) + target=self._send_ping, args=(ping_interval, event, ping_payload)) + thread.daemon = True thread.start() def read(): @@ -296,9 +345,9 @@ class WebSocketApp(object): has_pong_not_arrived_after_last_ping = self.last_pong_tm - self.last_ping_tm < 0 has_pong_arrived_too_late = self.last_pong_tm - self.last_ping_tm > ping_timeout - if (self.last_ping_tm - and has_timeout_expired - and (has_pong_not_arrived_after_last_ping or has_pong_arrived_too_late)): + if (self.last_ping_tm and + has_timeout_expired and + (has_pong_not_arrived_after_last_ping or has_pong_arrived_too_late)): raise WebSocketTimeoutException("ping/pong timed out") return True @@ -314,13 +363,15 @@ class WebSocketApp(object): def create_dispatcher(self, ping_timeout): timeout = ping_timeout or 10 if self.sock.is_ssl(): - return SSLDispacther(self, timeout) + return SSLDispatcher(self, timeout) return Dispatcher(self, timeout) def _get_close_args(self, data): - """ this functions extracts the code, reason from the close body - if they exists, and if the self.on_close except three arguments """ + """ + _get_close_args extracts the code, reason from the close body + if they exists, and if the self.on_close except three arguments + """ # if the on_close callback is "old", just return empty list if sys.version_info < (3, 0): if not self.on_close or len(inspect.getargspec(self.on_close).args) != 3: diff --git a/libs/websocket/_cookiejar.py b/libs/websocket/_cookiejar.py index 3efeb0fd2..bc2891a65 100644 --- a/libs/websocket/_cookiejar.py +++ b/libs/websocket/_cookiejar.py @@ -1,3 +1,27 @@ +""" + +""" + +""" +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. + + 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 + +""" try: import Cookie except: @@ -48,5 +72,7 @@ class SimpleCookieJar(object): if host.endswith(domain) or host == domain[1:]: cookies.append(self.jar.get(domain)) - return "; ".join(filter(None, ["%s=%s" % (k, v.value) for cookie in filter(None, sorted(cookies)) for k, v in - sorted(cookie.items())])) + return "; ".join(filter( + None, sorted( + ["%s=%s" % (k, v.value) for cookie in filter(None, cookies) for k, v in cookie.items()] + ))) diff --git a/libs/websocket/_core.py b/libs/websocket/_core.py index c91ad63e1..1ff80f05d 100644 --- a/libs/websocket/_core.py +++ b/libs/websocket/_core.py @@ -1,3 +1,10 @@ +from __future__ import print_function +""" +_core.py +==================================== +WebSocket Python client +""" + """ websocket - WebSocket client library for Python @@ -15,12 +22,9 @@ Copyright (C) 2010 Hiroki Ohtani(liris) 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-1335 USA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ -from __future__ import print_function - import socket import struct import threading @@ -40,21 +44,12 @@ from ._utils import * __all__ = ['WebSocket', 'create_connection'] -""" -websocket python client. -========================= - -This version support only hybi-13. -Please see http://tools.ietf.org/html/rfc6455 for protocol. -""" - class WebSocket(object): """ Low level WebSocket interface. - This class is based on - The WebSocket protocol draft-hixie-thewebsocketprotocol-76 - http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76 + + This class is based on the WebSocket protocol `draft-hixie-thewebsocketprotocol-76 `_ We can connect to the websocket server and send/receive data. The following example is an echo client. @@ -67,14 +62,22 @@ class WebSocket(object): 'Hello, Server' >>> ws.close() - get_mask_key: a callable to produce new mask keys, see the set_mask_key - function's docstring for more details - sockopt: values for socket.setsockopt. + Parameters + ---------- + get_mask_key: func + a callable to produce new mask keys, see the set_mask_key + function's docstring for more details + sockopt: tuple + values for socket.setsockopt. sockopt must be tuple and each element is argument of sock.setsockopt. - sslopt: dict object for ssl socket option. - fire_cont_frame: fire recv event for each cont frame. default is False - enable_multithread: if set to True, lock send method. - skip_utf8_validation: skip utf8 validation. + sslopt: dict + optional dict object for ssl socket option. + fire_cont_frame: bool + fire recv event for each cont frame. default is False + enable_multithread: bool + if set to True, lock send method. + skip_utf8_validation: bool + skip utf8 validation. """ def __init__(self, get_mask_key=None, sockopt=None, sslopt=None, @@ -82,6 +85,10 @@ class WebSocket(object): skip_utf8_validation=False, **_): """ Initialize WebSocket object. + + Parameters + ---------- + sslopt: specify ssl certification verification options """ self.sock_opt = sock_opt(sockopt, sslopt) self.handshake_response = None @@ -119,19 +126,27 @@ class WebSocket(object): def set_mask_key(self, func): """ - set function to create musk key. You can customize mask key generator. + Set function to create mask key. You can customize mask key generator. Mainly, this is for testing purpose. - func: callable object. the func takes 1 argument as integer. - The argument means length of mask key. - This func must return string(byte array), - which length is argument specified. + Parameters + ---------- + func: func + callable object. the func takes 1 argument as integer. + The argument means length of mask key. + This func must return string(byte array), + which length is argument specified. """ self.get_mask_key = func def gettimeout(self): """ - Get the websocket timeout(second). + Get the websocket timeout (in seconds) as an int or float + + Returns + ---------- + timeout: int or float + returns timeout value (in seconds). This value could be either float/integer. """ return self.sock_opt.timeout @@ -139,7 +154,10 @@ class WebSocket(object): """ Set the timeout to the websocket. - timeout: timeout time(second). + Parameters + ---------- + timeout: int or float + timeout time (in seconds). This value could be either float/integer. """ self.sock_opt.timeout = timeout if self.sock: @@ -149,7 +167,7 @@ class WebSocket(object): def getsubprotocol(self): """ - get subprotocol + Get subprotocol """ if self.handshake_response: return self.handshake_response.subprotocol @@ -160,7 +178,7 @@ class WebSocket(object): def getstatus(self): """ - get handshake status + Get handshake status """ if self.handshake_response: return self.handshake_response.status @@ -171,7 +189,7 @@ class WebSocket(object): def getheaders(self): """ - get handshake response header + Get handshake response header """ if self.handshake_response: return self.handshake_response.headers @@ -195,27 +213,39 @@ class WebSocket(object): ... header=["User-Agent: MyProgram", ... "x-custom: header"]) - timeout: socket timeout time. This value is integer. - if you set None for this value, - it means "use default_timeout value" - - options: "header" -> custom http header list or dict. - "cookie" -> cookie value. - "origin" -> custom origin url. - "suppress_origin" -> suppress outputting origin header. - "host" -> custom host header string. - "http_proxy_host" - http proxy host name. - "http_proxy_port" - http proxy port. If not set, set to 80. - "http_no_proxy" - host names, which doesn't use proxy. - "http_proxy_auth" - http proxy auth information. - tuple of username and password. - default is None - "redirect_limit" -> number of redirects to follow. - "subprotocols" - array of available sub protocols. - default is None. - "socket" - pre-initialized stream socket. - - """ + timeout: + 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. + - suppress_origin: bool + suppress outputting origin header. + - host: str + custom host header string. + - http_proxy_host: + http proxy host name. + - http_proxy_port: + http proxy port. If not set, set to 80. + - http_no_proxy: + host names, which doesn't use proxy. + - http_proxy_auth: + http proxy auth information. tuple of username and password. default is None + - redirect_limit: + number of redirects to follow. + - subprotocols: + array of available sub protocols. default is None. + - 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), options.pop('socket', None)) @@ -225,8 +255,8 @@ class WebSocket(object): if self.handshake_response.status in SUPPORTED_REDIRECT_STATUSES: url = self.handshake_response.headers['location'] self.sock.close() - self.sock, addrs = connect(url, self.sock_opt, proxy_info(**options), - options.pop('socket', None)) + self.sock, addrs = connect(url, self.sock_opt, proxy_info(**options), + options.pop('socket', None)) self.handshake_response = handshake(self.sock, *addrs, **options) self.connected = True except: @@ -239,11 +269,14 @@ class WebSocket(object): """ Send the data as string. - payload: Payload must be utf-8 string or unicode, + Parameters + ---------- + payload: + Payload must be utf-8 string or unicode, if the opcode is OPCODE_TEXT. Otherwise, it must be string(byte array) - - opcode: operation code to send. Please see OPCODE_XXX. + opcode: + operation code to send. Please see OPCODE_XXX. """ frame = ABNF.create_frame(payload, opcode) @@ -253,8 +286,6 @@ class WebSocket(object): """ Send the data frame. - frame: frame data created by ABNF.create_frame - >>> ws = create_connection("ws://echo.websocket.org/") >>> frame = ABNF.create_frame("Hello", ABNF.OPCODE_TEXT) >>> ws.send_frame(frame) @@ -263,12 +294,17 @@ class WebSocket(object): >>> cont_frame = ABNF.create_frame("Foo Bar", ABNF.OPCODE_CONT, 1) >>> ws.send_frame(frame) + Parameters + ---------- + frame: + frame data created by ABNF.create_frame """ if self.get_mask_key: frame.get_mask_key = self.get_mask_key data = frame.format() length = len(data) - trace("send: " + repr(data)) + if (isEnabledForTrace()): + trace("send: " + repr(data)) with self.lock: while data: @@ -282,19 +318,25 @@ class WebSocket(object): def ping(self, payload=""): """ - send ping data. + Send ping data. - payload: data payload to send server. + Parameters + ---------- + payload: + data payload to send server. """ if isinstance(payload, six.text_type): payload = payload.encode("utf-8") self.send(payload, ABNF.OPCODE_PING) - def pong(self, payload): + def pong(self, payload=""): """ - send pong data. + Send pong data. - payload: data payload to send server. + Parameters + ---------- + payload: + data payload to send server. """ if isinstance(payload, six.text_type): payload = payload.encode("utf-8") @@ -304,7 +346,9 @@ class WebSocket(object): """ Receive string data(byte array) from the server. - return value: string(byte array) value. + Returns + ---------- + data: string (byte array) value. """ with self.readlock: opcode, data = self.recv_data() @@ -319,10 +363,16 @@ class WebSocket(object): """ Receive data with operation code. - control_frame: a boolean flag indicating whether to return control frame - data, defaults to False + Parameters + ---------- + control_frame: bool + a boolean flag indicating whether to return control frame + data, defaults to False - return value: tuple of operation code and string(byte array) value. + Returns + ------- + opcode, frame.data: tuple + tuple of operation code and string(byte array) value. """ opcode, frame = self.recv_data_frame(control_frame) return opcode, frame.data @@ -331,10 +381,16 @@ class WebSocket(object): """ Receive data with operation code. - control_frame: a boolean flag indicating whether to return control frame - data, defaults to False + Parameters + ---------- + control_frame: bool + a boolean flag indicating whether to return control frame + data, defaults to False - return value: tuple of operation code and string(byte array) value. + Returns + ------- + frame.opcode, frame: tuple + tuple of operation code and string(byte array) value. """ while True: frame = self.recv_frame() @@ -367,19 +423,24 @@ class WebSocket(object): def recv_frame(self): """ - receive data as frame from server. + Receive data as frame from server. - return value: ABNF frame object. + Returns + ------- + self.frame_buffer.recv_frame(): ABNF frame object """ return self.frame_buffer.recv_frame() def send_close(self, status=STATUS_NORMAL, reason=six.b("")): """ - send close data to the server. - - status: status code to send. see STATUS_XXX. + Send close data to the server. - reason: the reason to close. This must be string or bytes. + Parameters + ---------- + status: + status code to send. see STATUS_XXX. + reason: str or bytes + the reason to close. This must be string or bytes. """ if status < 0 or status >= ABNF.LENGTH_16: raise ValueError("code is invalid range") @@ -390,11 +451,14 @@ class WebSocket(object): """ Close Websocket object - status: status code to send. see STATUS_XXX. - - reason: the reason to close. This must be string. - - timeout: timeout until receive a close frame. + Parameters + ---------- + status: + status code to send. see STATUS_XXX. + reason: + the reason to close. This must be string. + timeout: int or float + timeout until receive a close frame. If None, it will wait forever until receive a close frame. """ if self.connected: @@ -403,8 +467,7 @@ class WebSocket(object): try: self.connected = False - self.send(struct.pack('!H', status) + - reason, ABNF.OPCODE_CLOSE) + self.send(struct.pack('!H', status) + reason, ABNF.OPCODE_CLOSE) sock_timeout = self.sock.gettimeout() self.sock.settimeout(timeout) start_time = time.time() @@ -415,7 +478,9 @@ class WebSocket(object): continue if isEnabledForError(): recv_status = struct.unpack("!H", frame.data[0:2])[0] - if recv_status != STATUS_NORMAL: + if recv_status >= 3000 and recv_status <= 4999: + debug("close status: " + repr(recv_status)) + elif recv_status != STATUS_NORMAL: error("close status: " + repr(recv_status)) break except: @@ -425,7 +490,7 @@ class WebSocket(object): except: pass - self.shutdown() + self.shutdown() def abort(self): """ @@ -435,7 +500,9 @@ class WebSocket(object): self.sock.shutdown(socket.SHUT_RDWR) def shutdown(self): - """close socket, immediately.""" + """ + close socket, immediately. + """ if self.sock: self.sock.close() self.sock = None @@ -457,12 +524,12 @@ class WebSocket(object): def create_connection(url, timeout=None, class_=WebSocket, **options): """ - connect to url and return websocket object. + Connect to url and return websocket object. Connect to url and return the WebSocket object. Passing optional timeout parameter will set the timeout on the socket. If no timeout is supplied, - the global default timeout setting returned by getdefauttimeout() is used. + the global default timeout setting returned by getdefaulttimeout() is used. You can customize using 'options'. If you set "header" list object, you can set your own custom header. @@ -470,33 +537,49 @@ def create_connection(url, timeout=None, class_=WebSocket, **options): ... header=["User-Agent: MyProgram", ... "x-custom: header"]) - - timeout: socket timeout time. This value is integer. + Parameters + ---------- + 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_: class to instantiate when creating the connection. It has to implement + 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. - options: "header" -> custom http header list or dict. - "cookie" -> cookie value. - "origin" -> custom origin url. - "suppress_origin" -> suppress outputting origin header. - "host" -> custom host header string. - "http_proxy_host" - http proxy host name. - "http_proxy_port" - http proxy port. If not set, set to 80. - "http_no_proxy" - host names, which doesn't use proxy. - "http_proxy_auth" - http proxy auth information. - tuple of username and password. - default is None - "enable_multithread" -> enable lock for multithread. - "redirect_limit" -> number of redirects to follow. - "sockopt" -> socket options - "sslopt" -> ssl option - "subprotocols" - array of available sub protocols. - default is None. - "skip_utf8_validation" - skip utf8 validation. - "socket" - pre-initialized stream socket. + options: + - 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: + custom host header string. + - http_proxy_host: + http proxy host name. + - http_proxy_port: + http proxy port. If not set, set to 80. + - http_no_proxy: + host names, which doesn't use proxy. + - http_proxy_auth: + http proxy auth information. tuple of username and password. default is None + - enable_multithread: bool + enable lock for multithread. + - redirect_limit: + number of redirects to follow. + - sockopt: + socket options + - sslopt: + ssl option + - subprotocols: + array of available sub protocols. default is None. + - skip_utf8_validation: bool + skip utf8 validation. + - socket: + pre-initialized stream socket. """ sockopt = options.pop("sockopt", []) sslopt = options.pop("sslopt", {}) diff --git a/libs/websocket/_exceptions.py b/libs/websocket/_exceptions.py index ee0888083..83c6e42b7 100644 --- a/libs/websocket/_exceptions.py +++ b/libs/websocket/_exceptions.py @@ -1,3 +1,7 @@ +""" +Define WebSocket exceptions +""" + """ websocket - WebSocket client library for Python @@ -15,34 +19,28 @@ Copyright (C) 2010 Hiroki Ohtani(liris) 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-1335 USA - -""" - + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -""" -define websocket exceptions """ class WebSocketException(Exception): """ - websocket exception class. + WebSocket exception class. """ pass class WebSocketProtocolException(WebSocketException): """ - If the websocket protocol is invalid, this exception will be raised. + If the WebSocket protocol is invalid, this exception will be raised. """ pass class WebSocketPayloadException(WebSocketException): """ - If the websocket payload is invalid, this exception will be raised. + If the WebSocket payload is invalid, this exception will be raised. """ pass @@ -74,10 +72,12 @@ class WebSocketBadStatusException(WebSocketException): WebSocketBadStatusException will be raised when we get bad handshake status code. """ - def __init__(self, message, status_code, status_message=None): + def __init__(self, message, status_code, status_message=None, resp_headers=None): msg = message % (status_code, status_message) super(WebSocketBadStatusException, self).__init__(msg) self.status_code = status_code + self.resp_headers = resp_headers + class WebSocketAddressException(WebSocketException): """ diff --git a/libs/websocket/_handshake.py b/libs/websocket/_handshake.py index 3b8ddc060..c4d9d169d 100644 --- a/libs/websocket/_handshake.py +++ b/libs/websocket/_handshake.py @@ -15,8 +15,7 @@ Copyright (C) 2010 Hiroki Ohtani(liris) 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-1335 USA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ import hashlib @@ -31,13 +30,13 @@ from ._http import * from ._logging import * from ._socket import * -if six.PY3: +if hasattr(six, 'PY3') and six.PY3: from base64 import encodebytes as base64encode else: from base64 import encodestring as base64encode -if six.PY3: - if six.PY34: +if hasattr(six, 'PY3') and six.PY3: + if hasattr(six, 'PY34') and six.PY34: from http import client as HTTPStatus else: from http import HTTPStatus @@ -55,7 +54,8 @@ else: # websocket supported version. VERSION = 13 -SUPPORTED_REDIRECT_STATUSES = [HTTPStatus.MOVED_PERMANENTLY, HTTPStatus.FOUND, HTTPStatus.SEE_OTHER] +SUPPORTED_REDIRECT_STATUSES = (HTTPStatus.MOVED_PERMANENTLY, HTTPStatus.FOUND, HTTPStatus.SEE_OTHER,) +SUCCESS_STATUSES = SUPPORTED_REDIRECT_STATUSES + (HTTPStatus.SWITCHING_PROTOCOLS,) CookieJar = SimpleCookieJar() @@ -85,6 +85,7 @@ def handshake(sock, hostname, port, resource, **options): return handshake_response(status, resp, subproto) + def _pack_hostname(hostname): # IPv6 address if ':' in hostname: @@ -92,17 +93,16 @@ def _pack_hostname(hostname): return hostname + def _get_handshake_headers(resource, host, port, options): headers = [ "GET %s HTTP/1.1" % resource, - "Upgrade: websocket", - "Connection: Upgrade" + "Upgrade: websocket" ] if port == 80 or port == 443: hostport = _pack_hostname(host) else: hostport = "%s:%d" % (_pack_hostname(host), port) - if "host" in options and options["host"] is not None: headers.append("Host: %s" % options["host"]) else: @@ -115,8 +115,21 @@ def _get_handshake_headers(resource, host, port, options): headers.append("Origin: http://%s" % hostport) key = _create_sec_websocket_key() - headers.append("Sec-WebSocket-Key: %s" % key) - headers.append("Sec-WebSocket-Version: %s" % VERSION) + + # Append Sec-WebSocket-Key & Sec-WebSocket-Version if not manually specified + if 'header' not in options or 'Sec-WebSocket-Key' not in options['header']: + key = _create_sec_websocket_key() + headers.append("Sec-WebSocket-Key: %s" % key) + else: + key = options['header']['Sec-WebSocket-Key'] + + if 'header' not in options or 'Sec-WebSocket-Version' not in options['header']: + headers.append("Sec-WebSocket-Version: %s" % VERSION) + + if 'connection' not in options or options['connection'] is None: + headers.append('Connection: Upgrade') + else: + headers.append(options['connection']) subprotocols = options.get("subprotocols") if subprotocols: @@ -146,12 +159,13 @@ def _get_handshake_headers(resource, host, port, options): return headers, key -def _get_resp_headers(sock, success_statuses=(101, 301, 302, 303)): +def _get_resp_headers(sock, success_statuses=SUCCESS_STATUSES): status, resp_headers, status_message = read_headers(sock) if status not in success_statuses: - raise WebSocketBadStatusException("Handshake status %d %s", status, status_message) + raise WebSocketBadStatusException("Handshake status %d %s", status, status_message, resp_headers) return status, resp_headers + _HEADERS_TO_CHECK = { "upgrade": "websocket", "connection": "upgrade", @@ -164,15 +178,16 @@ def _validate(headers, key, subprotocols): r = headers.get(k, None) if not r: return False, None - r = r.lower() - if v != r: + r = [x.strip().lower() for x in r.split(',')] + if v not in r: return False, None if subprotocols: - subproto = headers.get("sec-websocket-protocol", None).lower() - if not subproto or subproto not in [s.lower() for s in subprotocols]: + subproto = headers.get("sec-websocket-protocol", None) + if not subproto or subproto.lower() not in [s.lower() for s in subprotocols]: error("Invalid subprotocol: " + str(subprotocols)) return False, None + subproto = subproto.lower() result = headers.get("sec-websocket-accept", None) if not result: diff --git a/libs/websocket/_http.py b/libs/websocket/_http.py index e7f1e2e0d..b0dad48ce 100644 --- a/libs/websocket/_http.py +++ b/libs/websocket/_http.py @@ -15,8 +15,7 @@ Copyright (C) 2010 Hiroki Ohtani(liris) 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-1335 USA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ import errno @@ -48,6 +47,7 @@ except: pass HAS_PYSOCKS = False + class proxy_info(object): def __init__(self, **options): @@ -64,6 +64,7 @@ class proxy_info(object): self.auth = None self.no_proxy = None + def _open_proxied_socket(url, options, proxy): hostname, port, resource, is_secure = parse_url(url) @@ -80,15 +81,15 @@ def _open_proxied_socket(url, options, proxy): rdns = True 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 + (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 ) if is_secure: @@ -138,15 +139,18 @@ 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) 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` + # or `OSError: [Errno 22] Invalid argument` when creating socket. Force the socket type to SOCK_STREAM. if not phost: addrinfo_list = socket.getaddrinfo( - hostname, port, 0, 0, socket.SOL_TCP) + hostname, port, 0, socket.SOCK_STREAM, socket.SOL_TCP) return addrinfo_list, False, None else: pport = pport and pport or 80 # when running on windows 10, the getaddrinfo used above # returns a socktype 0. This generates an error exception: - #_on_error: exception Socket type must be stream or datagram, not 0 + # _on_error: exception Socket type must be stream or datagram, not 0 # Force the socket type to SOCK_STREAM addrinfo_list = socket.getaddrinfo(phost, pport, 0, socket.SOCK_STREAM, socket.SOL_TCP) return addrinfo_list, True, pauth @@ -166,28 +170,35 @@ def _open_socket(addrinfo_list, sockopt, timeout): sock.setsockopt(*opts) address = addrinfo[4] - try: - sock.connect(address) - err = None - 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]) + err = None + while not err: try: - eConnRefused = (errno.ECONNREFUSED, errno.WSAECONNREFUSED) - except: - eConnRefused = (errno.ECONNREFUSED, ) - if error.errno in eConnRefused: - err = error + 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: + eConnRefused = (errno.ECONNREFUSED, errno.WSAECONNREFUSED) + except: + eConnRefused = (errno.ECONNREFUSED, ) + if error.errno == errno.EINTR: + continue + elif error.errno in eConnRefused: + err = error + continue + else: + raise error else: - raise error + break else: - break + continue + break else: - raise err + if err: + raise err return sock @@ -263,13 +274,15 @@ def _ssl_socket(sock, user_sslopt, hostname): def _tunnel(sock, host, port, auth): debug("Connecting proxy...") - connect_header = "CONNECT %s:%d HTTP/1.0\r\n" % (host, port) + connect_header = "CONNECT %s:%d HTTP/1.1\r\n" % (host, port) + connect_header += "Host: %s:%d\r\n" % (host, port) + # TODO: support digest auth. if auth and auth[0]: auth_str = auth[0] if auth[1]: auth_str += ":" + auth[1] - encoded_str = base64encode(auth_str.encode()).strip().decode() + encoded_str = base64encode(auth_str.encode()).strip().decode().replace('\n', '') connect_header += "Proxy-Authorization: Basic %s\r\n" % encoded_str connect_header += "\r\n" dump("request header", connect_header) @@ -310,7 +323,10 @@ def read_headers(sock): kv = line.split(":", 1) if len(kv) == 2: key, value = kv - headers[key.lower()] = value.strip() + if key.lower() == "set-cookie" and headers.get("set-cookie"): + headers["set-cookie"] = headers.get("set-cookie") + "; " + value.strip() + else: + headers[key.lower()] = value.strip() else: raise WebSocketException("Invalid header") diff --git a/libs/websocket/_logging.py b/libs/websocket/_logging.py index 70a6271d9..07d900903 100644 --- a/libs/websocket/_logging.py +++ b/libs/websocket/_logging.py @@ -1,3 +1,7 @@ +""" + +""" + """ websocket - WebSocket client library for Python @@ -15,8 +19,7 @@ Copyright (C) 2010 Hiroki Ohtani(liris) 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-1335 USA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ import logging @@ -34,14 +37,17 @@ _logger.addHandler(NullHandler()) _traceEnabled = False __all__ = ["enableTrace", "dump", "error", "warning", "debug", "trace", - "isEnabledForError", "isEnabledForDebug"] + "isEnabledForError", "isEnabledForDebug", "isEnabledForTrace"] -def enableTrace(traceable, handler = logging.StreamHandler()): +def enableTrace(traceable, handler=logging.StreamHandler()): """ - turn on/off the traceability. + Turn on/off the traceability. - traceable: boolean value. if set True, traceability is enabled. + Parameters + ---------- + traceable: bool + If set to True, traceability is enabled. """ global _traceEnabled _traceEnabled = traceable @@ -80,3 +86,7 @@ def isEnabledForError(): def isEnabledForDebug(): return _logger.isEnabledFor(logging.DEBUG) + + +def isEnabledForTrace(): + return _traceEnabled diff --git a/libs/websocket/_socket.py b/libs/websocket/_socket.py index c84fcf90a..2c383ed4d 100644 --- a/libs/websocket/_socket.py +++ b/libs/websocket/_socket.py @@ -1,3 +1,7 @@ +""" + +""" + """ websocket - WebSocket client library for Python @@ -15,14 +19,14 @@ Copyright (C) 2010 Hiroki Ohtani(liris) 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-1335 USA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ +import errno +import select import socket import six -import sys from ._exceptions import * from ._ssl_compat import * @@ -60,7 +64,10 @@ def setdefaulttimeout(timeout): """ Set the global timeout setting to connect. - timeout: default socket timeout time. This value is second. + Parameters + ---------- + timeout: int or float + default socket timeout time (in seconds) """ global _default_timeout _default_timeout = timeout @@ -68,7 +75,12 @@ def setdefaulttimeout(timeout): def getdefaulttimeout(): """ - Return the global timeout setting(second) to connect. + Get default timeout + + Returns + ---------- + _default_timeout: int or float + Return the global timeout setting (in seconds) to connect. """ return _default_timeout @@ -77,8 +89,27 @@ def recv(sock, bufsize): if not sock: raise WebSocketConnectionClosedException("socket is already closed.") + def _recv(): + try: + return sock.recv(bufsize) + except SSLWantReadError: + pass + except socket.error as exc: + error_code = extract_error_code(exc) + if error_code is None: + raise + if error_code != errno.EAGAIN or error_code != errno.EWOULDBLOCK: + raise + + r, w, e = select.select((sock, ), (), (), sock.gettimeout()) + if r: + return sock.recv(bufsize) + try: - bytes_ = sock.recv(bufsize) + if sock.gettimeout() == 0: + bytes_ = sock.recv(bufsize) + else: + bytes_ = _recv() except socket.timeout as e: message = extract_err_message(e) raise WebSocketTimeoutException(message) @@ -113,8 +144,27 @@ def send(sock, data): if not sock: raise WebSocketConnectionClosedException("socket is already closed.") + def _send(): + try: + return sock.send(data) + except SSLWantWriteError: + pass + except socket.error as exc: + error_code = extract_error_code(exc) + if error_code is None: + raise + if error_code != errno.EAGAIN or error_code != errno.EWOULDBLOCK: + raise + + r, w, e = select.select((), (sock, ), (), sock.gettimeout()) + if w: + return sock.send(data) + try: - return sock.send(data) + if sock.gettimeout() == 0: + return sock.send(data) + else: + return _send() except socket.timeout as e: message = extract_err_message(e) raise WebSocketTimeoutException(message) diff --git a/libs/websocket/_ssl_compat.py b/libs/websocket/_ssl_compat.py index 030481628..9e201ddf0 100644 --- a/libs/websocket/_ssl_compat.py +++ b/libs/websocket/_ssl_compat.py @@ -15,15 +15,16 @@ Copyright (C) 2010 Hiroki Ohtani(liris) 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-1335 USA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ -__all__ = ["HAVE_SSL", "ssl", "SSLError"] +__all__ = ["HAVE_SSL", "ssl", "SSLError", "SSLWantReadError", "SSLWantWriteError"] try: import ssl from ssl import SSLError + from ssl import SSLWantReadError + from ssl import SSLWantWriteError if hasattr(ssl, 'SSLContext') and hasattr(ssl.SSLContext, 'check_hostname'): HAVE_CONTEXT_CHECK_HOSTNAME = True else: @@ -41,4 +42,12 @@ except ImportError: class SSLError(Exception): pass + class SSLWantReadError(Exception): + pass + + class SSLWantWriteError(Exception): + pass + + ssl = None + HAVE_SSL = False diff --git a/libs/websocket/_url.py b/libs/websocket/_url.py index ae46d6c40..92ff939e3 100644 --- a/libs/websocket/_url.py +++ b/libs/websocket/_url.py @@ -1,4 +1,7 @@ """ + +""" +""" websocket - WebSocket client library for Python Copyright (C) 2010 Hiroki Ohtani(liris) @@ -15,8 +18,7 @@ Copyright (C) 2010 Hiroki Ohtani(liris) 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-1335 USA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ @@ -35,14 +37,17 @@ def parse_url(url): parse url and the result is tuple of (hostname, port, resource path and the flag of secure mode) - url: url string. + Parameters + ---------- + url: str + url string. """ if ":" not in url: raise ValueError("url is invalid") scheme, url = url.split(":", 1) - parsed = urlparse(url, scheme="ws") + parsed = urlparse(url, scheme="http") if parsed.hostname: hostname = parsed.hostname else: @@ -94,24 +99,31 @@ def _is_subnet_address(hostname): def _is_address_in_network(ip, net): - ipaddr = struct.unpack('I', socket.inet_aton(ip))[0] - netaddr, bits = net.split('/') - netmask = struct.unpack('I', socket.inet_aton(netaddr))[0] & ((2 << int(bits) - 1) - 1) - return ipaddr & netmask == netmask + ipaddr = struct.unpack('!I', socket.inet_aton(ip))[0] + netaddr, netmask = net.split('/') + netaddr = struct.unpack('!I', socket.inet_aton(netaddr))[0] + + netmask = (0xFFFFFFFF << (32 - int(netmask))) & 0xFFFFFFFF + return ipaddr & netmask == netaddr def _is_no_proxy_host(hostname, no_proxy): if not no_proxy: v = os.environ.get("no_proxy", "").replace(" ", "") - no_proxy = v.split(",") + if v: + no_proxy = v.split(",") if not no_proxy: no_proxy = DEFAULT_NO_PROXY_HOST + if '*' in no_proxy: + return True if hostname in no_proxy: return True - elif _is_ip_address(hostname): + if _is_ip_address(hostname): return any([_is_address_in_network(hostname, subnet) for subnet in no_proxy if _is_subnet_address(subnet)]) - + for domain in [domain for domain in no_proxy if domain.startswith('.')]: + if hostname.endswith(domain): + return True return False @@ -119,27 +131,30 @@ def get_proxy_info( hostname, is_secure, proxy_host=None, proxy_port=0, proxy_auth=None, no_proxy=None, proxy_type='http'): """ - try to retrieve proxy host and port from environment + Try to retrieve proxy host and port from environment if not provided in options. - result is (proxy_host, proxy_port, proxy_auth). + Result is (proxy_host, proxy_port, proxy_auth). proxy_auth is tuple of username and password - of proxy authentication information. - - hostname: websocket server name. - - is_secure: is the connection secure? (wss) - looks for "https_proxy" in env - before falling back to "http_proxy" - - options: "http_proxy_host" - http proxy host name. - "http_proxy_port" - http proxy port. - "http_no_proxy" - host names, which doesn't use proxy. - "http_proxy_auth" - http proxy auth information. - tuple of username and password. - default is None - "proxy_type" - if set to "socks5" PySocks wrapper - will be used in place of a http proxy. - default is "http" + of proxy authentication information. + + Parameters + ---------- + hostname: + websocket server name. + is_secure: + is the connection secure? (wss) looks for "https_proxy" in env + before falling back to "http_proxy" + options: + - http_proxy_host: + http proxy host name. + - http_proxy_port: + http proxy port. + - http_no_proxy: + host names, which doesn't use proxy. + - http_proxy_auth: + http proxy auth information. tuple of username and password. default is None + - proxy_type: + if set to "socks5" PySocks wrapper will be used in place of a http proxy. default is "http" """ if _is_no_proxy_host(hostname, no_proxy): return None, 0, None diff --git a/libs/websocket/_utils.py b/libs/websocket/_utils.py index 399fb89d9..0072bce8a 100644 --- a/libs/websocket/_utils.py +++ b/libs/websocket/_utils.py @@ -15,13 +15,12 @@ Copyright (C) 2010 Hiroki Ohtani(liris) 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-1335 USA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ import six -__all__ = ["NoLock", "validate_utf8", "extract_err_message"] +__all__ = ["NoLock", "validate_utf8", "extract_err_message", "extract_error_code"] class NoLock(object): @@ -32,6 +31,7 @@ class NoLock(object): def __exit__(self, exc_type, exc_value, traceback): pass + try: # If wsaccel is available we use compiled routines to validate UTF-8 # strings. @@ -103,3 +103,8 @@ def extract_err_message(exception): return exception.args[0] else: return None + + +def extract_error_code(exception): + if exception.args and len(exception.args) > 1: + return exception.args[0] if isinstance(exception.args[0], int) else None diff --git a/libs/websocket/tests/data/header03.txt b/libs/websocket/tests/data/header03.txt new file mode 100644 index 000000000..012b7d18d --- /dev/null +++ b/libs/websocket/tests/data/header03.txt @@ -0,0 +1,6 @@ +HTTP/1.1 101 WebSocket Protocol Handshake +Connection: Upgrade, Keep-Alive +Upgrade: WebSocket +Sec-WebSocket-Accept: Kxep+hNu9n51529fGidYu7a3wO0= +some_header: something + diff --git a/libs/websocket/tests/test_abnf.py b/libs/websocket/tests/test_abnf.py new file mode 100644 index 000000000..acce02068 --- /dev/null +++ b/libs/websocket/tests/test_abnf.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +# +""" +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. + + 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 + +""" + +import os +import websocket as ws +from websocket._abnf import * +import sys +sys.path[0:0] = [""] + +if sys.version_info[0] == 2 and sys.version_info[1] < 7: + import unittest2 as unittest +else: + import unittest + + +class ABNFTest(unittest.TestCase): + + def testInit(self): + a = ABNF(0,0,0,0, opcode=ABNF.OPCODE_PING) + self.assertEqual(a.fin, 0) + self.assertEqual(a.rsv1, 0) + self.assertEqual(a.rsv2, 0) + self.assertEqual(a.rsv3, 0) + self.assertEqual(a.opcode, 9) + self.assertEqual(a.data, '') + a_bad = ABNF(0,1,0,0, opcode=77) + self.assertEqual(a_bad.rsv1, 1) + self.assertEqual(a_bad.opcode, 77) + + def testValidate(self): + a = ABNF(0,0,0,0, opcode=ABNF.OPCODE_PING) + self.assertRaises(ws.WebSocketProtocolException, a.validate) + a_bad = ABNF(0,1,0,0, opcode=77) + self.assertRaises(ws.WebSocketProtocolException, a_bad.validate) + a_close = ABNF(0,1,0,0, opcode=ABNF.OPCODE_CLOSE, data="abcdefgh1234567890abcdefgh1234567890abcdefgh1234567890abcdefgh1234567890") + self.assertRaises(ws.WebSocketProtocolException, a_close.validate) + +# This caused an error in the Python 2.7 Github Actions build +# Uncomment test case when Python 2 support no longer wanted +# def testMask(self): +# ab = ABNF(0,0,0,0, opcode=ABNF.OPCODE_PING) +# bytes_val = bytes("aaaa", 'utf-8') +# self.assertEqual(ab._get_masked(bytes_val), bytes_val) + + def testFrameBuffer(self): + fb = frame_buffer(0, True) + self.assertEqual(fb.recv, 0) + self.assertEqual(fb.skip_utf8_validation, True) + fb.clear + self.assertEqual(fb.header, None) + self.assertEqual(fb.length, None) + self.assertEqual(fb.mask, None) + self.assertEqual(fb.has_mask(), False) + + +if __name__ == "__main__": + unittest.main() diff --git a/libs/websocket/tests/test_app.py b/libs/websocket/tests/test_app.py new file mode 100644 index 000000000..e5a739008 --- /dev/null +++ b/libs/websocket/tests/test_app.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- +# +""" +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. + + 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 + +""" + +import os +import os.path +import websocket as ws +import sys +sys.path[0:0] = [""] + +try: + import ssl +except ImportError: + HAVE_SSL = False + +if sys.version_info[0] == 2 and sys.version_info[1] < 7: + import unittest2 as unittest +else: + import unittest + +# Skip test to access the internet. +TEST_WITH_INTERNET = os.environ.get('TEST_WITH_INTERNET', '0') == '1' +TRACEABLE = True + + +class WebSocketAppTest(unittest.TestCase): + + class NotSetYet(object): + """ A marker class for signalling that a value hasn't been set yet. + """ + + def setUp(self): + ws.enableTrace(TRACEABLE) + + WebSocketAppTest.keep_running_open = WebSocketAppTest.NotSetYet() + WebSocketAppTest.keep_running_close = WebSocketAppTest.NotSetYet() + WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet() + + def tearDown(self): + WebSocketAppTest.keep_running_open = WebSocketAppTest.NotSetYet() + WebSocketAppTest.keep_running_close = WebSocketAppTest.NotSetYet() + WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet() + + @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") + def testKeepRunning(self): + """ A WebSocketApp should keep running as long as its self.keep_running + is not False (in the boolean context). + """ + + def on_open(self, *args, **kwargs): + """ Set the keep_running flag for later inspection and immediately + close the connection. + """ + WebSocketAppTest.keep_running_open = self.keep_running + + 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 + + app = ws.WebSocketApp('ws://echo.websocket.org/', on_open=on_open, on_close=on_close) + app.run_forever() + + # if numpy is installed, this assertion fail + # self.assertFalse(isinstance(WebSocketAppTest.keep_running_open, + # WebSocketAppTest.NotSetYet)) + + # self.assertFalse(isinstance(WebSocketAppTest.keep_running_close, + # WebSocketAppTest.NotSetYet)) + + # self.assertEqual(True, WebSocketAppTest.keep_running_open) + # self.assertEqual(False, WebSocketAppTest.keep_running_close) + + @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") + def testSockMaskKey(self): + """ A WebSocketApp should forward the received mask_key function down + to the actual socket. + """ + + def my_mask_key_func(): + pass + + def on_open(self, *args, **kwargs): + """ Set the value so the test can use it later on and immediately + close the connection. + """ + WebSocketAppTest.get_mask_key_id = id(self.get_mask_key) + self.close() + + app = ws.WebSocketApp('ws://echo.websocket.org/', on_open=on_open, get_mask_key=my_mask_key_func) + app.run_forever() + + # if numpy is installed, this assertion fail + # Note: We can't use 'is' for comparing the functions directly, need to use 'id'. + # self.assertEqual(WebSocketAppTest.get_mask_key_id, id(my_mask_key_func)) + + @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") + def testPingInterval(self): + """ A WebSocketApp should ping regularly + """ + + def on_ping(app, msg): + print("Got a ping!") + app.close() + + def on_pong(app, msg): + print("Got a pong! No need to respond") + app.close() + + app = ws.WebSocketApp('wss://api-pub.bitfinex.com/ws/1', on_ping=on_ping, on_pong=on_pong) + app.run_forever(ping_interval=2, ping_timeout=1) # , sslopt={"cert_reqs": ssl.CERT_NONE} + self.assertRaises(ws.WebSocketException, app.run_forever, ping_interval=2, ping_timeout=3, sslopt={"cert_reqs": ssl.CERT_NONE}) + + +if __name__ == "__main__": + unittest.main() diff --git a/libs/websocket/tests/test_cookiejar.py b/libs/websocket/tests/test_cookiejar.py index c40a00bd2..fc66e58b0 100644 --- a/libs/websocket/tests/test_cookiejar.py +++ b/libs/websocket/tests/test_cookiejar.py @@ -1,12 +1,31 @@ +""" + +""" + +""" +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. + + 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 + +""" import unittest from websocket._cookiejar import SimpleCookieJar -try: - import Cookie -except: - import http.cookies as Cookie - class CookieJarTest(unittest.TestCase): def testAdd(self): @@ -29,24 +48,24 @@ class CookieJarTest(unittest.TestCase): cookie_jar = SimpleCookieJar() cookie_jar.add("a=b; c=d; domain=abc") - self.assertEquals(cookie_jar.get("abc"), "a=b; c=d") + self.assertEqual(cookie_jar.get("abc"), "a=b; c=d") cookie_jar = SimpleCookieJar() cookie_jar.add("a=b; c=d; domain=abc") cookie_jar.add("e=f; domain=abc") - self.assertEquals(cookie_jar.get("abc"), "a=b; c=d; e=f") + self.assertEqual(cookie_jar.get("abc"), "a=b; c=d; e=f") cookie_jar = SimpleCookieJar() cookie_jar.add("a=b; c=d; domain=abc") cookie_jar.add("e=f; domain=.abc") - self.assertEquals(cookie_jar.get("abc"), "a=b; c=d; e=f") + self.assertEqual(cookie_jar.get("abc"), "a=b; c=d; e=f") cookie_jar = SimpleCookieJar() cookie_jar.add("a=b; c=d; domain=abc") cookie_jar.add("e=f; domain=xyz") - self.assertEquals(cookie_jar.get("abc"), "a=b; c=d") - self.assertEquals(cookie_jar.get("xyz"), "e=f") - self.assertEquals(cookie_jar.get("something"), "") + self.assertEqual(cookie_jar.get("abc"), "a=b; c=d") + self.assertEqual(cookie_jar.get("xyz"), "e=f") + self.assertEqual(cookie_jar.get("something"), "") def testSet(self): cookie_jar = SimpleCookieJar() @@ -64,35 +83,35 @@ class CookieJarTest(unittest.TestCase): cookie_jar = SimpleCookieJar() cookie_jar.set("a=b; c=d; domain=abc") - self.assertEquals(cookie_jar.get("abc"), "a=b; c=d") + self.assertEqual(cookie_jar.get("abc"), "a=b; c=d") cookie_jar = SimpleCookieJar() cookie_jar.set("a=b; c=d; domain=abc") cookie_jar.set("e=f; domain=abc") - self.assertEquals(cookie_jar.get("abc"), "e=f") + self.assertEqual(cookie_jar.get("abc"), "e=f") cookie_jar = SimpleCookieJar() cookie_jar.set("a=b; c=d; domain=abc") cookie_jar.set("e=f; domain=.abc") - self.assertEquals(cookie_jar.get("abc"), "e=f") + self.assertEqual(cookie_jar.get("abc"), "e=f") cookie_jar = SimpleCookieJar() cookie_jar.set("a=b; c=d; domain=abc") cookie_jar.set("e=f; domain=xyz") - self.assertEquals(cookie_jar.get("abc"), "a=b; c=d") - self.assertEquals(cookie_jar.get("xyz"), "e=f") - self.assertEquals(cookie_jar.get("something"), "") + self.assertEqual(cookie_jar.get("abc"), "a=b; c=d") + self.assertEqual(cookie_jar.get("xyz"), "e=f") + self.assertEqual(cookie_jar.get("something"), "") def testGet(self): cookie_jar = SimpleCookieJar() cookie_jar.set("a=b; c=d; domain=abc.com") - self.assertEquals(cookie_jar.get("abc.com"), "a=b; c=d") - self.assertEquals(cookie_jar.get("x.abc.com"), "a=b; c=d") - self.assertEquals(cookie_jar.get("abc.com.es"), "") - self.assertEquals(cookie_jar.get("xabc.com"), "") + self.assertEqual(cookie_jar.get("abc.com"), "a=b; c=d") + 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"), "") cookie_jar.set("a=b; c=d; domain=.abc.com") - self.assertEquals(cookie_jar.get("abc.com"), "a=b; c=d") - self.assertEquals(cookie_jar.get("x.abc.com"), "a=b; c=d") - self.assertEquals(cookie_jar.get("abc.com.es"), "") - self.assertEquals(cookie_jar.get("xabc.com"), "") + self.assertEqual(cookie_jar.get("abc.com"), "a=b; c=d") + 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"), "") diff --git a/libs/websocket/tests/test_http.py b/libs/websocket/tests/test_http.py new file mode 100644 index 000000000..0336ff7d7 --- /dev/null +++ b/libs/websocket/tests/test_http.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +# +""" +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. + + 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 + +""" + +import os +import os.path +import websocket as ws +from websocket._http import proxy_info, read_headers, _open_proxied_socket, _tunnel +import sys +sys.path[0:0] = [""] + +if sys.version_info[0] == 2 and sys.version_info[1] < 7: + import unittest2 as unittest +else: + import unittest + + +class SockMock(object): + def __init__(self): + self.data = [] + self.sent = [] + + def add_packet(self, data): + self.data.append(data) + + def gettimeout(self): + return None + + def recv(self, bufsize): + if self.data: + e = self.data.pop(0) + if isinstance(e, Exception): + raise e + if len(e) > bufsize: + self.data.insert(0, e[bufsize:]) + return e[:bufsize] + + def send(self, data): + self.sent.append(data) + return len(data) + + def close(self): + pass + + +class HeaderSockMock(SockMock): + + def __init__(self, fname): + SockMock.__init__(self) + path = os.path.join(os.path.dirname(__file__), fname) + with open(path, "rb") as f: + self.add_packet(f.read()) + + +class OptsList(): + + def __init__(self): + self.timeout = 0 + self.sockopt = [] + + +class HttpTest(unittest.TestCase): + + def testReadHeader(self): + status, header, status_message = read_headers(HeaderSockMock("data/header01.txt")) + self.assertEqual(status, 101) + self.assertEqual(header["connection"], "Upgrade") + # header02.txt is intentionally malformed + self.assertRaises(ws.WebSocketException, read_headers, HeaderSockMock("data/header02.txt")) + + def testTunnel(self): + self.assertRaises(ws.WebSocketProxyException, _tunnel, HeaderSockMock("data/header01.txt"), "example.com", 80, ("username", "password")) + self.assertRaises(ws.WebSocketProxyException, _tunnel, HeaderSockMock("data/header02.txt"), "example.com", 80, ("username", "password")) + + def testConnect(self): + # Not currently testing an actual proxy connection, so just check whether TypeError is raised + 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")) + + 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").auth, None) + + +if __name__ == "__main__": + unittest.main() diff --git a/libs/websocket/tests/test_url.py b/libs/websocket/tests/test_url.py new file mode 100644 index 000000000..b1d8e06f2 --- /dev/null +++ b/libs/websocket/tests/test_url.py @@ -0,0 +1,309 @@ +# -*- coding: utf-8 -*- +# +""" +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. + + 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 + +""" + +import sys +import os + +from websocket._url import get_proxy_info, parse_url, _is_address_in_network, _is_no_proxy_host + +if sys.version_info[0] == 2 and sys.version_info[1] < 7: + import unittest2 as unittest +else: + import unittest +sys.path[0:0] = [""] + + +class UrlTest(unittest.TestCase): + + def test_address_in_network(self): + self.assertTrue(_is_address_in_network('127.0.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')) + + def testParseUrl(self): + p = parse_url("ws://www.example.com/r") + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 80) + self.assertEqual(p[2], "/r") + self.assertEqual(p[3], False) + + p = parse_url("ws://www.example.com/r/") + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 80) + self.assertEqual(p[2], "/r/") + self.assertEqual(p[3], False) + + p = parse_url("ws://www.example.com/") + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 80) + self.assertEqual(p[2], "/") + self.assertEqual(p[3], False) + + p = parse_url("ws://www.example.com") + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 80) + self.assertEqual(p[2], "/") + self.assertEqual(p[3], False) + + p = parse_url("ws://www.example.com:8080/r") + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 8080) + self.assertEqual(p[2], "/r") + self.assertEqual(p[3], False) + + p = parse_url("ws://www.example.com:8080/") + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 8080) + self.assertEqual(p[2], "/") + self.assertEqual(p[3], False) + + p = parse_url("ws://www.example.com:8080") + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 8080) + self.assertEqual(p[2], "/") + self.assertEqual(p[3], False) + + p = parse_url("wss://www.example.com:8080/r") + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 8080) + self.assertEqual(p[2], "/r") + self.assertEqual(p[3], True) + + p = parse_url("wss://www.example.com:8080/r?key=value") + self.assertEqual(p[0], "www.example.com") + self.assertEqual(p[1], 8080) + self.assertEqual(p[2], "/r?key=value") + self.assertEqual(p[3], True) + + self.assertRaises(ValueError, parse_url, "http://www.example.com/r") + + if sys.version_info[0] == 2 and sys.version_info[1] < 7: + return + + p = parse_url("ws://[2a03:4000:123:83::3]/r") + self.assertEqual(p[0], "2a03:4000:123:83::3") + self.assertEqual(p[1], 80) + self.assertEqual(p[2], "/r") + self.assertEqual(p[3], False) + + p = parse_url("ws://[2a03:4000:123:83::3]:8080/r") + self.assertEqual(p[0], "2a03:4000:123:83::3") + self.assertEqual(p[1], 8080) + self.assertEqual(p[2], "/r") + self.assertEqual(p[3], False) + + p = parse_url("wss://[2a03:4000:123:83::3]/r") + self.assertEqual(p[0], "2a03:4000:123:83::3") + self.assertEqual(p[1], 443) + self.assertEqual(p[2], "/r") + self.assertEqual(p[3], True) + + p = parse_url("wss://[2a03:4000:123:83::3]:8080/r") + self.assertEqual(p[0], "2a03:4000:123:83::3") + self.assertEqual(p[1], 8080) + self.assertEqual(p[2], "/r") + self.assertEqual(p[3], True) + + +class IsNoProxyHostTest(unittest.TestCase): + def setUp(self): + self.no_proxy = os.environ.get("no_proxy", None) + if "no_proxy" in os.environ: + del os.environ["no_proxy"] + + def tearDown(self): + if self.no_proxy: + os.environ["no_proxy"] = self.no_proxy + elif "no_proxy" in os.environ: + del os.environ["no_proxy"] + + def testMatchAll(self): + 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("any.websocket.org", ['other.websocket.org', '*'])) + os.environ['no_proxy'] = '*' + self.assertTrue(_is_no_proxy_host("any.websocket.org", None)) + self.assertTrue(_is_no_proxy_host("192.168.0.1", None)) + os.environ['no_proxy'] = 'other.websocket.org, *' + self.assertTrue(_is_no_proxy_host("any.websocket.org", None)) + + def testIpAddress(self): + 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.assertTrue(_is_no_proxy_host("127.0.0.1", ['other.websocket.org', '127.0.0.1'])) + self.assertFalse(_is_no_proxy_host("127.0.0.2", ['other.websocket.org', '127.0.0.1'])) + os.environ['no_proxy'] = '127.0.0.1' + self.assertTrue(_is_no_proxy_host("127.0.0.1", None)) + self.assertFalse(_is_no_proxy_host("127.0.0.2", None)) + os.environ['no_proxy'] = 'other.websocket.org, 127.0.0.1' + self.assertTrue(_is_no_proxy_host("127.0.0.1", None)) + self.assertFalse(_is_no_proxy_host("127.0.0.2", None)) + + def testIpAddressInRange(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.2", ['127.0.0.0/8'])) + self.assertFalse(_is_no_proxy_host("127.1.0.1", ['127.0.0.0/24'])) + os.environ['no_proxy'] = '127.0.0.0/8' + self.assertTrue(_is_no_proxy_host("127.0.0.1", None)) + self.assertTrue(_is_no_proxy_host("127.0.0.2", None)) + os.environ['no_proxy'] = '127.0.0.0/24' + self.assertFalse(_is_no_proxy_host("127.1.0.1", None)) + + def testHostnameMatch(self): + self.assertTrue(_is_no_proxy_host("my.websocket.org", ['my.websocket.org'])) + self.assertTrue(_is_no_proxy_host("my.websocket.org", ['other.websocket.org', 'my.websocket.org'])) + self.assertFalse(_is_no_proxy_host("my.websocket.org", ['other.websocket.org'])) + os.environ['no_proxy'] = 'my.websocket.org' + self.assertTrue(_is_no_proxy_host("my.websocket.org", None)) + self.assertFalse(_is_no_proxy_host("other.websocket.org", None)) + os.environ['no_proxy'] = 'other.websocket.org, my.websocket.org' + self.assertTrue(_is_no_proxy_host("my.websocket.org", None)) + + def testHostnameMatchDomain(self): + 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("any.websocket.org", ['my.websocket.org', '.websocket.org'])) + self.assertFalse(_is_no_proxy_host("any.websocket.com", ['.websocket.org'])) + os.environ['no_proxy'] = '.websocket.org' + self.assertTrue(_is_no_proxy_host("any.websocket.org", None)) + self.assertTrue(_is_no_proxy_host("my.other.websocket.org", None)) + self.assertFalse(_is_no_proxy_host("any.websocket.com", None)) + os.environ['no_proxy'] = 'my.websocket.org, .websocket.org' + self.assertTrue(_is_no_proxy_host("any.websocket.org", None)) + + +class ProxyInfoTest(unittest.TestCase): + def setUp(self): + self.http_proxy = os.environ.get("http_proxy", None) + self.https_proxy = os.environ.get("https_proxy", None) + self.no_proxy = os.environ.get("no_proxy", None) + if "http_proxy" in os.environ: + del os.environ["http_proxy"] + if "https_proxy" in os.environ: + del os.environ["https_proxy"] + if "no_proxy" in os.environ: + del os.environ["no_proxy"] + + def tearDown(self): + if self.http_proxy: + os.environ["http_proxy"] = self.http_proxy + elif "http_proxy" in os.environ: + del os.environ["http_proxy"] + + if self.https_proxy: + os.environ["https_proxy"] = self.https_proxy + elif "https_proxy" in os.environ: + del os.environ["https_proxy"] + + if self.no_proxy: + os.environ["no_proxy"] = self.no_proxy + elif "no_proxy" in os.environ: + del os.environ["no_proxy"] + + def testProxyFromArgs(self): + self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost"), ("localhost", 0, None)) + self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_port=3128), + ("localhost", 3128, None)) + self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost"), ("localhost", 0, None)) + self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128), + ("localhost", 3128, None)) + + self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_auth=("a", "b")), + ("localhost", 0, ("a", "b"))) + self.assertEqual( + get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_port=3128, proxy_auth=("a", "b")), + ("localhost", 3128, ("a", "b"))) + self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_auth=("a", "b")), + ("localhost", 0, ("a", "b"))) + self.assertEqual( + get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128, proxy_auth=("a", "b")), + ("localhost", 3128, ("a", "b"))) + + self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128, + no_proxy=["example.com"], proxy_auth=("a", "b")), + ("localhost", 3128, ("a", "b"))) + self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128, + no_proxy=["echo.websocket.org"], proxy_auth=("a", "b")), + (None, 0, None)) + + def testProxyFromEnv(self): + os.environ["http_proxy"] = "http://localhost/" + self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, None)) + os.environ["http_proxy"] = "http://localhost:3128/" + self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, None)) + + os.environ["http_proxy"] = "http://localhost/" + os.environ["https_proxy"] = "http://localhost2/" + self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, None)) + os.environ["http_proxy"] = "http://localhost:3128/" + os.environ["https_proxy"] = "http://localhost2:3128/" + self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, None)) + + os.environ["http_proxy"] = "http://localhost/" + os.environ["https_proxy"] = "http://localhost2/" + self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", None, None)) + os.environ["http_proxy"] = "http://localhost:3128/" + os.environ["https_proxy"] = "http://localhost2:3128/" + self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", 3128, None)) + + os.environ["http_proxy"] = "http://a:b@localhost/" + self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, ("a", "b"))) + os.environ["http_proxy"] = "http://a:b@localhost:3128/" + self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, ("a", "b"))) + + os.environ["http_proxy"] = "http://a:b@localhost/" + os.environ["https_proxy"] = "http://a:b@localhost2/" + self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, ("a", "b"))) + os.environ["http_proxy"] = "http://a:b@localhost:3128/" + os.environ["https_proxy"] = "http://a:b@localhost2:3128/" + self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, ("a", "b"))) + + os.environ["http_proxy"] = "http://a:b@localhost/" + os.environ["https_proxy"] = "http://a:b@localhost2/" + self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", None, ("a", "b"))) + os.environ["http_proxy"] = "http://a:b@localhost:3128/" + 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://a:b@localhost/" + os.environ["https_proxy"] = "http://a:b@localhost2/" + os.environ["no_proxy"] = "example1.com,example2.com" + self.assertEqual(get_proxy_info("example.1.com", True), ("localhost2", None, ("a", "b"))) + os.environ["http_proxy"] = "http://a:b@localhost:3128/" + os.environ["https_proxy"] = "http://a:b@localhost2:3128/" + os.environ["no_proxy"] = "example1.com,example2.com, echo.websocket.org" + self.assertEqual(get_proxy_info("echo.websocket.org", True), (None, 0, None)) + os.environ["http_proxy"] = "http://a:b@localhost:3128/" + os.environ["https_proxy"] = "http://a:b@localhost2:3128/" + os.environ["no_proxy"] = "example1.com,example2.com, .websocket.org" + self.assertEqual(get_proxy_info("echo.websocket.org", True), (None, 0, None)) + + os.environ["http_proxy"] = "http://a:b@localhost:3128/" + os.environ["https_proxy"] = "http://a:b@localhost2:3128/" + os.environ["no_proxy"] = "127.0.0.0/8, 192.168.0.0/16" + self.assertEqual(get_proxy_info("127.0.0.1", False), (None, 0, None)) + self.assertEqual(get_proxy_info("192.168.1.1", False), (None, 0, None)) + + +if __name__ == "__main__": + unittest.main() diff --git a/libs/websocket/tests/test_websocket.py b/libs/websocket/tests/test_websocket.py index f49a89398..0d1d6395e 100644 --- a/libs/websocket/tests/test_websocket.py +++ b/libs/websocket/tests/test_websocket.py @@ -1,5 +1,29 @@ # -*- coding: utf-8 -*- # +""" + +""" + +""" +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. + + 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 + +""" import sys sys.path[0:0] = [""] @@ -15,7 +39,6 @@ import websocket as ws from websocket._handshake import _create_sec_websocket_key, \ _validate as _validate_header from websocket._http import read_headers -from websocket._url import get_proxy_info, parse_url from websocket._utils import validate_utf8 if six.PY3: @@ -37,9 +60,6 @@ except ImportError: # Skip test to access the internet. TEST_WITH_INTERNET = os.environ.get('TEST_WITH_INTERNET', '0') == '1' - -# Skip Secure WebSocket test. -TEST_SECURE_WS = True TRACEABLE = True @@ -55,6 +75,9 @@ class SockMock(object): def add_packet(self, data): self.data.append(data) + def gettimeout(self): + return None + def recv(self, bufsize): if self.data: e = self.data.pop(0) @@ -94,102 +117,24 @@ class WebSocketTest(unittest.TestCase): self.assertEqual(ws.getdefaulttimeout(), 10) ws.setdefaulttimeout(None) - def testParseUrl(self): - p = parse_url("ws://www.example.com/r") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 80) - self.assertEqual(p[2], "/r") - self.assertEqual(p[3], False) - - p = parse_url("ws://www.example.com/r/") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 80) - self.assertEqual(p[2], "/r/") - self.assertEqual(p[3], False) - - p = parse_url("ws://www.example.com/") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 80) - self.assertEqual(p[2], "/") - self.assertEqual(p[3], False) - - p = parse_url("ws://www.example.com") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 80) - self.assertEqual(p[2], "/") - self.assertEqual(p[3], False) - - p = parse_url("ws://www.example.com:8080/r") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 8080) - self.assertEqual(p[2], "/r") - self.assertEqual(p[3], False) - - p = parse_url("ws://www.example.com:8080/") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 8080) - self.assertEqual(p[2], "/") - self.assertEqual(p[3], False) - - p = parse_url("ws://www.example.com:8080") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 8080) - self.assertEqual(p[2], "/") - self.assertEqual(p[3], False) - - p = parse_url("wss://www.example.com:8080/r") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 8080) - self.assertEqual(p[2], "/r") - self.assertEqual(p[3], True) - - p = parse_url("wss://www.example.com:8080/r?key=value") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 8080) - self.assertEqual(p[2], "/r?key=value") - self.assertEqual(p[3], True) - - self.assertRaises(ValueError, parse_url, "http://www.example.com/r") - - if sys.version_info[0] == 2 and sys.version_info[1] < 7: - return - - p = parse_url("ws://[2a03:4000:123:83::3]/r") - self.assertEqual(p[0], "2a03:4000:123:83::3") - self.assertEqual(p[1], 80) - self.assertEqual(p[2], "/r") - self.assertEqual(p[3], False) - - p = parse_url("ws://[2a03:4000:123:83::3]:8080/r") - self.assertEqual(p[0], "2a03:4000:123:83::3") - self.assertEqual(p[1], 8080) - self.assertEqual(p[2], "/r") - self.assertEqual(p[3], False) - - p = parse_url("wss://[2a03:4000:123:83::3]/r") - self.assertEqual(p[0], "2a03:4000:123:83::3") - self.assertEqual(p[1], 443) - self.assertEqual(p[2], "/r") - self.assertEqual(p[3], True) - - p = parse_url("wss://[2a03:4000:123:83::3]:8080/r") - self.assertEqual(p[0], "2a03:4000:123:83::3") - self.assertEqual(p[1], 8080) - self.assertEqual(p[2], "/r") - self.assertEqual(p[3], True) - def testWSKey(self): key = _create_sec_websocket_key() self.assertTrue(key != 24) self.assertTrue(six.u("¥n") not in key) + def testNonce(self): + """ WebSocket key should be a random 16-byte nonce. + """ + key = _create_sec_websocket_key() + nonce = base64decode(key.encode("utf-8")) + self.assertEqual(16, len(nonce)) + def testWsUtils(self): key = "c6b8hTg4EeGb2gQMztV1/g==" required_header = { "upgrade": "websocket", "connection": "upgrade", - "sec-websocket-accept": "Kxep+hNu9n51529fGidYu7a3wO0=", - } + "sec-websocket-accept": "Kxep+hNu9n51529fGidYu7a3wO0="} self.assertEqual(_validate_header(required_header, key, None), (True, None)) header = required_header.copy() @@ -219,12 +164,18 @@ class WebSocketTest(unittest.TestCase): header["sec-websocket-protocol"] = "sUb1" self.assertEqual(_validate_header(header, key, ["Sub1", "suB2"]), (True, "sub1")) + header = required_header.copy() + self.assertEqual(_validate_header(header, key, ["Sub1", "suB2"]), (False, None)) def testReadHeader(self): status, header, status_message = read_headers(HeaderSockMock("data/header01.txt")) self.assertEqual(status, 101) self.assertEqual(header["connection"], "Upgrade") + status, header, status_message = read_headers(HeaderSockMock("data/header03.txt")) + self.assertEqual(status, 101) + self.assertEqual(header["connection"], "Upgrade, Keep-Alive") + HeaderSockMock("data/header02.txt") self.assertRaises(ws.WebSocketException, read_headers, HeaderSockMock("data/header02.txt")) @@ -242,7 +193,10 @@ class WebSocketTest(unittest.TestCase): sock.send(u"こんにちは") self.assertEqual(s.sent[1], six.b("\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc")) - sock.send("x" * 127) +# sock.send("x" * 5000) +# self.assertEqual(s.sent[1], six.b("\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc")) + + self.assertEqual(sock.send_binary(b'1111111111101'), 19) def testRecv(self): # TODO: add longer frame data @@ -260,14 +214,14 @@ class WebSocketTest(unittest.TestCase): @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") def testIter(self): count = 2 - for _ in ws.create_connection('ws://stream.meetup.com/2/rsvps'): + for _ in ws.create_connection('wss://stream.meetup.com/2/rsvps'): count -= 1 if count == 0: break @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") def testNext(self): - sock = ws.create_connection('ws://stream.meetup.com/2/rsvps') + sock = ws.create_connection('wss://stream.meetup.com/2/rsvps') self.assertEqual(str, type(next(sock))) def testInternalRecvStrict(self): @@ -415,6 +369,7 @@ class WebSocketTest(unittest.TestCase): s.send(u"こにゃにゃちは、世界") result = s.recv() self.assertEqual(result, "こにゃにゃちは、世界") + self.assertRaises(ValueError, s.send_close, -1, "") s.close() @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @@ -426,31 +381,24 @@ class WebSocketTest(unittest.TestCase): s.close() @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - @unittest.skipUnless(TEST_SECURE_WS, "wss://echo.websocket.org doesn't work well.") def testSecureWebSocket(self): - if 1: - import ssl - s = ws.create_connection("wss://echo.websocket.org/") - self.assertNotEqual(s, None) - self.assertTrue(isinstance(s.sock, ssl.SSLSocket)) - s.send("Hello, World") - result = s.recv() - self.assertEqual(result, "Hello, World") - s.send(u"こにゃにゃちは、世界") - result = s.recv() - self.assertEqual(result, "こにゃにゃちは、世界") - s.close() - #except: - # pass + import ssl + s = ws.create_connection("wss://api.bitfinex.com/ws/2") + self.assertNotEqual(s, None) + self.assertTrue(isinstance(s.sock, ssl.SSLSocket)) + self.assertEqual(s.getstatus(), 101) + self.assertNotEqual(s.getheaders(), None) + s.close() @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testWebSocketWihtCustomHeader(self): + def testWebSocketWithCustomHeader(self): s = ws.create_connection("ws://echo.websocket.org/", headers={"User-Agent": "PythonWebsocketClient"}) self.assertNotEqual(s, None) s.send("Hello, World") result = s.recv() self.assertEqual(result, "Hello, World") + self.assertRaises(ValueError, s.close, -1, "") s.close() @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @@ -461,87 +409,6 @@ class WebSocketTest(unittest.TestCase): self.assertRaises(ws.WebSocketConnectionClosedException, s.send, "Hello") self.assertRaises(ws.WebSocketConnectionClosedException, s.recv) - def testNonce(self): - """ WebSocket key should be a random 16-byte nonce. - """ - key = _create_sec_websocket_key() - nonce = base64decode(key.encode("utf-8")) - self.assertEqual(16, len(nonce)) - - -class WebSocketAppTest(unittest.TestCase): - - class NotSetYet(object): - """ A marker class for signalling that a value hasn't been set yet. - """ - - def setUp(self): - ws.enableTrace(TRACEABLE) - - WebSocketAppTest.keep_running_open = WebSocketAppTest.NotSetYet() - WebSocketAppTest.keep_running_close = WebSocketAppTest.NotSetYet() - WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet() - - def tearDown(self): - WebSocketAppTest.keep_running_open = WebSocketAppTest.NotSetYet() - WebSocketAppTest.keep_running_close = WebSocketAppTest.NotSetYet() - WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet() - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testKeepRunning(self): - """ A WebSocketApp should keep running as long as its self.keep_running - is not False (in the boolean context). - """ - - def on_open(self, *args, **kwargs): - """ Set the keep_running flag for later inspection and immediately - close the connection. - """ - WebSocketAppTest.keep_running_open = self.keep_running - - 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 - - app = ws.WebSocketApp('ws://echo.websocket.org/', on_open=on_open, on_close=on_close) - app.run_forever() - - # if numpy is installed, this assertion fail - # self.assertFalse(isinstance(WebSocketAppTest.keep_running_open, - # WebSocketAppTest.NotSetYet)) - - # self.assertFalse(isinstance(WebSocketAppTest.keep_running_close, - # WebSocketAppTest.NotSetYet)) - - # self.assertEqual(True, WebSocketAppTest.keep_running_open) - # self.assertEqual(False, WebSocketAppTest.keep_running_close) - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def testSockMaskKey(self): - """ A WebSocketApp should forward the received mask_key function down - to the actual socket. - """ - - def my_mask_key_func(): - pass - - def on_open(self, *args, **kwargs): - """ Set the value so the test can use it later on and immediately - close the connection. - """ - WebSocketAppTest.get_mask_key_id = id(self.get_mask_key) - self.close() - - app = ws.WebSocketApp('ws://echo.websocket.org/', on_open=on_open, get_mask_key=my_mask_key_func) - app.run_forever() - - # if numpu is installed, this assertion fail - # Note: We can't use 'is' for comparing the functions directly, need to use 'id'. - # self.assertEqual(WebSocketAppTest.get_mask_key_id, id(my_mask_key_func)) - class SockOptTest(unittest.TestCase): @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") @@ -562,101 +429,5 @@ class UtilsTest(unittest.TestCase): self.assertEqual(state, True) -class ProxyInfoTest(unittest.TestCase): - def setUp(self): - self.http_proxy = os.environ.get("http_proxy", None) - self.https_proxy = os.environ.get("https_proxy", None) - if "http_proxy" in os.environ: - del os.environ["http_proxy"] - if "https_proxy" in os.environ: - del os.environ["https_proxy"] - - def tearDown(self): - if self.http_proxy: - os.environ["http_proxy"] = self.http_proxy - elif "http_proxy" in os.environ: - del os.environ["http_proxy"] - - if self.https_proxy: - os.environ["https_proxy"] = self.https_proxy - elif "https_proxy" in os.environ: - del os.environ["https_proxy"] - - def testProxyFromArgs(self): - self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost"), ("localhost", 0, None)) - self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_port=3128), ("localhost", 3128, None)) - self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost"), ("localhost", 0, None)) - self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128), ("localhost", 3128, None)) - - self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_auth=("a", "b")), - ("localhost", 0, ("a", "b"))) - self.assertEqual(get_proxy_info("echo.websocket.org", False, proxy_host="localhost", proxy_port=3128, proxy_auth=("a", "b")), - ("localhost", 3128, ("a", "b"))) - self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_auth=("a", "b")), - ("localhost", 0, ("a", "b"))) - self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128, proxy_auth=("a", "b")), - ("localhost", 3128, ("a", "b"))) - - self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128, no_proxy=["example.com"], proxy_auth=("a", "b")), - ("localhost", 3128, ("a", "b"))) - self.assertEqual(get_proxy_info("echo.websocket.org", True, proxy_host="localhost", proxy_port=3128, no_proxy=["echo.websocket.org"], proxy_auth=("a", "b")), - (None, 0, None)) - - def testProxyFromEnv(self): - os.environ["http_proxy"] = "http://localhost/" - self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, None)) - os.environ["http_proxy"] = "http://localhost:3128/" - self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, None)) - - os.environ["http_proxy"] = "http://localhost/" - os.environ["https_proxy"] = "http://localhost2/" - self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, None)) - os.environ["http_proxy"] = "http://localhost:3128/" - os.environ["https_proxy"] = "http://localhost2:3128/" - self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, None)) - - os.environ["http_proxy"] = "http://localhost/" - os.environ["https_proxy"] = "http://localhost2/" - self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", None, None)) - os.environ["http_proxy"] = "http://localhost:3128/" - os.environ["https_proxy"] = "http://localhost2:3128/" - self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", 3128, None)) - - - os.environ["http_proxy"] = "http://a:b@localhost/" - self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, ("a", "b"))) - os.environ["http_proxy"] = "http://a:b@localhost:3128/" - self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, ("a", "b"))) - - os.environ["http_proxy"] = "http://a:b@localhost/" - os.environ["https_proxy"] = "http://a:b@localhost2/" - self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", None, ("a", "b"))) - os.environ["http_proxy"] = "http://a:b@localhost:3128/" - os.environ["https_proxy"] = "http://a:b@localhost2:3128/" - self.assertEqual(get_proxy_info("echo.websocket.org", False), ("localhost", 3128, ("a", "b"))) - - os.environ["http_proxy"] = "http://a:b@localhost/" - os.environ["https_proxy"] = "http://a:b@localhost2/" - self.assertEqual(get_proxy_info("echo.websocket.org", True), ("localhost2", None, ("a", "b"))) - os.environ["http_proxy"] = "http://a:b@localhost:3128/" - 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://a:b@localhost/" - os.environ["https_proxy"] = "http://a:b@localhost2/" - os.environ["no_proxy"] = "example1.com,example2.com" - self.assertEqual(get_proxy_info("example.1.com", True), ("localhost2", None, ("a", "b"))) - os.environ["http_proxy"] = "http://a:b@localhost:3128/" - os.environ["https_proxy"] = "http://a:b@localhost2:3128/" - os.environ["no_proxy"] = "example1.com,example2.com, echo.websocket.org" - self.assertEqual(get_proxy_info("echo.websocket.org", True), (None, 0, None)) - - os.environ["http_proxy"] = "http://a:b@localhost:3128/" - os.environ["https_proxy"] = "http://a:b@localhost2:3128/" - os.environ["no_proxy"] = "127.0.0.0/8, 192.168.0.0/16" - self.assertEqual(get_proxy_info("127.0.0.1", False), (None, 0, None)) - self.assertEqual(get_proxy_info("192.168.1.1", False), (None, 0, None)) - - if __name__ == "__main__": unittest.main()