Javascript 在隧道传输TLS连接时,如何传递附加信息?

Javascript 在隧道传输TLS连接时,如何传递附加信息?,javascript,node.js,http,Javascript,Node.js,Http,我已经创建了一个裸体HTTP代理,它使用 服务器接受HTTPS连接并以“OK”消息响应,而不进一步转发请求 正如您在代码中看到的(请参见//这里我知道目标服务器端口是什么。),我可以在HTTP CONNECT事件处理程序中获取目标服务器的端口。但是,我无法确定如何将此信息传递到createHttpsServerHTTP服务器路由器(请参见//我如何知道此处的目标服务器端口是什么?) 在隧道传输TLS连接时,如何传递附加信息 可以通过运行以下命令来测试上述代码: $ node proxy.js &

我已经创建了一个裸体HTTP代理,它使用

服务器接受HTTPS连接并以“OK”消息响应,而不进一步转发请求

正如您在代码中看到的(请参见
//这里我知道目标服务器端口是什么。
),我可以在HTTP CONNECT事件处理程序中获取目标服务器的端口。但是,我无法确定如何将此信息传递到
createHttpsServer
HTTP服务器路由器(请参见
//我如何知道此处的目标服务器端口是什么?

在隧道传输TLS连接时,如何传递附加信息

可以通过运行以下命令来测试上述代码:

$ node proxy.js &
$ curl --proxy http://localhost:9000 https://localhost:59194/foo.html -k

目标是以“OK localhost:59194”作为响应。

您不能向TLS流添加任何内容(谢天谢地),除非将其隧道传输到另一个协议中⁠—这就是Connect方法已经在做的事情。但是,由于HTTP代理和HTTPS服务器位于同一个代码库中,因此无需再次在网络上抛出TLS流。相反,您希望解析TLS流,然后可以将任何变量传递给处理它的代码

然而,在解析TLS之后,仍然会有一个原始HTTP流,并且需要一个HTTP服务器将其转换为请求并处理响应

快速而肮脏的方法是使用Node的HTTPS服务器来解码TLS和解析HTTP。但是服务器的API没有提供处理已经连接的套接字的功能,而且服务器代码也没有与连接代码完全分离。因此,您需要劫持服务器的内部连接处理逻辑,这当然很容易在将来发生更改时被破坏:

consthttp=require('http');
常量https=require('https');
const pem=需要(“pem”);
const createProxy=(httpsOptions)=>{
const proxy=http.createServer();
on('connect',(请求、请求套接字、头)=>{
const server=https.createServer(httpsOptions,(req,res)=>{
书面记录(200);
res.end(“OK”);
});
emit('connection',requestSocket);
write('HTTP/1.1200连接已建立\r\n\r\n');
});
代理听(9000);
};
常量main=()=>{
创建证书({
天数:365,
自签名:正确
},(错误,{serviceKey,certificate,csr})=>{
创建代理({
ca:csr,
证书:证书,
密钥:serviceKey
});
});
};
main();
为了避免在每次请求时创建HTTPS服务器实例,您可以将实例移出并将数据固定到套接字对象上:

const server=https.createServer(httpsOptions,(req,res)=>{
书面记录(200);
//这里我们将访问保存在tls.TLSSocket对象上的net.Socket实例,
//额外的肮脏
res.end('OK'+请求套接字.\u parent.marker+'\n');
});
on('connect',(请求、请求套接字、头)=>{
requestSocket.marker=Math.random();
emit('connection',requestSocket);
write('HTTP/1.1200连接已建立\r\n\r\n');
});
使用上述代码,如果您连续执行多个请求:

curl——代理http://localhost:9000 https://localhost:59194/foo.html \
https://localhost:59194/foo.html https://localhost:59194/foo.html \
https://localhost:59194/foo.html https://localhost:59194/foo.html -k
然后,您还会注意到它们在单个连接上处理,这很好:

OK 0.6113572936982015
正常0.6113572936982015
正常0.6113572936982015
正常0.6113572936982015
正常0.6113572936982015

我不能完全保证,在代理服务器已经管理套接字的情况下,将套接字交给HTTPS服务器不会破坏任何东西,但在其他方面似乎与插座有着密切的关系。您需要使用运行时间更长的连接来测试它


至于
head
参数:

  • 您可以使用
    requestSocket.unshift(head)
    将其放回流中,但我不确定代理服务器是否会立即使用它
  • 或者,您可以使用
    requestSocket.emit('data',head)
    将其扔到HTTPS服务器上,因为这与事件是互斥的,所以我不确定它们之间是如何工作的
  • 一种解决方案是为
    stream.Duplex
    制作自己的包装,在初始缓冲区存在的情况下,它将转发所有调用和事件,除了
    read()
    ,然后使用此包装代替
    requestSocket
    。但是您还需要复制“数据”事件
  • 最后,您可以尝试创建一个新的双工流,像最初一样写入
    head
    并通过管道将套接字连接到它,然后使用该流代替HTTPS服务器的套接字,但不确定它是否与HTTP服务器对套接字的过于霸道的管理兼容。




一种更干净的方法是解码TLS流,并对生成的HTTP流使用独立的解析器。谢天谢地,Node有一个
tls
模块,该模块被很好地隔离,并将tls套接字转换为常规套接字:

proxy.on('connect',(请求、请求套接字、头)=>{
const httpSocket=new tls.TLSSocket(requestSocket{
是的,
//此变量可用于所有请求,
//因为它通常保存在HTTPS服务器实例上
secureContext:tls.createSecureContext(httpsOptions)
});
...
});
关于复制HTTPS服务器的行为

唉,Node的HTTP解析器不是那么有用:它是一个C库,在套接字和解析器调用之间需要做大量的工作。还有A
$ node proxy.js &
$ curl --proxy http://localhost:9000 https://localhost:59194/foo.html -k