ode助手的bug,已上报
下面这份可以直接复制上报,按标准 issue 模板组织,包含环境、复现步骤、根因定位(含代码位置)和最小修复建议。
—
# 缺陷报告:ODE 多功能助手 1.6.3 —「继续下载」必然失败(远程探测 HTTP 400 / ECONNRESET)
## 一、摘要
下载列表中 `downstate` 为「已暂停」或「下载失败」的任务,点击「继续下载」后**必然**在“远程探测”阶段失败,报 `远程探测失败:HTTP 400 Bad Request` 或 `远程探测失败:read ECONNRESET`,文件 0 字节写入。同一条 URL、同一 Cookie 用其他 HTTP 客户端请求可正常返回 `206`。已定位为 `odeContinueByGid` 传递 `downHeaders` 类型错误,复现率 100%。
## 二、环境
– 系统:Windows 10/11 x64
– 客户端:ODE 多功能助手 **1.6.3**(`%LOCALAPPDATA%\Programs\ODE`)
– `resources\app.asar`:39,281,615 字节;`background.js`:2,300,756 字节
– 下载引擎:`downloadEngine = “ode”`,`chunkSize = 522240`,`concurrency` 写库为 128(引擎内取 `min(64, …)`)
– 资源:夸克网盘直链多分卷(`dl-pc-zb.drive.quark.cn`),单卷约 4.28 GB
## 三、复现步骤
1. 通过「解析链接」下载一个多分卷夸克资源;
2. 下载过程中点击「暂停」,或让其因网络波动进入「下载失败」;
3. 在下载列表中点击该任务(前端对「已暂停/下载失败」状态会触发 `continueDown`);
4. 任务立即变为「下载失败」,`downlist.erron` 写入上述错误。
**复现率:100%**(本机 10 个任务全部命中;单任务重试、重启客户端后重试均复现)。
补充特征:失败瞬间对应 `.ode` 文件的 mtime **完全不变**,说明在打开/写入下载文件之前就已失败,即失败发生在远端探测阶段。
## 四、根因定位
`background.js` 中 `odeContinueByGid` 将请求头对象序列化为字符串后传入任务:
“`js
var a = /* 解析 t.headers 得到的对象 */;
var r = JSON.stringify(a); // r 是字符串
var i = F({ url: t.downurl, downHeaders: r, … }); // ← 传入了字符串
“`
构造函数未做类型处理:
“`js
var a = e.downHeaders || { “User-Agent”: t };
this.downHeaders = a; // 此处是字符串
“`
`_probeAndCheckLink()` 中对该值做 `Object.assign`,字符串被按字符展开:
“`js
var a = Object.assign({}, this.downHeaders, { Range: “bytes=0-1” });
“`
结果生成了 **2815 个单字符“请求头”**(`0={`、`1=”`、`2=u` …,数量等于 JSON 字符串长度),请求头块远超服务器上限。
用 Node.js 复刻该请求,夸克 CDN 返回:
“`
HTTP 400 Request Header Or Cookie Too Large
“`
**对照组**(同一 URL、同一 UA、同一 Cookie、同一 `Range: bytes=0-1`):
| 请求方式 | 结果 |
|—|—|
| Python urllib(正常 headers 对象) | `206`,`Content-Range: bytes 0-1/4284229877` |
| Node https(正常 headers 对象) | `206` |
| Node https(`Object.assign({}, JSON字符串, …)`,即客户端现行为) | `400 Request Header Or Cookie Too Large` |
由此可确认:报错并非链接失效、Cookie 过期或网络问题,而是请求头构造错误;`read ECONNRESET` 是同一原因下服务器直接断开连接的另一种表现。
## 五、修复建议
**最小改动(1 字节):**
“`js
– var r = JSON.stringify(a), i = F({ url: t.downurl, downHeaders: r, … });
+ var i = F({ url: t.downurl, downHeaders: a, … });
“`
**建议补充的防御性处理**(兼容历史调用路径,避免同类问题):
“`js
this.downHeaders = (typeof a === “string”)
? (() => { try { return JSON.parse(a) } catch (e) { return {} } })()
: a;
“`
**同时建议修复的关联问题:**
1. `_probeAndCheckLink()` 失败后**不重试**(`retryLimit` 仅作用于分片下载),单次探测失败即永久判定「下载失败」;建议探测同样重试 2~3 次并加退避。
2. 重新解析同一资源时会生成 `xxx(1).rar` 新任务,**不复用已有 `.ode`**,导致已下载进度作废并重复占用磁盘(本次多出 16.33 GB 冗余文件)。
3. `downlist.erron` 仅保存状态码,建议附带 request host 与响应体前 200 字节,便于用户侧和开发者定位。
## 六、影响面
中等偏高。所有 ode 引擎任务只要经历「暂停→继续」或「失败→重试」,都会失败且无法自行恢复;用户只能重新解析链接从头下载(单卷 4.28 GB,进度全丢)。该问题在 1.6.3 稳定复现,建议列为下个版本的高优先级修复。
## 七、可附的现场证据
– `%APPDATA%\ODE 多功能助手\ODE.db` → `downlist` 表 `id 100~109` 的 `erron` 字段(10 条均为上述两类错误)
– 失败时 `.ode` 文件 mtime 无变化;其头部位图可读出真实进度(27.08% / 96.34% / 96.95% / 97.23%),可用于证明“失败发生在探测阶段、与已下载数据无关”
– `logs\main.log` 无下载相关记录(仅更新器日志),建议后续把引擎 `console.log` 落盘
—
需要的话我可以再出一版**纯文本(无 Markdown 标记)**的,方便贴到 QQ 群、论坛或工单系统;也可以把 `erron` 那 10 条记录导出成一张表附在报告后面。
