refactor: extract GatewayHTTPHandler from WebSocketChannel

Extract all HTTP route handling (bootstrap, sessions, settings, media,
commands, sidebar state, static serving, token management) into a new
GatewayHTTPHandler class in nanobot/channels/ws_http.py.

WebSocketChannel is reduced from 1907 to 1372 lines (-28%), retaining
only WebSocket connection management and message dispatch.

No behavior change. 3730 tests pass, 0 failures.

Shared HTTP utility functions (path parsing, response builders, auth
helpers) now live in ws_http.py with websocket.py importing from there,
avoiding circular dependencies.

Backwards-compat property aliases on WebSocketChannel ensure existing
tests continue to work without modification.
This commit is contained in:
chengyongru
2026-06-02 17:14:38 +08:00
committed by Xubin Ren
parent 92fe40a690
commit 1a585288b2
5 changed files with 859 additions and 628 deletions
+7 -7
View File
@@ -710,20 +710,20 @@ async def test_api_token_pool_purges_expired(bus: MagicMock, tmp_path: Path) ->
channel = _ch(bus, session_manager=sm, port=29908)
# Don't start a server — directly inject and validate.
import time as _time
channel._api_tokens["expired"] = _time.monotonic() - 1
channel._api_tokens["live"] = _time.monotonic() + 60
channel._http.api_tokens["expired"] = _time.monotonic() - 1
channel._http.api_tokens["live"] = _time.monotonic() + 60
class _FakeReq:
path = "/api/sessions"
headers = {"Authorization": "Bearer expired"}
assert channel._check_api_token(_FakeReq()) is False
assert channel._http.check_api_token(_FakeReq()) is False
class _LiveReq:
path = "/api/sessions"
headers = {"Authorization": "Bearer live"}
assert channel._check_api_token(_LiveReq()) is True
assert channel._http.check_api_token(_LiveReq()) is True
class _FakeConn:
@@ -814,7 +814,7 @@ def test_localhost_without_auth_is_valid(bus: MagicMock) -> None:
def test_bootstrap_prefers_runtime_model_name(bus: MagicMock, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(
"nanobot.channels.websocket._default_model_name_from_config",
"nanobot.channels.ws_http._default_model_name_from_config",
lambda: "from-disk",
)
channel = _ch(bus, host="127.0.0.1", runtime_model_name=lambda: " live/model ")
@@ -826,7 +826,7 @@ def test_bootstrap_prefers_runtime_model_name(bus: MagicMock, monkeypatch: pytes
def test_bootstrap_falls_back_when_runtime_returns_empty(bus: MagicMock, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(
"nanobot.channels.websocket._default_model_name_from_config",
"nanobot.channels.ws_http._default_model_name_from_config",
lambda: "from-disk",
)
channel = _ch(bus, host="127.0.0.1", runtime_model_name=lambda: " ")
@@ -838,7 +838,7 @@ def test_bootstrap_falls_back_when_runtime_returns_empty(bus: MagicMock, monkeyp
def test_bootstrap_falls_back_when_runtime_raises(bus: MagicMock, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(
"nanobot.channels.websocket._default_model_name_from_config",
"nanobot.channels.ws_http._default_model_name_from_config",
lambda: "from-disk",
)