惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

SecWiki News
SecWiki News
I
InfoQ
The Cloudflare Blog
人人都是产品经理
人人都是产品经理
博客园 - Franky
T
Tailwind CSS Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
量子位
博客园_首页
罗磊的独立博客
V
V2EX
李成银的技术随笔
大猫的无限游戏
大猫的无限游戏
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
True Tiger Recordings
Vercel News
Vercel News
Cyberwarzone
Cyberwarzone
Cisco Talos Blog
Cisco Talos Blog
F
Fox-IT International blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
M
Microsoft Research Blog - Microsoft Research
Know Your Adversary
Know Your Adversary
爱范儿
爱范儿
The Register - Security
The Register - Security
G
Google Developers Blog
The Hacker News
The Hacker News
Malwarebytes
Malwarebytes
S
Securelist
博客园 - 三生石上(FineUI控件)
Jina AI
Jina AI
T
Threat Research - Cisco Blogs
T
The Exploit Database - CXSecurity.com
S
SegmentFault 最新的问题
博客园 - 叶小钗
F
Fortinet All Blogs
Apple Machine Learning Research
Apple Machine Learning Research
宝玉的分享
宝玉的分享
博客园 - 聂微东
T
Threatpost
博客园 - 【当耐特】
D
Docker
P
Privacy & Cybersecurity Law Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
G
GRAHAM CLULEY
V
Visual Studio Blog
C
Cisco Blogs
IT之家
IT之家
S
Security Archives - TechRepublic
Latest news
Latest news
阮一峰的网络日志
阮一峰的网络日志

Claude's Blog

浅谈 npm 依赖治理 如何管理桌面窗口 SSR 页面 CDN 缓存实践 从 is-promise 事件我们可以学到什么 分享一个 npm dist-tag 的冷知识 从 Vimium 到 qutebrowser Verdaccio 性能优化:单机 Cluster 让 npm install 不使用缓存的方法 Verdaccio 性能优化:代理分流 Verdaccio 性能优化:上游路径转发 CSAPP DataLab 题解 不靠谱的 Egg.js 框架开发指南 如何解决 Debian 系 Elastic apm-server 7.x 启动失败 Hexo NexT 主题升级 7.4 From Journeyman to Master Mac 上移除 EasyConnect 常驻后台进程 Nginx SWRR 算法解读 记一次 Node.js 进程挂起的 BUG 追踪 警惕 Travis CI 的 npm 缓存
记录 Got(Node.js) 代理 HTTP 请求的坑
2021-01-27 · via Claude's Blog

在过去,request 模块几乎是 Node.js 端的不二选择,可惜已被放弃维护。如今流行的模块虽然变多,但不意味着它们足够成熟,我还是倾向于专注 Node.js 端的那几个。

需求越简单,选择越不重要。不过相较而言,Got 的接口设计看起来更友好,并且它是做到支持 Connection Timeout 和 Read Timeout 的少数。

https://github.com/sindresorhus/got/#comparison 的 Advanced timeouts

实际用下来,还是遇到了坑,顺便扒了一眼 Got 的代码。

native http?

先解释一下为什么不用原生 http 模块吧。

毕竟在 Node.js 提供代理服务是非常容易的事儿,普遍的优化无非是:

  • 使用 http.Agent。用它来支持 keepAlive,关闭 Nagle 算法等等。
  • 使用 stream.pipe()。相对于“接收-等待-发送”的模式,一方面主要是节省内存,另一方面可以减少等待,提高传输效率(但也不是绝对的)。

不用安装第三方依赖,基于原生 http 写上十来行代码即可完工,所以我自己很喜欢直接用 http 做一些工作。

但如果代理需求变得复杂,使用现成的轮子才能利于队友们(也包括自己)维护。

decompress

Got 默认对响应执行 decompress,对于代理而言毫无意义,需要关掉。

1
2
3
4
5
6
7
async _onResponseBase(response: IncomingMessageWithTimings): Promise<void> {
const {options} = this;

if (options.decompress) {
response = decompressResponse(response);
}
}

这在 decompress 的文档上有说明,但是一点儿都不醒目。

If this is disabled, a compressed response is returned as a Buffer. This may be useful if you want to handle decompression yourself or stream the raw compressed data.

最坑的是,我是因为遇到了 stream 的 bug (#issues/1279) 才注意到这个选项。在开启 decompress 的情况下,使响应值的 content-length 是错误的,该 bug 会导致返回的结果不完整。

accept-encoding

关闭 decompress 之后,响应开始变慢到肉眼可见得慢。

原因如下:

1
2
3
4
5
6
7
8
async _makeRequest(): Promise<void> {
const {options} = this;
const {headers} = options;

if (options.decompress && is.undefined(headers['accept-encoding'])) {
headers['accept-encoding'] = supportsBrotli ? 'gzip, deflate, br' : 'gzip, deflate';
}
}

accept-encoding 只在 decompress 为 true 的时候设置,否则无法享用 gzip 等压缩带来的优化。

solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const got = require('got');
const HttpAgent = require('agentkeepalive');
const HttpsAgent = HttpAgent.HttpsAgent;

const sec = 1000;
const min = 60 * sec;

const supportsBrotli = process.versions.brotli;

const request = got.extend({
agent: {
http: new HttpAgent(),
https: new HttpsAgent(),
},


decompress: false,

headers: {
'accept-encoding': supportsBrotli ? 'gzip, deflate, br' : 'gzip, deflate',
},
timeout: {
connect: 3 * sec,
socket: 2 * min,
},
});

这大概率不是最终版,立一个 flag,如果再遇到问题,就自己从头写一个专用于代理场景的 http 库。