#[non_exhaustive]pub enum LimitReason {
AccIdWhitelist {
id: u64,
allowed_count: usize,
},
MarketWhitelist {
requested: String,
allowed_count: usize,
},
SymbolWhitelist {
requested: String,
},
TrdSideWhitelist {
requested: String,
allowed_count: usize,
},
HoursOutsideWindow {
spec: String,
now_hhmm: String,
},
HoursInvalidSpec {
spec: String,
err: String,
},
PerOrderCap {
value: f64,
cap: f64,
},
RateLimit {
recent: u32,
cap: u32,
},
DailyCap {
next: f64,
cap: f64,
current: f64,
add: f64,
},
}Expand description
v1.4.106 codex 0542 F2 [P2 SECURITY]: 限额拒绝原因 typed enum.
三层语义视图同 reject 不同消费方:
| 视图 | 用途 | 是否含 PII / 敏感细节 |
|---|---|---|
Self::public_message | client error body (REST 403/429 JSON, gRPC Status.message) | 不含 — 只说 “rejected by |
Self::audit_message | audit log + tracing (内部 ops 用) | 含 — daily 27000.00 > 25000.00 等数值 |
Self::metric_label | Prometheus reason label (固定桶) | 不含 — 固定 8 字串集合 |
设计动机 (v1.4.105 之前):
老代码 LimitOutcome::*Reject(String) 把 format!("daily value 27000.00 > 25000.00 (current=18000.00 + order=9000.00)")
同字符串既给 client (HTTP body) 又给 audit log 又给 prometheus reason
(走 crate::metrics::classify_limit_reason 字符串前缀分桶). 三个 leak:
- client 看到 user 内部数值 — 攻击者撞 daily cap 时能精确推出 cap threshold 与当前累计 (cap=25000, current=18000 → 还能下 7000).
- prometheus reason label 字符串前缀分桶 — 任何 reason format 漂移
(e.g. 加 “, retry after 60s”) 落到
other桶, dashboard 静默断流. 维护需 “新增 reason category 时同步改 classify_limit_reason match arm” — 易漂. - audit log 与 client message 无法独立演进 — 想给 audit 加更细节 时, 等同于给 client error body 也加, surface mismatch.
F2 修法: typed enum + 3 个 method, 各自只 emit 自己 surface 需要的
信息. caller 用 match 编译期穷举确保不漏 surface, [crate::metrics:: classify_limit_reason] 仍保留 (作向后兼容兜底字符串路径), 但新代码走
LimitReason::metric_label() 拿固定桶名.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
AccIdWhitelist
per-key acc_id 白名单拒 (403). id = 实际请求 acc_id, allowed_count
= 配置白名单 entry 数 (audit 用; client surface 不展示).
MarketWhitelist
市场白名单拒 (403). requested = 请求 market (e.g. “US”),
allowed_count = 配置 set size.
SymbolWhitelist
品种白名单拒 (403). requested = 请求 symbol (e.g. “HK.09988”),
不返 allowed list (太长, 也 leak 内部允许品种).
TrdSideWhitelist
交易方向白名单拒 (403). requested = 请求 side (“BUY” / “SELL” /
“SELL_SHORT” / “BUY_BACK”), allowed_count = 配置 set size.
HoursOutsideWindow
时间窗外拒 (429 — 用户之后再试). spec = 配置 (e.g. “09:30-16:00”),
now_hhmm = 当前 local time (e.g. “08:15”).
HoursInvalidSpec
时间窗 spec 解析失败 (429 — 配置 bug). spec = 原 string, err = 解析错.
PerOrderCap
单笔上限超 (403). value = 请求金额, cap = 配置 per-order cap.
RateLimit
per-minute 速率超 (429). recent = 60s 内已下单数, cap = 配置 cap.
DailyCap
日累计超 (429). next = 累加后 total, cap = 配置 daily cap, current
= 累加前 total, add = 本次金额.
Implementations§
Source§impl LimitReason
impl LimitReason
Sourcepub fn public_message(&self) -> String
pub fn public_message(&self) -> String
client surface (REST 403/429 JSON / gRPC Status.message): 只说
“rejected by
Sourcepub fn audit_message(&self) -> String
pub fn audit_message(&self) -> String
audit / log surface (内部 ops, full detail), 含数值 + threshold.
与 v1.4.105 之前 Reject(String) 字符串内容兼容 — 保留前缀 (e.g.
"rate limit exceeded:") 让 crate::metrics::classify_limit_reason
字符串桶继续命中 (向后兼容已有 dashboard).
Sourcepub fn metric_label(&self) -> &'static str
pub fn metric_label(&self) -> &'static str
prometheus surface: 固定 8 字串集合, 任何漂移 (audit_message format
改) 都不影响 dashboard. 与 crate::metrics::classify_limit_reason 字符串
前缀分桶保持 1-1 对应, 但典型化为编译期穷举.
Sourcepub fn http_status_code(&self) -> u16
pub fn http_status_code(&self) -> u16
HTTP status code: 429 (rate-like, retry) vs 403 (whitelist/value, don’t retry).
Trait Implementations§
Source§impl Clone for LimitReason
impl Clone for LimitReason
Source§fn clone(&self) -> LimitReason
fn clone(&self) -> LimitReason
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more