mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-21 22:53:56 +08:00
103 lines
3.8 KiB
Python
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
|