Repository Wiki
ChanIok/SpinningMomo

HTTP 服务器、客户端与安全边界

SpinningMomo 后端围绕 src/core/http_server/(基于 uWebSockets 的 HTTP + SSE 开发期传输层)与 src/core/http_client/(出站 HTTP 客户端)构建网络通信能力,并通过传输层选择、访问控制模块与本地套接字边界共同划定应用的安全边界。

目的与范围

本页覆盖 operations.http-network 主题,即 HTTP 服务器与客户端子系统的组成、传输层架构、请求/推送流程与安全边界划分,包括:

  • src/core/http_server/ 的模块划分(routes、access、static、downloads、sse_manager、network_addresses 等)与 src/core/http_client/ 的模块划分;
  • 双传输层模型:WebView bridge(生产)与 HTTP + SSE(开发,uWebSockets 端口 51206);
  • 前端环境检测与传输自动选择、Vite 开发代理(/rpc、/static);
  • 与之相关的边界面:Android 捕获守护进程的 LocalServerSocket 本地套接字边界、playground/ 调试入口。

以下内容有意留给兄弟页面,本页仅做交叉指引:

  • JSON-RPC 2.0 端点的注册与组织方式(core::rpc::register_method、endpoints/<domain>/)——见 RPC 相关目录页;
  • WebView2 集成与原生 UI——见 WebView / UI 相关页;
  • Android 捕获管线(VirtualDisplay、编码、ADB 调试)——见 android/capture/README.md 对应页;
  • Asio 协程运行时与事件总线的内部实现——见 core 异步运行时相关页。

证据边界说明:本页在源码探索预算内完成了模块清单枚举与架构级文档(AGENTS.md)的核验,并核验了 Android 端 CaptureServer.java 的本地套接字代码片段;src/core/http_server/*.cpp|*.hpp 与 src/core/http_client/*.cpp|*.hpp 的内部实现细节未在本页预算内逐行读取,因此凡涉及这些文件内部逻辑的描述均以目录结构、命名与 AGENTS.md 已核验陈述为准,并明确标注"未在源码中读取"。相应文件的 API 签名请直接查阅源文件链接。

概述

SpinningMomo 是"Win32 原生 C++ 后端 + 内嵌 WebView2 前端"的双进程应用,前后端通过 JSON-RPC 2.0 通信,存在两条传输路径(AGENTS.md):

  • WebView bridge:Vue 应用运行在 WebView2 内时使用(生产路径),不经网络;
  • HTTP + SSE:Vue 应用运行在浏览器中时使用(开发路径),由 uWebSockets 在端口 51206 上提供 HTTP 服务,SSE 承担服务器到客户端的推送通知。

前端通过检测 window.chrome.webview 是否存在自动选择传输层。HTTP 服务器子系统因此是开发期的网络暴露面,其内部按职责拆分为路由、访问控制、静态资源、下载、SSE 管理与网络地址等模块;http_client 则是后端面向外部网络的出站方向(core::* 框架设施之一)。第三方依赖 uWebSockets / uSockets 以 Apache-2.0 引入(CREDITS.md)。

架构

传输与子系统架构图

下图展示已核验的组件关系:前端环境检测决定传输路径;HTTP 服务器子系统的模块按目录归属分组;RPC 注册表是两条传输路径的共同汇聚点。

Loading diagram...

要点说明:

  • 实线边为 AGENTS.md 已核验的数据流(传输选择、JSON-RPC、SSE 推送、出站方向);虚线边仅表示 http_server 目录内模块的归属示意,模块间的具体调用契约未在本页预算内读取源码核验。
  • state.hpp / types.hpp 是各子系统的状态与类型定义文件,符合 AGENTS.md 声明的中央 AppState 模式:"core::AppState 是单一根状态对象,以 std::unique_ptr 成员持有所有子系统状态"(AGENTS.md)。
  • 后端整体不使用 OOP 类层次,而是 POD 结构体 + 自由函数,所有状态集中于 AppState 并以 AppState& 传递(AGENTS.md)。因此 HTTP 子系统预期同样以"状态结构 + 自由函数"形态组织,而非服务类继承树。

核心流程

开发期 HTTP + SSE 请求/推送流程

以下时序图展示已核验的开发期端到端流程:前端环境检测 → HTTP 传输 → 路由分发 → RPC 处理 → SSE 推送。Vite 开发服务器将 /rpc 与 /static 代理到 localhost:51206(AGENTS.md)。

Loading diagram...

关键点:

  • RPC 与推送是两个方向:请求-响应走 JSON-RPC 2.0,服务器主动推送走 SSE。AGENTS.md 明确 "SSE provides server-to-client push notifications"(AGENTS.md)。
  • RPC 处理器的异步契约:"core::rpc::register_method<Req, Res>() 注册处理器并配合 reflect-cpp 做 (反)序列化,C++ 的 snake_case 与 JSON 的 camelCase 字段名自动互转",且 "RPC handlers return asio::awaitable<RpcResult<T>>"(AGENTS.md)。HTTP 传输层需要把这个协程契约桥接到 uWebSockets 的事件循环上。
  • /static 与 /rpc 的分工:static 模块服务于静态资源(生产内嵌、开发由 Vite 代理),routes 模块服务于 RPC 入口。该分工来自目录命名与代理配置,模块内部路由表结构未在源码中读取。

生产期 WebView bridge 流程(对照)

生产期前端运行于 WebView2 内,通过 window.chrome.webview 桥接直接与后端通信,不经过网络栈,因此 HTTP 服务器不构成生产期暴露面。该路径的内部实现属于 WebView 集成主题,见相关页。

安全边界

本应用的网络相关安全边界由三部分构成,均已在源码/文档中核验:

1. 传输层选择边界:HTTP 仅限开发期

生产模式下前后端通信经 WebView bridge(进程内桥接),HTTP + SSE 只在浏览器开发场景启用。这意味着 uWebSockets 端口 51206 是开发工具链的一部分而非产品攻击面——这是本设计最重要的边界决策:生产部署不打开网络监听端口,浏览器开发者无需对端口做加固即可使用生产构建。

2. 访问控制模块:access

src/core/http_server/access.cpp|hpp 是 http_server 子系统内专门负责访问控制的模块(模块存在性已由文件清单核验)。其内部策略(如令牌校验、来源校验、绑定地址约束)未在本页预算内读取源码,实现细节请查阅源文件:access.hpp 与 access.cpp。

network_addresses.cpp|hpp 模块的存在进一步表明服务器对监听地址有独立管理(例如区分回环与全网监听),这与"开发期本地服务"的定位一致。具体绑定逻辑未在源码中读取。

3. Android 侧本地套接字边界

Android 捕获守护进程不使用 TCP/HTTP,而是通过 Android 的 LocalServerSocket(Unix 域本地套接字)接收连接,将通信限制在设备内部命名空间,避免任何网络暴露。以下为 CaptureServer.java 的真实代码片段:

java
import android.net.LocalServerSocket; import android.net.LocalSocket;

Source: CaptureServer.java

java
static void run(String socketName) throws Exception { LocalServerSocket serverSocket = new LocalServerSocket(socketName); LocalServerSocket recordServerSocket = new LocalServerSocket(socketName + "-record");

Source: CaptureServer.java

java
LocalSocket recordClient = recordServerSocket.accept(); RecordSession.handle(recordClient);

Source: CaptureServer.java

设计意图解读:

  • 双套接字命名约定:socketName 与 socketName + "-record" 分离了主控制通道与录制通道,二者共用一个 run() 入口但各自 accept() 循环,隔离了会话生命周期。
  • 逐连接处理 + 资源清理:主循环内 serverSocket.accept() 得到 LocalSocket client,并以 try-with-resources(try (LocalSocket activeClient = client; ...))确保连接关闭;退出路径上分别对 serverSocket 与 recordServerSocket 执行 close(),且对 IOException 显式忽略(ignored),保证关闭阶段不抛出中断关闭流程(CaptureServer.java)。
  • 本地命名空间即边界:LocalServerSocket 只能被同设备进程访问(通常经 ADB 转发),这与 Windows 侧"开发期本地 HTTP"形成一致的纵深防御哲学——默认不跨网络暴露。

使用示例

前端侧:环境检测与传输选择(已核验路径)

前端环境检测位于 web/src/core/env/,RPC 客户端位于 web/src/core/rpc/,二者协作完成传输自动选择。AGENTS.md 对该机制的核验描述如下(此处为文档陈述,代码本身位于 web 前端源码,本页未逐行读取):

  • "The frontend auto-detects its environment (window.chrome.webview presence) and selects the appropriate transport."(AGENTS.md)

后端模块组成清单(已核验的文件级事实)

src/core/http_server/ 目录下的模块划分(文件清单经 ListFiles 核验):

文件职责(依据目录命名与 AGENTS.md 架构陈述推断,内部实现未逐行读取)
http_server.hpp/cpp服务器入口与生命周期(uWebSockets 集成)
routes.hpp/cpp路由表与请求分发
access.hpp/cpp访问控制(安全边界模块)
static.hpp/cpp静态资源服务(/static)
downloads.hpp/cpp下载处理
sse_manager.hpp/cppSSE 连接管理与推送
network_addresses.hpp/cpp监听网络地址管理
state.hpp子系统状态定义(挂入中央 AppState)
types.hpp类型定义

src/core/http_client/ 目录下的模块划分:

文件职责
http_client.hpp/cpp客户端入口
state.hpp客户端状态定义
types.hpp类型定义

Sources:

后端构建与前端构建入口(已核验命令)

1# C++ backend — debug 2xmake build 3 4# C++ backend — release 5xmake release 6 7# Web frontend 8pnpm run build:web

Source: AGENTS.md

配置选项

本页范围内未发现以独立配置文件形式存在的 HTTP 相关配置项(未在预算内读取 src/core/http_server 内部实现)。已核验的运行期常量/约定如下:

项值来源
开发期 HTTP 端口51206AGENTS.md
开发代理路径/rpc、/static → localhost:51206AGENTS.md
开发代理服务Vite dev serverAGENTS.md
HTTP 栈uWebSockets / uSockets(Apache-2.0)CREDITS.md
环境检测标志window.chrome.webviewAGENTS.md

若需核对端口、地址绑定与访问策略的代码级默认值,请直接查阅 network_addresses.cpp 与 access.cpp。

API 参考

实现细节未在源码中读取:受本页源码探索预算限制,src/core/http_server/ 与 src/core/http_client/ 的函数签名未逐行核验,此处不提供臆造的签名列表。以下仅给出经 AGENTS.md 核验的跨传输层契约,供读者定位真实实现:

契约形式说明
RPC 方法注册core::rpc::register_method<Req, Res>()处理器与 reflect-cpp 序列化绑定,字段名 snake_case ↔ camelCase 自动互转
RPC 处理器返回asio::awaitable<RpcResult<T>>Asio 协程契约,需由 HTTP/WebView 两种传输分别桥接
错误处理惯例std::expected<T, std::string>全仓库统一,无异常控制流

Source: AGENTS.md

故障模式、边界情况与并发

基于已核验证据的可确认项:

  • 双传输层一致性风险:同一 JSON-RPC 契约必须同时被 WebView bridge 与 HTTP 路由满足。任何新增端点若只注册到一条路径,会在浏览器开发可用、生产 WebView 不可用(或反之)时暴露分歧。注册机制集中在 core::rpc::register_method,两个传输层共用同一注册表是控制该风险的结构性手段。
  • SSE 连接生命周期:SSE 是长连接,sse_manager 模块的存在表明连接被集中管理(避免句柄泄漏/推送丢失)。具体重连与心跳策略未在源码中读取。
  • Android 端并发模型:CaptureServer.run 的主循环逐个 accept(),录制套接字在独立循环中处理;每个客户端连接以 try-with-resources 保证确定性关闭(CaptureServer.java)。两个 accept 循环的线程/并发关系未在源码中读取。
  • 关停顺序:CaptureServer 在退出时分别关闭两个 serverSocket 并吞掉关闭阶段的 IOException,避免清理路径中断(CaptureServer.java)。

性能与运行注意事项

  • uWebSockets 是以高性能著称的轻量 HTTP/WS 实现,选择它与"开发期工具链服务"的定位匹配:高吞吐、低资源占用,同时免去生产期常驻监听。
  • playground/ 目录提供独立的 Node/TypeScript 脚本,用于后端 HTTP/RPC 调试与实验(AGENTS.md),是脱离前端直接验证 HTTP 接口的运维入口。
  • 后端使用 Asio 协程运行时(core::async),HTTP 处理与协程的调度关系(如 per-connection strand)未在源码中读取,涉及热路径改动时应先核验 http_server.cpp。

扩展点

  • 新增 RPC 端点:遵循 src/core/rpc/endpoints/<domain>/ 组织方式,每个域提供 register_all(state) 由 registry.cpp 调用(AGENTS.md)。传输层无需改动——这正是双传输层共用注册表带来的扩展收益。
  • 静态资源与下载:static / downloads 模块是 HTTP 子系统内独立扩展位,新增资源路由理论上不改 RPC 注册表。
  • 游戏扩展:src/extensions/ 下的适配器经 rpc/endpoints/extensions/ 暴露,与 HTTP 传输解耦(AGENTS.md)。

相关链接

Sources

(1 files)