Support for SSL websockets, which also happens to improve our SSL support in WSGI generally. Fixes #62.
This commit is contained in:
@@ -301,6 +301,8 @@ class GreenSSLSocket(__ssl.SSLSocket):
|
|||||||
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
|
||||||
|
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
Reference in New Issue
Block a user