在 Node.JS 中间人代理中解码 GZIP 响应

3
我正在使用TypeScript在Node上开发一个MITM代理。我尝试解码使用gzip编码的响应。我不想像在SO上某些答案中看到的那样仅删除accept-encoding头。我想使用zlib解码响应正文,但是当我尝试加载使用gzip(例如github.com)的页面时,页面无法加载(图像,颜色,文本等)。我的解压缩不起作用,我不知道为什么。我用于解压缩响应正文的代码如下:
注意:serverResponse是我(作为代理)连接到的服务器(例如github.com)的响应,而proxyResponse是我(代理)向发起请求的客户端返回的响应。
  protected async receiveResponse(serverResponse: http.IncomingMessage, proxyResponse: http.ServerResponse) {

    const contentEncoding = serverResponse.headers["content-encoding"]
    let responseContent: http.IncomingMessage | zlib.Gunzip = serverResponse

    if (contentEncoding && contentEncoding.toLowerCase().includes("gzip")) {
      responseContent = zlib.createGunzip()
      serverResponse.pipe(responseContent)
      delete serverResponse.headers["content-encoding"]
    }

    let responseBody: Buffer

    try {
      responseBody = await this.collectMessageBody(responseContent)
    } catch (error) {
      console.log(error)
      return
    }
    
    proxyResponse.writeHead(serverResponse.statusCode!, serverResponse.headers)
    proxyResponse.write(responseBody)
    proxyResponse.end()
  }

  private collectMessageBody(stream: http.IncomingMessage | zlib.Gunzip): Promise<Buffer> {
    return new Promise<Buffer>((resolve, reject) => {
      let bodyBuffers: Buffer[] = []

      stream.on('data', chunk => bodyBuffers.push(chunk))
      stream.on('end', () => resolve(Buffer.concat(bodyBuffers)))
      stream.on('error', error => reject(error))
    })
  }

这里的策略与大多数在 SO 上的回答相同,但我不知道我的为什么不起作用。

注意:这是一个开源项目,因此整个文件可以在此处找到:https://github.com/olmps/web-sniffer/blob/master/src/server.ts

我已经简化了逻辑,删除了不必要的内容,以便在此发布。

例如,当加载使用 gzip 压缩其内容的 github.com 时,当代理打开时,我得到以下结果:

enter image description here


似乎资源文件,如样式表没有被加载。 - sdgluck
是的!但我不知道为什么。当我在向Github发送请求之前删除头部的“accept-encoding”时,它可以正常工作,因为显然Github没有压缩响应。但是当我不删除这个标头并尝试在本地解压响应时,就会出现这种结果。我认为这是超出我的知识范围或是我没有看到的非常愚蠢的问题。 - GGirotto
从代码的快速阅读来看,当您发送带有gzip的accept-encoding时,为什么要再次使用zlib.creategunzip()进行编码?要么将响应传递为原样,要么在重新编码之前对其进行解码?当您不发送accept-encoding标头时,您会收到一个原始响应,您可能正在正确地进行编码。 - suv
@suv,您所说的“使用zlib.creategunzip()重新对其进行编码”是什么意思?我正在使用管道,将Gunzip用于解码来自Github服务器的响应,而不是对其进行编码。 - GGirotto
啊,那个函数名有误导性。我刚刚读了文档,你是正确的。creategunzip解码gzip流。你尝试过在控制台打印响应并查看发生了什么吗?我可以想到另一个原因为什么会出现这种情况,console.log应该能够很清楚地说明问题。 - suv
@suv 实际上,解码后的响应正文是来自Github的HTML和JS内容。就像sdgluck在上面所说的一样,样式表由于某些原因未能加载。 - GGirotto
1个回答

1
你可以尝试处理content-length头部。如果content-encodinggzip,你可以尝试使用未压缩的正文长度来更改content-length头部的值,而不仅仅是解压正文。希望这能有所帮助。

直戳要点。调整 content-length 就解决了!SO(Stack Overflow)说我需要等待6个小时才能给你悬赏,所以我稍后会回来完成它。感谢您的解决方案,我实际上很惊讶这里的所有相关答案都没有提到这个细节。 - GGirotto
1
很高兴你解决了问题。奖励没问题,我可以等待 ;) - Daniele Ricci

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接