Got 流式请求带 CookieJar 后一直等不到 end
快速响应在等待 CookieJar 写入时结束,旧 Got 流可能错过结束事件。核对正式实现的监听顺序,保留 Cookie 保存、流消费和异常传播。
got.stream() 下载普通响应正常,一加 cookieJar,收到 Set-Cookie 后却一直没有 end。若正文很短、服务器已结束响应,应检查 Cookie 存储的等待过程与结束监听器的注册顺序。
Got 15.0.0 在处理 Set-Cookie 时先等待 CookieJar 的异步写入,随后才注册响应 end 监听器。快速响应可能在等待期间已经读完,后加的监听器收不到过去的事件,外层可读流就无法正常结束。
先排除没有消费流和没有完成的存储
可读流没有被读取时,不能只监听 end 就等待下载完成。调用方应沿原有保存、解析或丢弃路径消费正文,并处理 error。另一方面,自定义 CookieJar 如果 setCookie 永不完成,升级响应监听顺序也不能让这次写入自动结束。
因此,先判断正文是否被消费、CookieJar 的写入 Promise 是否完成、服务端是否确实发送完整响应。不要为诊断打印 Cookie 值;记录状态、时序和必要的非敏感标识即可。
采用提前监听且补查结束状态的正式实现
Got 16.0.0 与 16.1.0 已在等待 CookieJar 前注册 end,并通过 canFinalizeResponse 控制外层结束:既要知道正文已读完,也要等 Cookie 处理完成。后续还会检查 readableEnded,覆盖早已结束的情况。
这为命中旧路径的应用提供了正式升级方向,不据两个版本对照推定最早修复版本。升级前核对运行时和选项兼容,16.1.0 要求 Node 22 或更新。不能把关闭 cookieJar 或忽略所有无效 Cookie 当成修复,那会改变会话和认证行为。
使用流式管道的项目,可保留类似下面的错误传播方式。url、cookieJar、destination 都来自应用现有的已审定配置:
import got from 'got';
import { pipeline } from 'node:stream/promises';
export async function download(url, cookieJar, destination, signal) {
await pipeline(
got.stream(url, {
cookieJar,
signal,
timeout: { request: 10000 },
}),
destination,
);
}
一万毫秒是示例时限,需符合实际下载大小与服务策略。destination 必须是本次操作可写的目标流;pipeline 会在结束或失败时处理流生命周期,不适合不希望被结束的共享输出流。这里用它等待完整消费并传递失败,不能用它替代缺陷版本的升级。
若业务不需要正文而选择 resume 丢弃,也要保留 error 和完成处理。不能在收到 response 事件时就认定下载成功:它只表示响应可用,正文与 Cookie 存储可能还没结束。
回归应包括短正文加 Set-Cookie、无 Cookie、较大正文、CookieJar 写入拒绝和中途取消。确认流按预期完成或失败,并检查后续请求使用到的 Cookie;只让外层等待函数提前返回,不能证明会话已保存。


