← 中文资源

简体中文版本

带时钟偏差校正的 Retry-After 解析器

把 Retry-After 的秒数或 HTTP 日期解析为毫秒,支持上限截断并用服务器时间校正时钟偏差。

阅读英文原文

功能说明

该示例解析 HTTP 响应中的 Retry-After 头,兼容两种合法形式:非负整数秒,以及 HTTP-date 日期字符串。返回值统一为毫秒延迟,便于直接交给定时器或退避调度器。对于日期形式,可以传入响应 Date 头解析出的 serverDate 作为参考时间,从而抵消客户端与服务器之间的时钟偏差,避免过早重试或等待过久。

关键代码

函数先校验 now、maxMs 与 serverDate,再对字符串做 trim。纯数字分支用 Number.isSafeInteger 排除超出安全整数范围的秒数,并乘以一千后与 maxMs 取较小值。日期分支用 Date.parse 解析,失败返回 null;随后以 serverDate 或本地 now 为基准计算差值,把负值钳制为零,再用 maxMs 截断。所有无效输入统一返回 null,方便调用方回退到默认退避策略。

使用注意

Retry-After 只表示服务器建议的最早重试时间,不应替代指数退避与抖动;多个客户端同时遵循同一秒数仍可能形成重试风暴。日期解析依赖运行时对 HTTP-date 的支持,遇到非标准格式会返回 null。maxMs 默认一天,防止异常响应导致超长等待。该函数不读取响应头也不发起请求,大小写与头部合并需由调用方处理。

验证方式

用 node:test 验证:传入 120 返回 120000;传入未来日期并在 serverDate 固定的情况下得到预期差值;传入过去日期返回 0;传入远超上限的秒数被 maxMs 截断;传入空串、负数字符串、随机文本与数字类型均返回 null;now 非法时抛 RangeError。

代码示例

const MAX_DELAY_MS = 24 * 60 * 60 * 1000;

export function parseRetryAfter(value, options = {}) {
  if (typeof value !== "string") return null;
  const { now = Date.now(), serverDate, maxMs = MAX_DELAY_MS } = options;
  if (!Number.isFinite(now)) throw new RangeError("now must be a finite timestamp");
  if (!Number.isFinite(maxMs) || maxMs < 0) {
    throw new RangeError("maxMs must be a non-negative finite number");
  }
  if (serverDate !== undefined && !Number.isFinite(serverDate)) {
    throw new RangeError("serverDate must be a finite timestamp");
  }

  const trimmed = value.trim();
  if (trimmed === "") return null;

  if (/^\d+$/.test(trimmed)) {
    const seconds = Number(trimmed);
    if (!Number.isSafeInteger(seconds)) return null;
    return Math.min(seconds * 1000, maxMs);
  }

  const target = Date.parse(trimmed);
  if (Number.isNaN(target)) return null;
  const reference = serverDate === undefined ? now : serverDate;
  return Math.min(Math.max(0, target - reference), maxMs);
}