summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--ChangeLog8
-rwxr-xr-xbin/wsdump.py24
-rw-r--r--setup.py2
-rw-r--r--websocket/__init__.py2
-rw-r--r--websocket/_app.py15
-rw-r--r--websocket/_core.py3
-rw-r--r--websocket/_exceptions.py1
-rw-r--r--websocket/_handshake.py16
-rw-r--r--websocket/_http.py10
-rw-r--r--websocket/_logging.py6
-rw-r--r--websocket/_socket.py10
-rw-r--r--websocket/_ssl_compat.py2
-rw-r--r--websocket/_url.py3
-rw-r--r--websocket/_utils.py1
-rw-r--r--websocket/tests/test_websocket.py3
16 files changed, 81 insertions, 27 deletions
diff --git a/.gitignore b/.gitignore
index ebf5448..f75aba2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,5 @@ dist
.cache/
compliance/reports/
+.eggs
+.vscode \ No newline at end of file
diff --git a/ChangeLog b/ChangeLog
index b85f05d..d84d27c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,14 @@
ChangeLog
============
+- 0.57.0
+ - wsdump: Fix --headers option (#589)
+ - Fix getting 400 bad request with long proxy authorization string (#587)
+
+- 0.56.0
+
+ - Don't retry sockets with timeout set to 0(#536)
+
- 0.55.0
- Add response headers in WebSocketBadStatusException (#501)
diff --git a/bin/wsdump.py b/bin/wsdump.py
index bc07246..d16d361 100755
--- a/bin/wsdump.py
+++ b/bin/wsdump.py
@@ -6,6 +6,8 @@ import sys
import threading
import time
import ssl
+import gzip
+import zlib
import six
from six.moves.urllib.parse import urlparse
@@ -130,7 +132,7 @@ def main():
if args.nocert:
opts = {"cert_reqs": ssl.CERT_NONE, "check_hostname": False}
if args.headers:
- options['header'] = map(str.strip, args.headers.split(','))
+ options['header'] = list(map(str.strip, args.headers.split(',')))
ws = websocket.create_connection(args.url, sslopt=opts, **options)
if args.raw:
console = NonInteractive()
@@ -162,10 +164,24 @@ def main():
msg = None
if six.PY3 and opcode == websocket.ABNF.OPCODE_TEXT and isinstance(data, bytes):
data = str(data, "utf-8")
- if not args.verbose and opcode in OPCODE_DATA:
- msg = data
- elif args.verbose:
+ if isinstance(data, bytes) and len(data)>2 and data[:2] == b'\037\213': # gzip magick
+ try:
+ data = "[gzip] " + str(gzip.decompress(data), "utf-8")
+ except:
+ pass
+ elif isinstance(data, bytes):
+ try:
+ data = "[zlib] " + str(zlib.decompress(data, -zlib.MAX_WBITS), "utf-8")
+ except:
+ pass
+
+ if isinstance(data, bytes):
+ data = repr(data)
+
+ if args.verbose:
msg = "%s: %s" % (websocket.ABNF.OPCODE_MAP.get(opcode), data)
+ else:
+ msg = data
if msg is not None:
if args.timings:
diff --git a/setup.py b/setup.py
index 860f8b9..2a4254b 100644
--- a/setup.py
+++ b/setup.py
@@ -3,7 +3,7 @@ import sys
from setuptools import setup
import pkg_resources
-VERSION = "0.55.0"
+VERSION = "0.56.0"
NAME = "websocket_client"
install_requires = ["six"]
diff --git a/websocket/__init__.py b/websocket/__init__.py
index 7c3154d..605f76c 100644
--- a/websocket/__init__.py
+++ b/websocket/__init__.py
@@ -26,4 +26,4 @@ from ._exceptions import *
from ._logging import *
from ._socket import *
-__version__ = "0.55.0"
+__version__ = "0.57.0"
diff --git a/websocket/_app.py b/websocket/_app.py
index 81aa1fc..e4e9f99 100644
--- a/websocket/_app.py
+++ b/websocket/_app.py
@@ -42,11 +42,11 @@ __all__ = ["WebSocketApp"]
class 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)
if r:
@@ -54,13 +54,13 @@ class Dispatcher:
break
check_callback()
-class SSLDispacther:
+class 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,6 +75,7 @@ class SSLDispacther:
r, w, e = select.select((sock, ), (), (), self.ping_timeout)
return r
+
class WebSocketApp(object):
"""
Higher level of APIs are provided.
@@ -179,7 +180,7 @@ class WebSocketApp(object):
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.
@@ -314,7 +315,7 @@ 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)
diff --git a/websocket/_core.py b/websocket/_core.py
index 0f914c2..418aafc 100644
--- a/websocket/_core.py
+++ b/websocket/_core.py
@@ -271,7 +271,8 @@ class WebSocket(object):
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:
diff --git a/websocket/_exceptions.py b/websocket/_exceptions.py
index b7a61d3..2070790 100644
--- a/websocket/_exceptions.py
+++ b/websocket/_exceptions.py
@@ -80,6 +80,7 @@ class WebSocketBadStatusException(WebSocketException):
self.status_code = status_code
self.resp_headers = resp_headers
+
class WebSocketAddressException(WebSocketException):
"""
If the websocket address info cannot be found, this exception will be raised.
diff --git a/websocket/_handshake.py b/websocket/_handshake.py
index c4bcb69..7476a07 100644
--- a/websocket/_handshake.py
+++ b/websocket/_handshake.py
@@ -55,7 +55,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 +86,7 @@ def handshake(sock, hostname, port, resource, **options):
return handshake_response(status, resp, subproto)
+
def _pack_hostname(hostname):
# IPv6 address
if ':' in hostname:
@@ -95,14 +97,12 @@ def _pack_hostname(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:
@@ -126,6 +126,11 @@ def _get_handshake_headers(resource, host, port, options):
if not 'header' in options or 'Sec-WebSocket-Version' not in options['header']:
headers.append("Sec-WebSocket-Version: %s" % VERSION)
+ if not 'connection' in options or options['connection'] is None:
+ headers.append('Connection: upgrade')
+ else:
+ headers.append(options['connection'])
+
subprotocols = options.get("subprotocols")
if subprotocols:
headers.append("Sec-WebSocket-Protocol: %s" % ",".join(subprotocols))
@@ -154,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, resp_headers)
return status, resp_headers
+
_HEADERS_TO_CHECK = {
"upgrade": "websocket",
"connection": "upgrade",
diff --git a/websocket/_http.py b/websocket/_http.py
index 5b9a26d..a8777de 100644
--- a/websocket/_http.py
+++ b/websocket/_http.py
@@ -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)
@@ -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
@@ -276,7 +280,7 @@ def _tunnel(sock, host, port, auth):
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)
diff --git a/websocket/_logging.py b/websocket/_logging.py
index 70a6271..c947778 100644
--- a/websocket/_logging.py
+++ b/websocket/_logging.py
@@ -34,7 +34,7 @@ _logger.addHandler(NullHandler())
_traceEnabled = False
__all__ = ["enableTrace", "dump", "error", "warning", "debug", "trace",
- "isEnabledForError", "isEnabledForDebug"]
+ "isEnabledForError", "isEnabledForDebug", "isEnabledForTrace"]
def enableTrace(traceable, handler = logging.StreamHandler()):
@@ -49,7 +49,6 @@ def enableTrace(traceable, handler = logging.StreamHandler()):
_logger.addHandler(handler)
_logger.setLevel(logging.DEBUG)
-
def dump(title, message):
if _traceEnabled:
_logger.debug("--- " + title + " ---")
@@ -80,3 +79,6 @@ def isEnabledForError():
def isEnabledForDebug():
return _logger.isEnabledFor(logging.DEBUG)
+
+def isEnabledForTrace():
+ return _traceEnabled
diff --git a/websocket/_socket.py b/websocket/_socket.py
index d811c21..7be3913 100644
--- a/websocket/_socket.py
+++ b/websocket/_socket.py
@@ -96,7 +96,10 @@ def recv(sock, bufsize):
return sock.recv(bufsize)
try:
- bytes_ = _recv()
+ if sock.gettimeout() == 0:
+ bytes_ = sock.recv(bufsize)
+ else:
+ bytes_ = _recv()
except socket.timeout as e:
message = extract_err_message(e)
raise WebSocketTimeoutException(message)
@@ -148,7 +151,10 @@ def send(sock, data):
return sock.send(data)
try:
- return _send()
+ 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/websocket/_ssl_compat.py b/websocket/_ssl_compat.py
index 5b3c413..96cd173 100644
--- a/websocket/_ssl_compat.py
+++ b/websocket/_ssl_compat.py
@@ -49,4 +49,6 @@ except ImportError:
class SSLWantWriteError(Exception):
pass
+ ssl = lambda: None
+
HAVE_SSL = False
diff --git a/websocket/_url.py b/websocket/_url.py
index ae46d6c..a394fc3 100644
--- a/websocket/_url.py
+++ b/websocket/_url.py
@@ -103,7 +103,8 @@ def _is_address_in_network(ip, net):
def _is_no_proxy_host(hostname, no_proxy):
if not no_proxy:
v = os.environ.get("no_proxy", "").replace(" ", "")
- no_proxy = v.split(",")
+ if v:
+ no_proxy = v.split(",")
if not no_proxy:
no_proxy = DEFAULT_NO_PROXY_HOST
diff --git a/websocket/_utils.py b/websocket/_utils.py
index 8eddabf..32ee12e 100644
--- a/websocket/_utils.py
+++ b/websocket/_utils.py
@@ -32,6 +32,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.
diff --git a/websocket/tests/test_websocket.py b/websocket/tests/test_websocket.py
index f49a893..8b131bb 100644
--- a/websocket/tests/test_websocket.py
+++ b/websocket/tests/test_websocket.py
@@ -55,6 +55,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)