Files
xiaozhi-esp32-server/main/xiaozhi-server/test/wakeword_runtime/runtime/http_server.py
T

103 lines
3.8 KiB
Python

import json
import queue
from http import HTTPStatus
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from ..bridge import WakewordEventBridge
class TestRuntimeHttpServer:
def __init__(self, test_root: Path, host: str = "0.0.0.0", port: int = 8006) -> None:
self.test_root = test_root
self.host = host
self.port = port
self.event_bridge = WakewordEventBridge()
self._server = self._build_server()
@property
def page_url(self) -> str:
return f"http://127.0.0.1:{self.port}/test_page.html"
@property
def events_url(self) -> str:
return f"http://127.0.0.1:{self.port}/events"
def serve_forever(self) -> None:
self._server.serve_forever()
def shutdown(self) -> None:
self._server.shutdown()
self.event_bridge.close()
self._server.server_close()
def _build_server(self) -> ThreadingHTTPServer:
test_root = self.test_root
event_bridge = self.event_bridge
class TestRuntimeHandler(SimpleHTTPRequestHandler):
protocol_version = "HTTP/1.1"
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=str(test_root), **kwargs)
def handle(self) -> None:
try:
super().handle()
except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError):
pass
def do_GET(self) -> None:
if self.path == "/events":
self._handle_events(event_bridge)
return
if self.path == "/health":
body = json.dumps({"status": "ok"}).encode("utf-8")
self.send_response(HTTPStatus.OK)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Cache-Control", "no-cache")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
return
super().do_GET()
def log_message(self, format: str, *args) -> None:
return
def _handle_events(self, bridge: WakewordEventBridge) -> None:
client_queue = bridge.add_client()
self.send_response(HTTPStatus.OK)
self.send_header("Content-Type", "text/event-stream; charset=utf-8")
self.send_header("Cache-Control", "no-cache")
self.send_header("Connection", "keep-alive")
self.send_header("X-Accel-Buffering", "no")
self.end_headers()
try:
ready_message = bridge.build_ready_message()
self.wfile.write(f"data: {ready_message}\n\n".encode("utf-8"))
self.wfile.flush()
while bridge.is_running:
try:
message = client_queue.get(timeout=15)
if message == "__bridge_closed__":
break
self.wfile.write(f"data: {message}\n\n".encode("utf-8"))
except queue.Empty:
if not bridge.is_running:
break
self.wfile.write(b": keepalive\n\n")
self.wfile.flush()
except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError):
pass
finally:
bridge.remove_client(client_queue)
server = ThreadingHTTPServer((self.host, self.port), TestRuntimeHandler)
server.daemon_threads = True
return server