Skip to content

Commit 39c7cb0

Browse files
committed
Merge branch 'http_api' of https://github.com/CendioOssman/websockify
2 parents c123bfb + be7b868 commit 39c7cb0

File tree

4 files changed

+117
-21
lines changed

4 files changed

+117
-21
lines changed

tests/test_websocketserver.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
2+
""" Unit tests for websocketserver """
3+
import unittest
4+
from unittest.mock import patch, MagicMock
5+
6+
from websockify.websocketserver import HttpWebSocket
7+
8+
9+
class HttpWebSocketTest(unittest.TestCase):
10+
@patch("websockify.websocketserver.WebSocket.__init__", autospec=True)
11+
def test_constructor(self, websock):
12+
# Given
13+
req_obj = MagicMock()
14+
15+
# When
16+
sock = HttpWebSocket(req_obj)
17+
18+
# Then
19+
websock.assert_called_once_with(sock)
20+
self.assertEqual(sock.request_handler, req_obj)
21+
22+
@patch("websockify.websocketserver.WebSocket.__init__", MagicMock(autospec=True))
23+
def test_send_response(self):
24+
# Given
25+
req_obj = MagicMock()
26+
sock = HttpWebSocket(req_obj)
27+
28+
# When
29+
sock.send_response(200, "message")
30+
31+
# Then
32+
req_obj.send_response.assert_called_once_with(200, "message")
33+
34+
@patch("websockify.websocketserver.WebSocket.__init__", MagicMock(autospec=True))
35+
def test_send_response_default_message(self):
36+
# Given
37+
req_obj = MagicMock()
38+
sock = HttpWebSocket(req_obj)
39+
40+
# When
41+
sock.send_response(200)
42+
43+
# Then
44+
req_obj.send_response.assert_called_once_with(200, None)
45+
46+
@patch("websockify.websocketserver.WebSocket.__init__", MagicMock(autospec=True))
47+
def test_send_header(self):
48+
# Given
49+
req_obj = MagicMock()
50+
sock = HttpWebSocket(req_obj)
51+
52+
# When
53+
sock.send_header("keyword", "value")
54+
55+
# Then
56+
req_obj.send_header.assert_called_once_with("keyword", "value")
57+
58+
@patch("websockify.websocketserver.WebSocket.__init__", MagicMock(autospec=True))
59+
def test_end_headers(self):
60+
# Given
61+
req_obj = MagicMock()
62+
sock = HttpWebSocket(req_obj)
63+
64+
# When
65+
sock.end_headers()
66+
67+
# Then
68+
req_obj.end_headers.assert_called_once_with()
69+

websockify/websocket.py

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -158,19 +158,19 @@ def connect(self, uri, origin=None, protocols=[]):
158158
if not path:
159159
path = "/"
160160

161-
self._queue_str("GET %s HTTP/1.1\r\n" % path)
162-
self._queue_str("Host: %s\r\n" % uri.hostname)
163-
self._queue_str("Upgrade: websocket\r\n")
164-
self._queue_str("Connection: upgrade\r\n")
165-
self._queue_str("Sec-WebSocket-Key: %s\r\n" % self._key)
166-
self._queue_str("Sec-WebSocket-Version: 13\r\n")
161+
self.send_request("GET", path)
162+
self.send_header("Host", uri.hostname)
163+
self.send_header("Upgrade", "websocket")
164+
self.send_header("Connection", "upgrade")
165+
self.send_header("Sec-WebSocket-Key", self._key)
166+
self.send_header("Sec-WebSocket-Version", 13)
167167

168168
if origin is not None:
169-
self._queue_str("Origin: %s\r\n" % origin)
169+
self.send_header("Origin", origin)
170170
if len(protocols) > 0:
171-
self._queue_str("Sec-WebSocket-Protocol: %s\r\n" % ", ".join(protocols))
171+
self.send_header("Sec-WebSocket-Protocol", ", ".join(protocols))
172172

173-
self._queue_str("\r\n")
173+
self.end_headers()
174174

175175
self._state = "send_headers"
176176

@@ -283,15 +283,15 @@ def accept(self, socket, headers):
283283
if self.protocol not in protocols:
284284
raise Exception('Invalid protocol selected')
285285

286-
self._queue_str("HTTP/1.1 101 Switching Protocols\r\n")
287-
self._queue_str("Upgrade: websocket\r\n")
288-
self._queue_str("Connection: Upgrade\r\n")
289-
self._queue_str("Sec-WebSocket-Accept: %s\r\n" % accept)
286+
self.send_response(101, "Switching Protocols")
287+
self.send_header("Upgrade", "websocket")
288+
self.send_header("Connection", "Upgrade")
289+
self.send_header("Sec-WebSocket-Accept", accept)
290290

291291
if self.protocol:
292-
self._queue_str("Sec-WebSocket-Protocol: %s\r\n" % self.protocol)
292+
self.send_header("Sec-WebSocket-Protocol", self.protocol)
293293

294-
self._queue_str("\r\n")
294+
self.end_headers()
295295

296296
self._state = "flush"
297297

@@ -447,6 +447,18 @@ def sendmsg(self, msg):
447447

448448
return len(msg)
449449

450+
def send_response(self, code, message):
451+
self._queue_str("HTTP/1.1 %d %s\r\n" % (code, message))
452+
453+
def send_header(self, keyword, value):
454+
self._queue_str("%s: %s\r\n" % (keyword, value))
455+
456+
def end_headers(self):
457+
self._queue_str("\r\n")
458+
459+
def send_request(self, type, path):
460+
self._queue_str("%s %s HTTP/1.1\r\n" % (type.upper(), path))
461+
450462
def ping(self, data=b''):
451463
"""Write a ping message to the WebSocket
452464

websockify/websocketserver.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,23 @@
1212

1313
from websockify.websocket import WebSocket, WebSocketWantReadError, WebSocketWantWriteError
1414

15+
class HttpWebSocket(WebSocket):
16+
"""Class to glue websocket and http request functionality together"""
17+
def __init__(self, request_handler):
18+
super().__init__()
19+
20+
self.request_handler = request_handler
21+
22+
def send_response(self, code, message=None):
23+
self.request_handler.send_response(code, message)
24+
25+
def send_header(self, keyword, value):
26+
self.request_handler.send_header(keyword, value)
27+
28+
def end_headers(self):
29+
self.request_handler.end_headers()
30+
31+
1532
class WebSocketRequestHandlerMixIn:
1633
"""WebSocket request handler mix-in class
1734
@@ -25,7 +42,7 @@ class WebSocketRequestHandlerMixIn:
2542
use for the WebSocket connection.
2643
"""
2744

28-
SocketClass = WebSocket
45+
SocketClass = HttpWebSocket
2946

3047
def handle_one_request(self):
3148
"""Extended request handler
@@ -59,16 +76,14 @@ def handle_upgrade(self):
5976
The WebSocket object will then replace the request object and
6077
handle_websocket() will be called.
6178
"""
62-
websocket = self.SocketClass()
79+
websocket = self.SocketClass(self)
6380
try:
6481
websocket.accept(self.request, self.headers)
6582
except Exception:
6683
exc = sys.exc_info()[1]
6784
self.send_error(400, str(exc))
6885
return
6986

70-
self.log_request(101)
71-
7287
self.request = websocket
7388

7489
# Other requests cannot follow Websocket data

websockify/websockifyserver.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@
2929
# make sockets pickle-able/inheritable
3030
import multiprocessing.reduction
3131

32-
from websockify.websocket import WebSocket, WebSocketWantReadError, WebSocketWantWriteError
32+
from websockify.websocket import WebSocketWantReadError, WebSocketWantWriteError
3333
from websockify.websocketserver import WebSocketRequestHandlerMixIn
3434

35-
class CompatibleWebSocket(WebSocket):
35+
class CompatibleWebSocket(WebSocketRequestHandlerMixIn.SocketClass):
3636
def select_subprotocol(self, protocols):
3737
# Handle old websockify clients that still specify a sub-protocol
3838
if 'binary' in protocols:

0 commit comments

Comments
 (0)