import json
import os
import sys
-from websockets.datastructures import Headers
from websockets.exceptions import ConnectionClosed
-from websockets.server import serve, WebSocketServerProtocol
+from websockets.asyncio.server import serve, ServerConnection
+from websockets.http11 import Request, Response
from controlpi import BasePlugin, MessageBus, BusException, Message, MessageTemplate
-from typing import Optional, Tuple
+from typing import Optional
class Connection:
Instances are created on external connections to websocket.
"""
- def __init__(self, bus: MessageBus, websocket: WebSocketServerProtocol) -> None:
+ def __init__(self, bus: MessageBus, websocket: ServerConnection) -> None:
"""Initialise conncection.
Message bus and websocket are set by websocket server when creating
self._bus.unregister(self._name)
-Response = Optional[Tuple[http.HTTPStatus, Headers, bytes]]
-
-
class WSServer(BasePlugin):
"""Websocket server plugin.
base_url += "/"
self._web_proxies[path] = base_url
- async def _process_request(self, path: str, request_headers: Headers) -> Response:
+ async def _process_request(
+ self, websocket: ServerConnection, request: Request
+ ) -> Optional[Response]:
"""Serve as simple web server."""
- if "Upgrade" in request_headers:
+ if "Upgrade" in request.headers:
return None
- status = None
- body = b""
- response_headers = Headers()
- response_headers["Server"] = "controlpi-wsserver websocket server"
- response_headers["Connection"] = "close"
+ path = request.path
location = ""
url = ""
start_path_length = 0
start_path += "/"
relative_path = path[len(start_path) :]
url = base_url + relative_path
+ response = None
if location:
if os.path.isdir(location) and not path.endswith("/"):
- status = http.HTTPStatus.MOVED_PERMANENTLY
- response_headers["Location"] = path + "/"
+ response = websocket.respond(http.HTTPStatus.MOVED_PERMANENTLY, "")
+ response.headers["Location"] = path + "/"
else:
if os.path.isdir(location):
location = os.path.join(location, "index.html")
if os.path.isfile(location):
- status = http.HTTPStatus.OK
+ # Read body from file:
+ async with aiofiles.open(location, "rb") as f:
+ body = (await f.read()).decode()
+ response = websocket.respond(http.HTTPStatus.OK, body)
# Determine MIME type:
content_type = "application/octet-stream"
extension = os.path.basename(location).split(".")[-1]
content_type = "text/css"
elif extension == "jpg":
content_type = "image/jpeg"
- response_headers["Content-Type"] = content_type
- # Read body from file:
- async with aiofiles.open(location, "rb") as f:
- body = await f.read()
- response_headers["Content-Length"] = str(len(body))
+ del response.headers["Content-Type"]
+ response.headers["Content-Type"] = content_type
if url:
async with aiohttp.ClientSession() as session:
- async with session.get(url) as resp:
- status = http.HTTPStatus.OK
- response_headers["Content-Type"] = resp.headers["Content-Type"]
- response_headers["Content-Length"] = resp.headers["Content-Length"]
- body = await resp.read()
- if not status:
- status = http.HTTPStatus.NOT_FOUND
- body = f"'{path}' not found!".encode()
- response_headers["Content-Type"] = "text/plain"
- response_headers["Content-Length"] = str(len(body))
- return status, response_headers, body
+ async with session.get(url) as original:
+ body = (await original.read()).decode()
+ response = websocket.respond(http.HTTPStatus.OK, body)
+ del response.headers["Content-Type"]
+ response.headers["Content-Type"] = original.headers["Content-Type"]
+ if not response:
+ response = websocket.respond(
+ http.HTTPStatus.NOT_FOUND, f"'{path}' not found!"
+ )
+ return response
- async def _handler(self, websocket: WebSocketServerProtocol, path: str) -> None:
+ async def _handler(self, websocket: ServerConnection) -> None:
"""Create and run connection."""
connection = Connection(self.bus, websocket)
await connection.run()