Implement closing frame detection/sending for new WebSockets.
This commit is contained in:
@@ -97,6 +97,8 @@ class WebSocketWSGI(object):
|
|||||||
except socket.error, e:
|
except socket.error, e:
|
||||||
if get_errno(e) not in ACCEPTABLE_CLIENT_ERRORS:
|
if get_errno(e) not in ACCEPTABLE_CLIENT_ERRORS:
|
||||||
raise
|
raise
|
||||||
|
# Make sure we send the closing frame
|
||||||
|
ws._send_closing_frame()
|
||||||
# use this undocumented feature of eventlet.wsgi to ensure that it
|
# use this undocumented feature of eventlet.wsgi to ensure that it
|
||||||
# doesn't barf on the fact that we didn't call start_response
|
# doesn't barf on the fact that we didn't call start_response
|
||||||
return wsgi.ALREADY_HANDLED
|
return wsgi.ALREADY_HANDLED
|
||||||
@@ -134,17 +136,20 @@ class WebSocket(object):
|
|||||||
The full WSGI environment for this request.
|
The full WSGI environment for this request.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, sock, environ):
|
def __init__(self, sock, environ, version=76):
|
||||||
"""
|
"""
|
||||||
:param socket: The eventlet socket
|
:param socket: The eventlet socket
|
||||||
:type socket: :class:`eventlet.greenio.GreenSocket`
|
:type socket: :class:`eventlet.greenio.GreenSocket`
|
||||||
:param environ: The wsgi environment
|
:param environ: The wsgi environment
|
||||||
|
:param version: The WebSocket spec version to follow (default is 76)
|
||||||
"""
|
"""
|
||||||
self.socket = sock
|
self.socket = sock
|
||||||
self.origin = environ.get('HTTP_ORIGIN')
|
self.origin = environ.get('HTTP_ORIGIN')
|
||||||
self.protocol = environ.get('HTTP_WEBSOCKET_PROTOCOL')
|
self.protocol = environ.get('HTTP_WEBSOCKET_PROTOCOL')
|
||||||
self.path = environ.get('PATH_INFO')
|
self.path = environ.get('PATH_INFO')
|
||||||
self.environ = environ
|
self.environ = environ
|
||||||
|
self.version = version
|
||||||
|
self.websocket_closed = False
|
||||||
self._buf = ""
|
self._buf = ""
|
||||||
self._msgs = collections.deque()
|
self._msgs = collections.deque()
|
||||||
self._sendlock = semaphore.Semaphore()
|
self._sendlock = semaphore.Semaphore()
|
||||||
@@ -173,12 +178,21 @@ class WebSocket(object):
|
|||||||
end_idx = 0
|
end_idx = 0
|
||||||
buf = self._buf
|
buf = self._buf
|
||||||
while buf:
|
while buf:
|
||||||
assert ord(buf[0]) == 0, "Don't understand how to parse this type of message: %r" % buf
|
frame_type = ord(buf[0])
|
||||||
end_idx = buf.find("\xFF")
|
if frame_type == 0:
|
||||||
if end_idx == -1: #pragma NO COVER
|
# Normal message.
|
||||||
|
end_idx = buf.find("\xFF")
|
||||||
|
if end_idx == -1: #pragma NO COVER
|
||||||
|
break
|
||||||
|
msgs.append(buf[1:end_idx].decode('utf-8', 'replace'))
|
||||||
|
buf = buf[end_idx+1:]
|
||||||
|
elif frame_type == 255:
|
||||||
|
# Closing handshake.
|
||||||
|
assert ord(buf[1]) == 0, "Unexpected closing handshake: %r" % buf
|
||||||
|
self.websocket_closed = True
|
||||||
break
|
break
|
||||||
msgs.append(buf[1:end_idx].decode('utf-8', 'replace'))
|
else:
|
||||||
buf = buf[end_idx+1:]
|
raise ValueError("Don't understand how to parse this type of message: %r" % buf)
|
||||||
self._buf = buf
|
self._buf = buf
|
||||||
return msgs
|
return msgs
|
||||||
|
|
||||||
@@ -199,18 +213,25 @@ class WebSocket(object):
|
|||||||
"""Waits for and deserializes messages. Returns a single
|
"""Waits for and deserializes messages. Returns a single
|
||||||
message; the oldest not yet processed."""
|
message; the oldest not yet processed."""
|
||||||
while not self._msgs:
|
while not self._msgs:
|
||||||
# no parsed messages, must mean buf needs more data
|
# no parsed messages, must mean buf needs more data (or it's closed)
|
||||||
delta = self.socket.recv(8096)
|
delta = self.socket.recv(8096)
|
||||||
if delta == '':
|
if delta == '' or self.websocket_closed:
|
||||||
return None
|
return None
|
||||||
self._buf += delta
|
self._buf += delta
|
||||||
msgs = self._parse_messages()
|
msgs = self._parse_messages()
|
||||||
self._msgs.extend(msgs)
|
self._msgs.extend(msgs)
|
||||||
return self._msgs.popleft()
|
return self._msgs.popleft()
|
||||||
|
|
||||||
|
def _send_closing_frame(self):
|
||||||
|
"""Sends the closing frame to the client, if required."""
|
||||||
|
if self.version == 76 and not self.websocket_closed:
|
||||||
|
self.socket.sendall("\xff\x00")
|
||||||
|
self.websocket_closed = True
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""Forcibly close the websocket; generally it is preferable to
|
"""Forcibly close the websocket; generally it is preferable to
|
||||||
return from the handler method."""
|
return from the handler method."""
|
||||||
|
self._send_closing_frame()
|
||||||
self.socket.shutdown(True)
|
self.socket.shutdown(True)
|
||||||
self.socket.close()
|
self.socket.close()
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user