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, do_handshake_on_connect=self.do_handshake_on_connect,
suppress_ragged_eofs=self.suppress_ragged_eofs) suppress_ragged_eofs=self.suppress_ragged_eofs)
return (new_ssl, addr) return (new_ssl, addr)
def dup(self):
raise NotImplementedError("Can't dup an ssl object")
SSLSocket = GreenSSLSocket SSLSocket = GreenSSLSocket

View File

@@ -71,7 +71,11 @@ class WebSocketWSGI(object):
response = md5(key).digest() response = md5(key).digest()
# Start building the response # 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('HTTP_HOST'),
environ.get('SCRIPT_NAME'), environ.get('SCRIPT_NAME'),
environ.get('PATH_INFO') environ.get('PATH_INFO')

View File

@@ -160,7 +160,7 @@ class Input(object):
return iter(self.read()) return iter(self.read())
def get_socket(self): def get_socket(self):
return self.rfile._sock.dup() return self.rfile._sock
class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
@@ -498,6 +498,10 @@ class Server(BaseHTTPServer.HTTPServer):
'wsgi.run_once': False, 'wsgi.run_once': False,
'wsgi.url_scheme': 'http', '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: if self.environ is not None:
d.update(self.environ) d.update(self.environ)
return d return d

View File

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

View File

@@ -224,3 +224,6 @@ def get_database_auth():
except IOError: except IOError:
pass pass
return retval 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 from unittest import main
import eventlet import eventlet
from eventlet import util, coros, greenio from eventlet import util, coros, greenio
import socket import socket
import os 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)): def listen_ssl_socket(address=('127.0.0.1', 0)):
sock = util.wrap_ssl(socket.socket(), certificate_file, sock = util.wrap_ssl(socket.socket(), certificate_file,
private_key_file, True) private_key_file, True)

View File

@@ -8,7 +8,7 @@ from eventlet.websocket import WebSocket, WebSocketWSGI
from eventlet import wsgi from eventlet import wsgi
from eventlet import event 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 from tests.wsgi_test import _TestBase
@@ -460,6 +460,50 @@ class TestWebSocket(_TestBase):
self.assert_(error_detected[0]) 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): class TestWebSocketObject(LimitedTestCase):
def setUp(self): def setUp(self):