diff --git a/doc/modules/websocket.rst b/doc/modules/websocket.rst index 7405e8c..b42a25f 100644 --- a/doc/modules/websocket.rst +++ b/doc/modules/websocket.rst @@ -2,7 +2,7 @@ ===================================== This module provides a simple way to create a `websocket -` server. It works with a few +`_ server. It works with a few tweaks in the :mod:`~eventlet.wsgi` module that allow websockets to coexist with other WSGI applications. diff --git a/eventlet/websocket.py b/eventlet/websocket.py index b2f783f..194e984 100644 --- a/eventlet/websocket.py +++ b/eventlet/websocket.py @@ -81,7 +81,7 @@ class WebSocketWSGI(object): environ.get('PATH_INFO') ) qs = environ.get('QUERY_STRING') - if qs: + if qs is not None: location += '?' + qs if self.protocol_version == 75: handshake_reply = ("HTTP/1.1 101 Web Socket Protocol Handshake\r\n" @@ -212,9 +212,11 @@ class WebSocket(object): return msgs def send(self, message): - """Send a message to the browser. *message* should be - convertable to a string; unicode objects should be encodable - as utf-8.""" + """Send a message to the browser. + + *message* should be convertable to a string; unicode objects should be + encodable as utf-8. Raises socket.error with errno of 32 + (broken pipe) if the socket has already been closed by the client.""" packed = self._pack_message(message) # if two greenthreads are trying to send at the same time # on the same socket, sendlock prevents interleaving and corruption @@ -225,8 +227,12 @@ class WebSocket(object): self._sendlock.release() def wait(self): - """Waits for and deserializes messages. Returns a single - message; the oldest not yet processed.""" + """Waits for and deserializes messages. + + Returns a single message; the oldest not yet processed. If the client + has already closed the connection, returns None. This is different + from normal socket behavior because the empty string is a valid + websocket message.""" while not self._msgs: # Websocket might be closed already. if self.websocket_closed: diff --git a/eventlet/wsgi.py b/eventlet/wsgi.py index a5ea4ff..54adf02 100644 --- a/eventlet/wsgi.py +++ b/eventlet/wsgi.py @@ -402,12 +402,10 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): env['REQUEST_METHOD'] = self.command env['SCRIPT_NAME'] = '' - if '?' in self.path: - path, query = self.path.split('?', 1) - else: - path, query = self.path, '' - env['PATH_INFO'] = urllib.unquote(path) - env['QUERY_STRING'] = query + pq = self.path.split('?', 1) + env['PATH_INFO'] = urllib.unquote(pq[0]) + if len(pq) > 1: + env['QUERY_STRING'] = pq[1] if self.headers.typeheader is None: env['CONTENT_TYPE'] = self.headers.type diff --git a/tests/websocket_test.py b/tests/websocket_test.py index 4e16ad4..f8bb45f 100644 --- a/tests/websocket_test.py +++ b/tests/websocket_test.py @@ -145,6 +145,58 @@ class TestWebSocket(_TestBase): 'Sec-WebSocket-Origin: http://localhost:%s' % self.port, 'Sec-WebSocket-Protocol: ws', 'Sec-WebSocket-Location: ws://localhost:%s/echo\r\n\r\n8jKS\'y:G*Co,Wxa-' % self.port])) + + + def test_query_string(self): + # verify that the query string comes out the other side unscathed + connect = [ + "GET /echo?query_string HTTP/1.1", + "Upgrade: WebSocket", + "Connection: Upgrade", + "Host: localhost:%s" % self.port, + "Origin: http://localhost:%s" % self.port, + "Sec-WebSocket-Protocol: ws", + "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5", + "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00", + ] + sock = eventlet.connect( + ('localhost', self.port)) + + sock.sendall('\r\n'.join(connect) + '\r\n\r\n^n:ds[4U') + result = sock.recv(1024) + self.assertEqual(result, + '\r\n'.join(['HTTP/1.1 101 WebSocket Protocol Handshake', + 'Upgrade: WebSocket', + 'Connection: Upgrade', + 'Sec-WebSocket-Origin: http://localhost:%s' % self.port, + 'Sec-WebSocket-Protocol: ws', + 'Sec-WebSocket-Location: ws://localhost:%s/echo?query_string\r\n\r\n8jKS\'y:G*Co,Wxa-' % self.port])) + + def test_empty_query_string(self): + # verify that a single trailing ? doesn't get nuked + connect = [ + "GET /echo? HTTP/1.1", + "Upgrade: WebSocket", + "Connection: Upgrade", + "Host: localhost:%s" % self.port, + "Origin: http://localhost:%s" % self.port, + "Sec-WebSocket-Protocol: ws", + "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5", + "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00", + ] + sock = eventlet.connect( + ('localhost', self.port)) + + sock.sendall('\r\n'.join(connect) + '\r\n\r\n^n:ds[4U') + result = sock.recv(1024) + self.assertEqual(result, + '\r\n'.join(['HTTP/1.1 101 WebSocket Protocol Handshake', + 'Upgrade: WebSocket', + 'Connection: Upgrade', + 'Sec-WebSocket-Origin: http://localhost:%s' % self.port, + 'Sec-WebSocket-Protocol: ws', + 'Sec-WebSocket-Location: ws://localhost:%s/echo?\r\n\r\n8jKS\'y:G*Co,Wxa-' % self.port])) + def test_sending_messages_to_websocket_75(self): connect = [