Skip to content

缓存代理对外服务:字幕缓存名防越界,修 Range 下溢与截短块空转,整理与补测试 - #10

Merged
stqfdyr merged 8 commits into
mainfrom
opt/M05-serve
Nov 14, 2026
Merged

stqfdyr merged 8 commits into
mainfrom
opt/M05-serve

Conversation

@stqfdyr

@stqfdyr stqfdyr commented Nov 14, 2026

Copy link
Copy Markdown
Owner

本地缓存代理对外服务这一块的整理,含一处安全修复。

安全

  • 外挂字幕的缓存文件名以服务器给的编码名为扩展名,原样拼进路径:编码名里带 /、\、.. 时能把内容写到字幕缓存目录以外(Windows 上可写到任意位置)。登记时文件名只保留字母数字和 ._-,全是点或为空时改用随机 key。

修复

  • Range: bytes=5-3(终点小于起点)会让 Content-Length 下溢;按 RFC 当作没有 Range、回整个文件。
  • 磁盘上被截短的已完成块:读任务一直重读、空转到程序退出。repair 改为核对大小,读循环每轮检查播放器是否已断开。
  • 接受连接出错(句柄耗尽等)时立即重试会占满一个核,改为等 100 ms。
  • 第一次读一个文件(还在等总大小)时,块尾 1 MiB 的预览没有连下一块一起取;与后续读取共用 wanted。

整理

  • 读循环拆出 locate,接受连接拆出 accept;Role::Thumb / Role::Prefetch 调度上早无区别,并为 Role::Preview。
  • 响应头用 HeaderValue::from,去掉 parse().unwrap();等总大小的上限提成 SIZE_WAIT(编译期断言长于 OPEN_LIMIT)。
  • 字幕缓存命中时的读与刷新修改时间复用图片缓存的 cached(阻塞池里做),写入改 tokio::fs。
  • stream.rs 的注释全部改英文(日志与错误文案保持中文)。

测试

  • 退避类测试(反复断线、断网、5xx、429、预览失败)原来断言整个读的总耗时,机器一忙就超;改为断言「上游断开到下一次请求」的间隔,翻倍退避照样能抓到。
  • 按变异测试补测试:405 / HEAD / key 种类、跨块边界读、块尾预览取两块、同名重新登记换地址、字幕只重试暂时性失败且有上限、正在写的块边下边读、上游 403 后已缓存的块照常读、预取遇拒绝只请求一次、主任务碰到已下好的块断开重开、别处被拒绝时不断开读着在跑任务的连接、每次 retain 都重算占用等。

验证

  • cargo test 80 项、cargo clippy --all-targets -- -D warnings、cargo fmt --check 通过;Linux 上 stream 测试连跑 10 轮无失败。
  • 未跑:真服端到端、Windows CI。

- 字幕缓存名的扩展名取自服务器给的编码名,含 / \ .. 时能写到字幕缓存目录以外;登记时只留字母数字和 ._-
- bytes=5-3 这类终点小于起点的 Range 当没有,原来 Content-Length 下溢
- 磁盘上被截短的已完成块:repair 只看文件在不在,读任务会一直重读;改为核对大小,读循环每轮检查 mpv 是否已断开
- read_loop 的「读文件 / 读 .part / 等下载」判断拆成 locate,accept 循环拆成 accept;accept 出错后睡 100 ms 再接(原来句柄耗尽时空转)
- Role::Thumb 与 Role::Prefetch 调度上早已无区别,并为 Role::Preview
- 响应头用 HeaderValue::from,去掉 parse().unwrap();等总大小的 35 秒提成 SIZE_WAIT
- 字幕缓存命中的读与刷新时间复用 images::cached,写入改 tokio::fs,不再在异步线程上做阻塞 IO
- stream.rs 的模块说明与本块注释改英文
- 反复断线、断网、5xx、429、预览失败五个测试原来断言整个读的总耗时,里面含搬数据的时间,机器一忙就超(约 4 秒的等待实测到 9.9 秒);
  测试上游在每次断开 / 拒绝时记下时刻,改为断言「断开到下一次请求」的间隔(retry_delays),翻倍退避照样抓得到
- 对冲测试的上限放宽到 10 秒(只需区分 20 秒的卡住)
- e2e_ui_shots:这一集没有外挂字幕时明确提示「没验到」,不再只打印 0 条
- 第一次读一个文件(等总大小时)安排下载只取一块,没走「块的最后 1 MiB 连下一块」的规则;提成 wanted 两处共用
- 补测试:405、HEAD 长度、key 种类不符 404、跨块边界的读、块尾预览取两块、同名重新登记换地址、字幕只重试暂时性失败、
  截短的是第 1 块、已缓存的块在上游 403 后照常读;resolve 的空文件 / 空后缀;SIZE_WAIT 必须长于 OPEN_LIMIT(编译期断言)
变异测试复跑(此前超时设得太紧,有漏网被记成超时)后补的:
- 主任务旁边 MAIN_NEAR 块内的读等它,不另开任务
- 预取遇到上游拒绝只请求一次
- 主任务碰到已下好的块断开、从下一个缺块重开
- 别处的任务被拒绝时,读着正在跑的任务的数据的连接不断(St::busy)
- 每次 retain 都重算占用,没有目录可删时也算
- wanted 的块尾 1 MiB 边界
- 正在写的块边下边读(慢上游下第一段数据不等整块)
- 字幕一直遇到暂时性失败时重试有上限(RETRIES)
@stqfdyr
stqfdyr merged commit 06b7b92 into main Nov 14, 2026
@stqfdyr
stqfdyr deleted the opt/M05-serve branch November 14, 2026 23:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant