完成 WebSocket 与 workspace 架构优化收尾,并修复全量回归阻塞。

本次将握手鉴权、Origin 校验、限流、重连补偿、发送背压与观测字段打通,同时修复 gateway 异常路径与备份清理兼容问题,确保工具历史压缩语义一致并恢复全量测试通过。

Made-with: Cursor
This commit is contained in:
oliver 2026-04-27 20:41:50 +08:00
parent 09836dddfa
commit 7253f6795d
12 changed files with 536 additions and 24 deletions

View file

@ -6,6 +6,8 @@ from typing import Any
from starlette.websockets import WebSocketDisconnect
from oclaw.interfaces.ws.common import WS_REQUIRE_AUTH
async def recv_frame(
*,
@ -93,16 +95,6 @@ async def handle_connect(
)
conn.handshake_failed = True
return
hello = conn.build_hello_ok(params)
hello_errs = validate_or_errors(conn.schemas.hello_ok, hello)
if hello_errs:
await conn.send_res(
req_id,
ok=False,
error=error_shape("UNAVAILABLE", f"server hello-ok schema mismatch: {format_validation_errors(hello_errs)}"),
)
conn.handshake_failed = True
return
conn.connected = True
conn.client_meta = dict(params or {})
try:
@ -122,15 +114,38 @@ async def handle_connect(
or str(auth.get("bootstrapToken") or "").strip()
or str(auth.get("deviceToken") or "").strip()
)
if auth_provided and not conn.auth_ctx:
if not conn.auth_ctx and (WS_REQUIRE_AUTH or auth_provided):
await conn.send_res(
req_id,
ok=False,
error=error_shape("INVALID_REQUEST", "unauthorized", details={"code": "AUTH_UNAUTHORIZED"}),
error=error_shape("UNAUTHORIZED", "unauthorized", details={"code": "AUTH_UNAUTHORIZED"}),
)
conn.handshake_failed = True
if hasattr(conn, "mark_handshake"):
conn.mark_handshake(ok=False)
return
if conn.auth_ctx:
conn.role = str(conn.auth_ctx.get("role") or "member")
conn.scopes = [f"user:{str(conn.auth_ctx.get('user_id') or '')}"]
hello = conn.build_hello_ok(params)
hello_errs = validate_or_errors(conn.schemas.hello_ok, hello)
if hello_errs:
await conn.send_res(
req_id,
ok=False,
error=error_shape("UNAVAILABLE", f"server hello-ok schema mismatch: {format_validation_errors(hello_errs)}"),
)
conn.handshake_failed = True
return
await conn.send_res(req_id, ok=True, payload=hello)
if hasattr(conn, "mark_handshake"):
conn.mark_handshake(ok=True)
if hasattr(conn, "replay_events_since"):
try:
last_seq = int(p.get("lastSeq") or 0)
except Exception:
last_seq = 0
await conn.replay_events_since(last_seq)
__all__ = ["recv_frame", "handle_connect"]