summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Ossman <ossman@cendio.se>2022-11-08 14:29:12 +0100
committerPierre Ossman <ossman@cendio.se>2022-11-08 14:29:12 +0100
commit39c7cb011577399fd0a4fee5624dcc229db7b17e (patch)
tree446aefaaa66dec8432dbfbbbb9655281164fbefa
parentc123bfbbffc7cc799b49b69cda1496b1e69aaf3c (diff)
parentbe7b868518801c376168cfb6c40bacdc8c49b3b2 (diff)
downloadwebsockify-39c7cb011577399fd0a4fee5624dcc229db7b17e.tar.gz
Merge branch 'http_api' of https://github.com/CendioOssman/websockify
-rw-r--r--tests/test_websocketserver.py69
-rw-r--r--websockify/websocket.py42
-rw-r--r--websockify/websocketserver.py23
-rw-r--r--websockify/websockifyserver.py4
4 files changed, 117 insertions, 21 deletions
diff --git a/tests/test_websocketserver.py b/tests/test_websocketserver.py
new file mode 100644
index 0000000..0e37e3d
--- /dev/null
+++ b/tests/test_websocketserver.py
@@ -0,0 +1,69 @@
+
+""" Unit tests for websocketserver """
+import unittest
+from unittest.mock import patch, MagicMock
+
+from websockify.websocketserver import HttpWebSocket
+
+
+class HttpWebSocketTest(unittest.TestCase):
+ @patch("websockify.websocketserver.WebSocket.__init__", autospec=True)
+ def test_constructor(self, websock):
+ # Given
+ req_obj = MagicMock()
+
+ # When
+ sock = HttpWebSocket(req_obj)
+
+ # Then
+ websock.assert_called_once_with(sock)
+ self.assertEqual(sock.request_handler, req_obj)
+
+ @patch("websockify.websocketserver.WebSocket.__init__", MagicMock(autospec=True))
+ def test_send_response(self):
+ # Given
+ req_obj = MagicMock()
+ sock = HttpWebSocket(req_obj)
+
+ # When
+ sock.send_response(200, "message")
+
+ # Then
+ req_obj.send_response.assert_called_once_with(200, "message")
+
+ @patch("websockify.websocketserver.WebSocket.__init__", MagicMock(autospec=True))
+ def test_send_response_default_message(self):
+ # Given
+ req_obj = MagicMock()
+ sock = HttpWebSocket(req_obj)
+
+ # When
+ sock.send_response(200)
+
+ # Then
+ req_obj.send_response.assert_called_once_with(200, None)
+
+ @patch("websockify.websocketserver.WebSocket.__init__", MagicMock(autospec=True))
+ def test_send_header(self):
+ # Given
+ req_obj = MagicMock()
+ sock = HttpWebSocket(req_obj)
+
+ # When
+ sock.send_header("keyword", "value")
+
+ # Then
+ req_obj.send_header.assert_called_once_with("keyword", "value")
+
+ @patch("websockify.websocketserver.WebSocket.__init__", MagicMock(autospec=True))
+ def test_end_headers(self):
+ # Given
+ req_obj = MagicMock()
+ sock = HttpWebSocket(req_obj)
+
+ # When
+ sock.end_headers()
+
+ # Then
+ req_obj.end_headers.assert_called_once_with()
+
diff --git a/websockify/websocket.py b/websockify/websocket.py
index f0db3ba..5a819f7 100644
--- a/websockify/websocket.py
+++ b/websockify/websocket.py
@@ -158,19 +158,19 @@ class WebSocket(object):
if not path:
path = "/"
- self._queue_str("GET %s HTTP/1.1\r\n" % path)
- self._queue_str("Host: %s\r\n" % uri.hostname)
- self._queue_str("Upgrade: websocket\r\n")
- self._queue_str("Connection: upgrade\r\n")
- self._queue_str("Sec-WebSocket-Key: %s\r\n" % self._key)
- self._queue_str("Sec-WebSocket-Version: 13\r\n")
+ self.send_request("GET", path)
+ self.send_header("Host", uri.hostname)
+ self.send_header("Upgrade", "websocket")
+ self.send_header("Connection", "upgrade")
+ self.send_header("Sec-WebSocket-Key", self._key)
+ self.send_header("Sec-WebSocket-Version", 13)
if origin is not None:
- self._queue_str("Origin: %s\r\n" % origin)
+ self.send_header("Origin", origin)
if len(protocols) > 0:
- self._queue_str("Sec-WebSocket-Protocol: %s\r\n" % ", ".join(protocols))
+ self.send_header("Sec-WebSocket-Protocol", ", ".join(protocols))
- self._queue_str("\r\n")
+ self.end_headers()
self._state = "send_headers"
@@ -283,15 +283,15 @@ class WebSocket(object):
if self.protocol not in protocols:
raise Exception('Invalid protocol selected')
- self._queue_str("HTTP/1.1 101 Switching Protocols\r\n")
- self._queue_str("Upgrade: websocket\r\n")
- self._queue_str("Connection: Upgrade\r\n")
- self._queue_str("Sec-WebSocket-Accept: %s\r\n" % accept)
+ self.send_response(101, "Switching Protocols")
+ self.send_header("Upgrade", "websocket")
+ self.send_header("Connection", "Upgrade")
+ self.send_header("Sec-WebSocket-Accept", accept)
if self.protocol:
- self._queue_str("Sec-WebSocket-Protocol: %s\r\n" % self.protocol)
+ self.send_header("Sec-WebSocket-Protocol", self.protocol)
- self._queue_str("\r\n")
+ self.end_headers()
self._state = "flush"
@@ -447,6 +447,18 @@ class WebSocket(object):
return len(msg)
+ def send_response(self, code, message):
+ self._queue_str("HTTP/1.1 %d %s\r\n" % (code, message))
+
+ def send_header(self, keyword, value):
+ self._queue_str("%s: %s\r\n" % (keyword, value))
+
+ def end_headers(self):
+ self._queue_str("\r\n")
+
+ def send_request(self, type, path):
+ self._queue_str("%s %s HTTP/1.1\r\n" % (type.upper(), path))
+
def ping(self, data=b''):
"""Write a ping message to the WebSocket
diff --git a/websockify/websocketserver.py b/websockify/websocketserver.py
index 9088fe9..4e62f2e 100644
--- a/websockify/websocketserver.py
+++ b/websockify/websocketserver.py
@@ -12,6 +12,23 @@ from http.server import BaseHTTPRequestHandler, HTTPServer
from websockify.websocket import WebSocket, WebSocketWantReadError, WebSocketWantWriteError
+class HttpWebSocket(WebSocket):
+ """Class to glue websocket and http request functionality together"""
+ def __init__(self, request_handler):
+ super().__init__()
+
+ self.request_handler = request_handler
+
+ def send_response(self, code, message=None):
+ self.request_handler.send_response(code, message)
+
+ def send_header(self, keyword, value):
+ self.request_handler.send_header(keyword, value)
+
+ def end_headers(self):
+ self.request_handler.end_headers()
+
+
class WebSocketRequestHandlerMixIn:
"""WebSocket request handler mix-in class
@@ -25,7 +42,7 @@ class WebSocketRequestHandlerMixIn:
use for the WebSocket connection.
"""
- SocketClass = WebSocket
+ SocketClass = HttpWebSocket
def handle_one_request(self):
"""Extended request handler
@@ -59,7 +76,7 @@ class WebSocketRequestHandlerMixIn:
The WebSocket object will then replace the request object and
handle_websocket() will be called.
"""
- websocket = self.SocketClass()
+ websocket = self.SocketClass(self)
try:
websocket.accept(self.request, self.headers)
except Exception:
@@ -67,8 +84,6 @@ class WebSocketRequestHandlerMixIn:
self.send_error(400, str(exc))
return
- self.log_request(101)
-
self.request = websocket
# Other requests cannot follow Websocket data
diff --git a/websockify/websockifyserver.py b/websockify/websockifyserver.py
index 0199e42..3e13fa1 100644
--- a/websockify/websockifyserver.py
+++ b/websockify/websockifyserver.py
@@ -29,10 +29,10 @@ if sys.platform == 'win32':
# make sockets pickle-able/inheritable
import multiprocessing.reduction
-from websockify.websocket import WebSocket, WebSocketWantReadError, WebSocketWantWriteError
+from websockify.websocket import WebSocketWantReadError, WebSocketWantWriteError
from websockify.websocketserver import WebSocketRequestHandlerMixIn
-class CompatibleWebSocket(WebSocket):
+class CompatibleWebSocket(WebSocketRequestHandlerMixIn.SocketClass):
def select_subprotocol(self, protocols):
# Handle old websockify clients that still specify a sub-protocol
if 'binary' in protocols: