You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Chrome在Connection关闭时无法处理XMLHttpRequest响应问题咨询

Chrome在Keep-Alive连接关闭时返回XHR状态码0的原因及解决方法

这个问题我之前也碰到过,Chrome对HTTP持久连接(Keep-Alive)的关闭处理逻辑确实和其他浏览器有差异,尤其是在复用XMLHttpRequest实例的场景下。

核心原因分析

  • 复用XHR实例的兼容性问题:你的代码里一直在复用同一个this._request实例,虽然XHR规范允许复用实例(重新调用opensend),但Chrome的网络栈在处理服务器发送的Connection: close头部时,会立即终止TCP连接,而复用的XHR实例无法正确处理这种连接主动关闭的场景,导致请求直接失败,返回状态码0且无响应文本。其他浏览器(Firefox/Safari/Opera)在这种情况下会更宽松地处理复用实例,允许完成最后一次响应的解析。
  • Chrome对连接关闭的严格校验:当服务器在第10次响应中发送Connection: close时,Chrome会立即标记该连接为不可用,即使当前请求的响应还在传输过程中,也会终止XHR的处理流程,直接触发error事件或者将readyState设为DONE但状态码为0。而其他浏览器会等待当前响应完全接收后再关闭连接,所以能正常获取响应内容。

解决方案

针对这个问题,有几个可行的修复方向:

1. 每次请求创建新的XHR实例

不要再复用同一个this._request,每次调用send方法时都创建新的XMLHttpRequest对象。这样可以避免旧实例的连接状态影响新请求,Chrome就能正确处理每次请求的连接生命周期:

function send(method, data) {
  try {
    const request = new XMLHttpRequest();
    request.onreadystatechange = (event) => {
      switch (request.readyState) {
        case XMLHttpRequest.OPENED: {
          // Do something
        }
        break;
        case XMLHttpRequest.LOADING: {
          // Do something
        }
        break;
        case XMLHttpRequest.DONE: {
          // 处理响应,包括状态码0的情况
          if (request.status === 0) {
            // 可以在这里重试请求或者提示用户
          } else {
            // 正常处理响应
          }
        }
        break;
      }
    };
    request.addEventListener('error', (event) => {
      // Do something
    });
    request.addEventListener('abort', (event) => {
      // Do something
    });
    request.open(String(method), this.path);
    request.timeout = 200;
    // 设置请求头部等初始化操作
    request.send(data);
    // 如果需要保存实例,可以把request赋值给this._request,但每次请求都替换
    this._request = request;
  } catch (error) {}
}

2. 处理状态码0的重试逻辑

如果一定要复用实例,可以在DONE状态中检测到status === 0时,重新创建实例并发送当前请求。不过这种方式不如每次创建新实例可靠,因为你需要处理连接关闭后的实例状态重置问题。

3. 调整超时时间(可选)

你的代码设置了timeout = 200,这个时间非常短,可能会在服务器关闭连接的过程中触发超时,导致状态码0。可以适当延长超时时间,比如设置为1000或者根据实际场景调整,排除超时因素的影响。

额外说明

Chrome的这种行为其实是符合HTTP规范的,因为Connection: close头部明确要求服务器在响应后关闭连接,Chrome只是更严格地执行了这个要求。而其他浏览器的处理属于“容错性”更强的实现,但从规范角度来说,Chrome的处理并没有问题,只是兼容性上需要我们调整代码来适配。

内容的提问来源于stack exchange,提问作者wemb

火山引擎 最新活动