Support for SSL websockets, which also happens to improve our SSL support in WSGI generally. Fixes #62.

This commit is contained in:
Ryan Williams
2010-09-16 11:42:03 -07:00
parent d5403dc963
commit dc70cc3812
7 changed files with 64 additions and 8 deletions

View File

@@ -300,7 +300,9 @@ class GreenSSLSocket(__ssl.SSLSocket):
do_handshake_on_connect=self.do_handshake_on_connect,
suppress_ragged_eofs=self.suppress_ragged_eofs)
return (new_ssl, addr)
def dup(self):
raise NotImplementedError("Can't dup an ssl object")
SSLSocket = GreenSSLSocket

View File

@@ -71,7 +71,11 @@ class WebSocketWSGI(object):
response = md5(key).digest()
# Start building the response
location = 'ws://%s%s%s' % (
scheme = 'ws'
if environ.get('wsgi.url_scheme') == 'https':
scheme = 'wss'
location = '%s://%s%s%s' % (
scheme,
environ.get('HTTP_HOST'),
environ.get('SCRIPT_NAME'),
environ.get('PATH_INFO')

View File

@@ -160,7 +160,7 @@ class Input(object):
return iter(self.read())
def get_socket(self):
return self.rfile._sock.dup()
return self.rfile._sock
class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
@@ -498,6 +498,10 @@ class Server(BaseHTTPServer.HTTPServer):
'wsgi.run_once': False,
'wsgi.url_scheme': 'http',
}
# detect secure socket
if hasattr(self.socket, 'do_handshake'):
d['wsgi.url_scheme'] = 'https'
d['HTTPS'] = 'on'
if self.environ is not None:
d.update(self.environ)
return d

View File

@@ -1,3 +1,5 @@
import os
import eventlet
from eventlet import wsgi
from eventlet import websocket

View File

@@ -224,3 +224,6 @@ def get_database_auth():
except IOError:
pass
return retval
certificate_file = os.path.join(os.path.dirname(__file__), 'test_server.crt')
private_key_file = os.path.join(os.path.dirname(__file__), 'test_server.key')

View File

@@ -1,13 +1,10 @@
from tests import skipped, LimitedTestCase, skip_unless
from tests import skipped, LimitedTestCase, skip_unless, certificate_file, private_key_file
from unittest import main
import eventlet
from eventlet import util, coros, greenio
import socket
import os
certificate_file = os.path.join(os.path.dirname(__file__), 'test_server.crt')
private_key_file = os.path.join(os.path.dirname(__file__), 'test_server.key')
def listen_ssl_socket(address=('127.0.0.1', 0)):
sock = util.wrap_ssl(socket.socket(), certificate_file,
private_key_file, True)

View File

@@ -8,7 +8,7 @@ from eventlet.websocket import WebSocket, WebSocketWSGI
from eventlet import wsgi
from eventlet import event
from tests import mock, LimitedTestCase
from tests import mock, LimitedTestCase, certificate_file, private_key_file
from tests.wsgi_test import _TestBase
@@ -460,6 +460,50 @@ class TestWebSocket(_TestBase):
self.assert_(error_detected[0])
class TestWebSocketSSL(_TestBase):
def set_site(self):
self.site = wsapp
def test_ssl_sending_messages(self):
s = eventlet.wrap_ssl(eventlet.listen(('localhost', 0)),
certfile=certificate_file,
keyfile=private_key_file,
server_side=True)
self.spawn_server(sock=s)
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.wrap_ssl(eventlet.connect(
('localhost', self.port)))
sock.sendall('\r\n'.join(connect) + '\r\n\r\n^n:ds[4U')
first_resp = sock.recv(1024)
# make sure it sets the wss: protocol on the location header
loc_line = [x for x in first_resp.split("\r\n")
if x.lower().startswith('sec-websocket-location')][0]
self.assert_("wss://localhost" in loc_line,
"Expecting wss protocol in location: %s" % loc_line)
sock.sendall('\x00hello\xFF')
result = sock.recv(1024)
self.assertEqual(result, '\x00hello\xff')
sock.sendall('\x00start')
eventlet.sleep(0.001)
sock.sendall(' end\xff')
result = sock.recv(1024)
self.assertEqual(result, '\x00start end\xff')
sock.shutdown(socket.SHUT_RDWR)
sock.close()
eventlet.sleep(0.01)
class TestWebSocketObject(LimitedTestCase):
def setUp(self):