Merge remote-tracking branch 'origin/master' into tweaks
* origin/master: fixed #192 introduced close timeout fixed #199 WebSocketBadStatusException for handshake error fixed #198 introduce on_data callback to pass data type. documentation add space. Typo fix in README.rst Fix string formatting in exception add support for ssl cert chains to support client certs
This commit is contained in:
@@ -7,6 +7,12 @@ ChangeLog
|
|||||||
- fixed timeout+ssl error handling bug on python 2.7.10 (#190)
|
- fixed timeout+ssl error handling bug on python 2.7.10 (#190)
|
||||||
- add proxy support to wsdump.py (#194)
|
- add proxy support to wsdump.py (#194)
|
||||||
- use wsaccel if available (#193)
|
- use wsaccel if available (#193)
|
||||||
|
- add support for ssl cert chains to support client certs (#195)
|
||||||
|
- fix string formatting in exception (#196)
|
||||||
|
- fix typo in README.rst (#197)
|
||||||
|
- introduce on_data callback to pass data type. (#198)
|
||||||
|
- WebSocketBadStatusException for Handshake error (#199)
|
||||||
|
- set close timeout (#192)
|
||||||
|
|
||||||
- 0.32.0
|
- 0.32.0
|
||||||
|
|
||||||
|
@@ -62,7 +62,7 @@ Low Level API example::
|
|||||||
print "Sending 'Hello, World'..."
|
print "Sending 'Hello, World'..."
|
||||||
ws.send("Hello, World")
|
ws.send("Hello, World")
|
||||||
print "Sent"
|
print "Sent"
|
||||||
print "Reeiving..."
|
print "Receiving..."
|
||||||
result = ws.recv()
|
result = ws.recv()
|
||||||
print "Received '%s'" % result
|
print "Received '%s'" % result
|
||||||
ws.close()
|
ws.close()
|
||||||
|
@@ -48,7 +48,8 @@ class WebSocketApp(object):
|
|||||||
on_close=None, on_ping=None, on_pong=None,
|
on_close=None, on_ping=None, on_pong=None,
|
||||||
on_cont_message=None,
|
on_cont_message=None,
|
||||||
keep_running=True, get_mask_key=None, cookie=None,
|
keep_running=True, get_mask_key=None, cookie=None,
|
||||||
subprotocols=None):
|
subprotocols=None,
|
||||||
|
on_data=None):
|
||||||
"""
|
"""
|
||||||
url: websocket url.
|
url: websocket url.
|
||||||
header: custom header for websocket handshake.
|
header: custom header for websocket handshake.
|
||||||
@@ -71,6 +72,14 @@ class WebSocketApp(object):
|
|||||||
The passing 2nd arugment is utf-8 string which we get from the server.
|
The passing 2nd arugment is utf-8 string which we get from the server.
|
||||||
The 3rd arugment is continue flag. if 0, the data continue
|
The 3rd arugment is continue flag. if 0, the data continue
|
||||||
to next frame data
|
to next frame data
|
||||||
|
on_data: callback object which is called when a message recieved.
|
||||||
|
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 arugment is this class object.
|
||||||
|
The passing 2nd arugment 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 4rd arugment is continue flag. if 0, the data continue
|
||||||
keep_running: a boolean flag indicating whether the app's main loop
|
keep_running: a boolean flag indicating whether the app's main loop
|
||||||
should keep running, defaults to True
|
should keep running, defaults to True
|
||||||
get_mask_key: a callable to produce new mask keys,
|
get_mask_key: a callable to produce new mask keys,
|
||||||
@@ -82,6 +91,7 @@ class WebSocketApp(object):
|
|||||||
self.cookie = cookie
|
self.cookie = cookie
|
||||||
self.on_open = on_open
|
self.on_open = on_open
|
||||||
self.on_message = on_message
|
self.on_message = on_message
|
||||||
|
self.on_data = on_data
|
||||||
self.on_error = on_error
|
self.on_error = on_error
|
||||||
self.on_close = on_close
|
self.on_close = on_close
|
||||||
self.on_ping = on_ping
|
self.on_ping = on_ping
|
||||||
@@ -192,11 +202,13 @@ class WebSocketApp(object):
|
|||||||
elif op_code == ABNF.OPCODE_PONG:
|
elif op_code == ABNF.OPCODE_PONG:
|
||||||
self._callback(self.on_pong, frame.data)
|
self._callback(self.on_pong, frame.data)
|
||||||
elif op_code == ABNF.OPCODE_CONT and self.on_cont_message:
|
elif op_code == ABNF.OPCODE_CONT and self.on_cont_message:
|
||||||
|
self._callback(self.on_data, data, frame.opcode, frame.fin)
|
||||||
self._callback(self.on_cont_message, frame.data, frame.fin)
|
self._callback(self.on_cont_message, frame.data, frame.fin)
|
||||||
else:
|
else:
|
||||||
data = frame.data
|
data = frame.data
|
||||||
if six.PY3 and frame.opcode == ABNF.OPCODE_TEXT:
|
if six.PY3 and frame.opcode == ABNF.OPCODE_TEXT:
|
||||||
data = data.decode("utf-8")
|
data = data.decode("utf-8")
|
||||||
|
self._callback(self.on_data, data, frame.opcode, True)
|
||||||
self._callback(self.on_message, data)
|
self._callback(self.on_message, data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._callback(self.on_error, e)
|
self._callback(self.on_error, e)
|
||||||
|
@@ -422,13 +422,16 @@ class WebSocket(object):
|
|||||||
self.connected = False
|
self.connected = False
|
||||||
self.send(struct.pack('!H', status) + reason, ABNF.OPCODE_CLOSE)
|
self.send(struct.pack('!H', status) + reason, ABNF.OPCODE_CLOSE)
|
||||||
|
|
||||||
def close(self, status=STATUS_NORMAL, reason=six.b("")):
|
def close(self, status=STATUS_NORMAL, reason=six.b(""), timeout=3):
|
||||||
"""
|
"""
|
||||||
Close Websocket object
|
Close Websocket object
|
||||||
|
|
||||||
status: status code to send. see STATUS_XXX.
|
status: status code to send. see STATUS_XXX.
|
||||||
|
|
||||||
reason: the reason to close. This must be string.
|
reason: the reason to close. This must be string.
|
||||||
|
|
||||||
|
timeout: timeout until recieve a close frame.
|
||||||
|
If None, it will wait forever until recieve a close frame.
|
||||||
"""
|
"""
|
||||||
if self.connected:
|
if self.connected:
|
||||||
if status < 0 or status >= ABNF.LENGTH_16:
|
if status < 0 or status >= ABNF.LENGTH_16:
|
||||||
@@ -437,8 +440,8 @@ class WebSocket(object):
|
|||||||
try:
|
try:
|
||||||
self.connected = False
|
self.connected = False
|
||||||
self.send(struct.pack('!H', status) + reason, ABNF.OPCODE_CLOSE)
|
self.send(struct.pack('!H', status) + reason, ABNF.OPCODE_CLOSE)
|
||||||
timeout = self.sock.gettimeout()
|
sock_timeout = self.sock.gettimeout()
|
||||||
self.sock.settimeout(3)
|
self.sock.settimeout(timeout)
|
||||||
try:
|
try:
|
||||||
frame = self.recv_frame()
|
frame = self.recv_frame()
|
||||||
if isEnabledForError():
|
if isEnabledForError():
|
||||||
@@ -447,7 +450,7 @@ class WebSocket(object):
|
|||||||
error("close status: " + repr(recv_status))
|
error("close status: " + repr(recv_status))
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
self.sock.settimeout(timeout)
|
self.sock.settimeout(sock_timeout)
|
||||||
self.sock.shutdown(socket.SHUT_RDWR)
|
self.sock.shutdown(socket.SHUT_RDWR)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
@@ -31,18 +31,21 @@ class WebSocketException(Exception):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class WebSocketProtocolException(WebSocketException):
|
class WebSocketProtocolException(WebSocketException):
|
||||||
"""
|
"""
|
||||||
If the webscoket protocol is invalid, this exception will be raised.
|
If the webscoket protocol is invalid, this exception will be raised.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class WebSocketPayloadException(WebSocketException):
|
class WebSocketPayloadException(WebSocketException):
|
||||||
"""
|
"""
|
||||||
If the webscoket payload is invalid, this exception will be raised.
|
If the webscoket payload is invalid, this exception will be raised.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class WebSocketConnectionClosedException(WebSocketException):
|
class WebSocketConnectionClosedException(WebSocketException):
|
||||||
"""
|
"""
|
||||||
If remote host closed the connection or some network error happened,
|
If remote host closed the connection or some network error happened,
|
||||||
@@ -50,12 +53,14 @@ class WebSocketConnectionClosedException(WebSocketException):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class WebSocketTimeoutException(WebSocketException):
|
class WebSocketTimeoutException(WebSocketException):
|
||||||
"""
|
"""
|
||||||
WebSocketTimeoutException will be raised at socket timeout during read/write data.
|
WebSocketTimeoutException will be raised at socket timeout during read/write data.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class WebSocketProxyException(WebSocketException):
|
class WebSocketProxyException(WebSocketException):
|
||||||
"""
|
"""
|
||||||
WebSocketProxyException will be raised when proxy error occured.
|
WebSocketProxyException will be raised when proxy error occured.
|
||||||
@@ -63,3 +68,10 @@ class WebSocketProxyException(WebSocketException):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class WebSocketBadStatusException(WebSocketException):
|
||||||
|
"""
|
||||||
|
WebSocketBadStatusException will be raised when we get bad handshake status code.
|
||||||
|
"""
|
||||||
|
def __init__(self, message, status_code):
|
||||||
|
super(WebSocketBadStatusException, self).__init__(message % status_code)
|
||||||
|
self.status_code = status_code
|
||||||
|
@@ -108,7 +108,7 @@ def _get_handshake_headers(resource, host, port, options):
|
|||||||
def _get_resp_headers(sock, success_status=101):
|
def _get_resp_headers(sock, success_status=101):
|
||||||
status, resp_headers = read_headers(sock)
|
status, resp_headers = read_headers(sock)
|
||||||
if status != success_status:
|
if status != success_status:
|
||||||
raise WebSocketException("Handshake status %d" % status)
|
raise WebSocketBadStatusException("Handshake status %d", status)
|
||||||
return status, resp_headers
|
return status, resp_headers
|
||||||
|
|
||||||
_HEADERS_TO_CHECK = {
|
_HEADERS_TO_CHECK = {
|
||||||
|
@@ -132,6 +132,9 @@ def _wrap_sni_socket(sock, sslopt, hostname, check_hostname):
|
|||||||
context.check_hostname = check_hostname
|
context.check_hostname = check_hostname
|
||||||
if 'ciphers' in sslopt:
|
if 'ciphers' in sslopt:
|
||||||
context.set_ciphers(sslopt['ciphers'])
|
context.set_ciphers(sslopt['ciphers'])
|
||||||
|
if 'cert_chain' in sslopt :
|
||||||
|
certfile,keyfile,password = sslopt['cert_chain']
|
||||||
|
context.load_cert_chain(certfile, keyfile, password)
|
||||||
|
|
||||||
return context.wrap_socket(
|
return context.wrap_socket(
|
||||||
sock,
|
sock,
|
||||||
@@ -183,7 +186,7 @@ def _tunnel(sock, host, port, auth):
|
|||||||
|
|
||||||
if status != 200:
|
if status != 200:
|
||||||
raise WebSocketProxyException(
|
raise WebSocketProxyException(
|
||||||
"failed CONNECT via proxy status: %r" + status)
|
"failed CONNECT via proxy status: %r" % status)
|
||||||
|
|
||||||
return sock
|
return sock
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user