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)
|
||||
- add proxy support to wsdump.py (#194)
|
||||
- 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
|
||||
|
||||
|
@@ -62,7 +62,7 @@ Low Level API example::
|
||||
print "Sending 'Hello, World'..."
|
||||
ws.send("Hello, World")
|
||||
print "Sent"
|
||||
print "Reeiving..."
|
||||
print "Receiving..."
|
||||
result = ws.recv()
|
||||
print "Received '%s'" % result
|
||||
ws.close()
|
||||
|
@@ -48,7 +48,8 @@ class WebSocketApp(object):
|
||||
on_close=None, on_ping=None, on_pong=None,
|
||||
on_cont_message=None,
|
||||
keep_running=True, get_mask_key=None, cookie=None,
|
||||
subprotocols=None):
|
||||
subprotocols=None,
|
||||
on_data=None):
|
||||
"""
|
||||
url: websocket url.
|
||||
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 3rd arugment is continue flag. if 0, the data continue
|
||||
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
|
||||
should keep running, defaults to True
|
||||
get_mask_key: a callable to produce new mask keys,
|
||||
@@ -82,6 +91,7 @@ class WebSocketApp(object):
|
||||
self.cookie = cookie
|
||||
self.on_open = on_open
|
||||
self.on_message = on_message
|
||||
self.on_data = on_data
|
||||
self.on_error = on_error
|
||||
self.on_close = on_close
|
||||
self.on_ping = on_ping
|
||||
@@ -192,11 +202,13 @@ class WebSocketApp(object):
|
||||
elif op_code == ABNF.OPCODE_PONG:
|
||||
self._callback(self.on_pong, frame.data)
|
||||
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)
|
||||
else:
|
||||
data = frame.data
|
||||
if six.PY3 and frame.opcode == ABNF.OPCODE_TEXT:
|
||||
data = data.decode("utf-8")
|
||||
self._callback(self.on_data, data, frame.opcode, True)
|
||||
self._callback(self.on_message, data)
|
||||
except Exception as e:
|
||||
self._callback(self.on_error, e)
|
||||
|
@@ -422,13 +422,16 @@ class WebSocket(object):
|
||||
self.connected = False
|
||||
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
|
||||
|
||||
status: status code to send. see STATUS_XXX.
|
||||
|
||||
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 status < 0 or status >= ABNF.LENGTH_16:
|
||||
@@ -437,8 +440,8 @@ class WebSocket(object):
|
||||
try:
|
||||
self.connected = False
|
||||
self.send(struct.pack('!H', status) + reason, ABNF.OPCODE_CLOSE)
|
||||
timeout = self.sock.gettimeout()
|
||||
self.sock.settimeout(3)
|
||||
sock_timeout = self.sock.gettimeout()
|
||||
self.sock.settimeout(timeout)
|
||||
try:
|
||||
frame = self.recv_frame()
|
||||
if isEnabledForError():
|
||||
@@ -447,7 +450,7 @@ class WebSocket(object):
|
||||
error("close status: " + repr(recv_status))
|
||||
except:
|
||||
pass
|
||||
self.sock.settimeout(timeout)
|
||||
self.sock.settimeout(sock_timeout)
|
||||
self.sock.shutdown(socket.SHUT_RDWR)
|
||||
except:
|
||||
pass
|
||||
|
@@ -31,18 +31,21 @@ class WebSocketException(Exception):
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class WebSocketProtocolException(WebSocketException):
|
||||
"""
|
||||
If the webscoket protocol is invalid, this exception will be raised.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class WebSocketPayloadException(WebSocketException):
|
||||
"""
|
||||
If the webscoket payload is invalid, this exception will be raised.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class WebSocketConnectionClosedException(WebSocketException):
|
||||
"""
|
||||
If remote host closed the connection or some network error happened,
|
||||
@@ -50,12 +53,14 @@ class WebSocketConnectionClosedException(WebSocketException):
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class WebSocketTimeoutException(WebSocketException):
|
||||
"""
|
||||
WebSocketTimeoutException will be raised at socket timeout during read/write data.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class WebSocketProxyException(WebSocketException):
|
||||
"""
|
||||
WebSocketProxyException will be raised when proxy error occured.
|
||||
@@ -63,3 +68,10 @@ class WebSocketProxyException(WebSocketException):
|
||||
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):
|
||||
status, resp_headers = read_headers(sock)
|
||||
if status != success_status:
|
||||
raise WebSocketException("Handshake status %d" % status)
|
||||
raise WebSocketBadStatusException("Handshake status %d", status)
|
||||
return status, resp_headers
|
||||
|
||||
_HEADERS_TO_CHECK = {
|
||||
|
@@ -132,6 +132,9 @@ def _wrap_sni_socket(sock, sslopt, hostname, check_hostname):
|
||||
context.check_hostname = check_hostname
|
||||
if 'ciphers' in sslopt:
|
||||
context.set_ciphers(sslopt['ciphers'])
|
||||
if 'cert_chain' in sslopt :
|
||||
certfile,keyfile,password = sslopt['cert_chain']
|
||||
context.load_cert_chain(certfile, keyfile, password)
|
||||
|
||||
return context.wrap_socket(
|
||||
sock,
|
||||
@@ -183,7 +186,7 @@ def _tunnel(sock, host, port, auth):
|
||||
|
||||
if status != 200:
|
||||
raise WebSocketProxyException(
|
||||
"failed CONNECT via proxy status: %r" + status)
|
||||
"failed CONNECT via proxy status: %r" % status)
|
||||
|
||||
return sock
|
||||
|
||||
|
Reference in New Issue
Block a user