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 注册表是两条传输路径的共同汇聚点。
要点说明:
- 实线边为
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)。
关键点:
- 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 returnasio::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 的真实代码片段:
import android.net.LocalServerSocket;
import android.net.LocalSocket;Source: CaptureServer.java
static void run(String socketName) throws Exception {
LocalServerSocket serverSocket = new LocalServerSocket(socketName);
LocalServerSocket recordServerSocket = new LocalServerSocket(socketName + "-record");Source: CaptureServer.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.webviewpresence) 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/cpp | SSE 连接管理与推送 |
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:webSource: AGENTS.md
配置选项
本页范围内未发现以独立配置文件形式存在的 HTTP 相关配置项(未在预算内读取 src/core/http_server 内部实现)。已核验的运行期常量/约定如下:
| 项 | 值 | 来源 |
|---|---|---|
| 开发期 HTTP 端口 | 51206 | AGENTS.md |
| 开发代理路径 | /rpc、/static → localhost:51206 | AGENTS.md |
| 开发代理服务 | Vite dev server | AGENTS.md |
| HTTP 栈 | uWebSockets / uSockets(Apache-2.0) | CREDITS.md |
| 环境检测标志 | window.chrome.webview | AGENTS.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)。