通过 HTTP CONNECT 使用 createConnection 的 Node.js HTTP/2
完成 CONNECT,用 h2 协商目标 TLS,然后用 createConnection 让 http2.connect 留在该流上。
HTTP/1.1 代理可以在 CONNECT 内承载目标的 HTTP/2 帧。代理不必实现这些内部帧,但客户端必须按顺序完成协议交接。
在验证完整的 CONNECT 应答并保留 tunnelSocket 之后,加上目标 TLS,并把已建立的流交给 HTTP/2:
import tls from 'node:tls';
import http2 from 'node:http2';
const secureSocket = tls.connect({
socket: tunnelSocket,
servername: 'api.example',
ALPNProtocols: ['h2'],
});
secureSocket.once('error', failRoute);
secureSocket.once('secureConnect', () => {
if (secureSocket.alpnProtocol !== 'h2') {
secureSocket.destroy();
failRoute(new Error('Destination did not negotiate h2'));
return;
}
const session = http2.connect('https://api.example', {
createConnection: () => secureSocket,
});
session.on('error', failRoute);
useSession(session);
});createConnection 返回的是代理已经承载的 TLS 流,而不是再开一个直连套接字。目标的 servername 提供 SNI 与证书身份,校验保持开启。ALPN 检查会拒绝没有协商 h2 的目标。
failRoute 必须清理路由;useSession 负责请求与关闭会话。请围绕这段代码设置连接与操作超时。
外层连接器仍然负责 host:port 形式的 CONNECT 目标、代理认证、拒绝处理以及头之后缓冲的字节。把这些做完再把套接字交给 TLS;直接发给 HTTP/1.1 代理监听器的 HTTP/2 前导会从错误的层开始。