1. 背景
本文主要介绍如何使用nodejs搭建http(s)中转服务,本文所写内容仅用于学习为目的,请勿作为其他用途使用。阅读本文之前,请先了解:
- 如何自建证书,可以参考这里
- 如何使用 nodejs 创建http(s) 服务器
- 如何使用 nodejs 创建 tls 服务器
- 学习 http 协议的格式及请求类型
2. 实现步骤
2.1 http中转思路
- 创建 http(s) 服务器作为 proxy server
- 监听request请求
- 通过 node-fetch 发送并取到数据之后将其返回给客户端即可
2.2 https中转思路
- 创建 http(s) 服务器作为 proxy server
- 创建 tls 服务器,tls server
- 劫持所有请求到 proxy server
- 监听connect请求,将所有 connect 请求,全部发送到 tls server
- tls server根据客户端请求,使用 axios 发送请求到目标服务器
- 通过 node-fetch 发送并取到数据之后,将其返回给 proxy server 的 socket,这样客户端便能拿到数据
- 客户端拿到了目标服务器的内容,然后正常显示其内容
3 常见问题
3.1 websocket
针对websocket需做特殊处理,请学习websocket协议。
3.2 二次转发
请学习connect请求的格式。
3.3 内容被截断
请关注 content-length 响应头。
3.4 关于响应数据的压缩
请学习 zlib 库的使用方法。
示例:
const zlib = require('zlib')
async function compressResonpseData (contentEncoding, respBuffer) {
const NAME = 'compressResonpseData'
return new Promise((resolve, reject) => {
let zlibStream = null
const respDataStream = Readable.from([respBuffer])
let compressedData = Buffer.alloc(0)
const zlibCallback = err => {
if (err) return reject(`${NAME} 管道传送失败: ${err.message}`)
}
if (/\bdeflate\b/.test(contentEncoding)) {
zlibStream = pipeline(
respDataStream,
zlib.createDeflate(),
zlibCallback
)
} else if (/\bgzip\b/.test(contentEncoding)) {
zlibStream = pipeline(respDataStream, zlib.createGzip(), zlibCallback)
} else if (/\bbr\b/.test(contentEncoding)) {
zlibStream = pipeline(
respDataStream,
zlib.createBrotliCompress(),
zlibCallback
)
}
if (zlibStream) {
zlibStream
.on('data', data => {
compressedData = Buffer.concat([compressedData, data])
})
.on('end', () => {
resolve(compressedData)
})
} else {
resolve(respBuffer)
}
})
}
3.5 二级转发请不要使用axios
axios 对代理的支持有些问题,此处推荐node-fetch 。
|