pub struct IncomingRequest {
pub conn_id: u64,
pub proto_id: u32,
pub serial_no: u32,
pub proto_fmt_type: ProtoFmtType,
pub body: Bytes,
pub idempotency_key: Option<String>,
pub caller_key_id: Option<String>,
pub caller_allowed_acc_ids: Option<Arc<HashSet<u64>>>,
}Expand description
从连接接收到的请求
Fields§
§conn_id: u64发送请求的连接 ID(用于响应路由 + SubscriptionManager / cache 记账)
跨 surface 命名空间分配(v1.4.106 codex 0517 ζ25-redo F2 沉淀):
- raw TCP listener:
ClientConn::generate_conn_id()派生(u32 范围) - REST:
crates/futu-rest/src/routes/qot.rs::REST_SHARED_CONN=0xFFFF_FFFE(u32 上限附近, 单值共享) - gRPC:
crates/futu-grpc/src/auth.rs::GRPC_STABLE_CONN_NAMESPACE=0x4000_0000_0000_0000(bit 62 namespace, 按 caller 派生) - WS / MCP: 通常派生自所属物理 TCP 连接的 conn_id
各 surface 不重叠. 加新 surface 时分配一个 namespace base, 不要与 上述 4 个段重合.
proto_id: u32协议 ID(对齐 C++ NN_ProtoCmd_*)
serial_no: u32序列号(和 Response 配对,供 client 端请求-响应匹配)
proto_fmt_type: ProtoFmtType请求体 proto 格式(Protobuf / JSON)
body: Bytes请求 body(已解密后的明文)
idempotency_key: Option<String>v1.4.38 Phase 4: 订单幂等 key(由 REST Idempotency-Key header / gRPC
metadata / WS envelope / MCP tool args 填入)。None 表示客户端未传,
handler 走无幂等直通 path(backward-compat)。
caller_key_id: Option<String>v1.4.106 codex 0920 F1 (P1): caller key id 副本 (per-call snapshot, 由 surface adapter 层从 KeyRecord 读取后填入).
目标: idempotency cache key namespace 必须含 caller key id, 否则 不同 caller 用同 Idempotency-Key 会跨 caller 命中老 response —— 严重 跨账户数据泄漏 + 重复下单 silent fail.
None = 无 caller 标识 (legacy TCP / 未 auth) → namespace 用 <no_key>
占位符. Some("alice") = WS / MCP / REST 已 auth 的 caller —— namespace
用 <caller_key_id="alice">, 不与其他 caller 串.
caller_allowed_acc_ids: Option<Arc<HashSet<u64>>>v1.4.105 D2 contract-hardening 补丁: caller key 的 allowed_acc_ids 硬限额
副本 (per-call snapshot, 在 surface adapter 层从 KeyRecord 读取后填入).
目标: 让 dispatch-time handlers (e.g. SubAccPushHandler 注册 acc_id
到 SubscriptionManager) 也能 enforce per-acc whitelist — 即使上游 pipeline
body-aware step 已 enforce, 让 handler 自己 defense-in-depth 防 future
regression (新 surface 加进来漏调 pipeline body-aware).
None / Some(empty) = caller 无 acc_id 限制 (legacy mode 或 unrestricted
key) → handler 不 filter; Some(non-empty set) → handler 应 reject 不在
set 中的 acc_id. Deny-all 使用 sentinel {0},不使用空集合。
Implementations§
Source§impl IncomingRequest
impl IncomingRequest
Sourcepub fn builder(
conn_id: u64,
proto_id: u32,
serial_no: u32,
proto_fmt_type: ProtoFmtType,
body: Bytes,
) -> IncomingRequestBuilder
pub fn builder( conn_id: u64, proto_id: u32, serial_no: u32, proto_fmt_type: ProtoFmtType, body: Bytes, ) -> IncomingRequestBuilder
codex 0522 F4 v1.4.106: cross-surface 单测 hook. 构 IncomingRequest
并填 caller scope (caller_key_id + caller_allowed_acc_ids) — 让
REST / gRPC / WS / MCP 等 surface 的 adapter 都用同一构造路径, 防
“某个 surface 漏填字段” silent regression.
之前 4 surface 各写一份 struct literal, 加新字段需逐个改, 漏一个就 出现 silent None — 与坑 #54 schema-only fix 同模式 (实装符号 vs 真 行为差距). 本 helper 是 single point, 加新字段 schema 自动 propagate.
注意: 本 helper 不 take ownership of body — caller 已 own bytes. idempotency_key / caller_key_id 接 String 而非 &str 让 caller 决定 是 clone 还是 move.