tls: native pipe / splice between TLS and another native stream or fd
还没有人认领这个 Issue。
评估
- 难度
- 5/5
- 预计耗时
- 一周以上
- 新手友好度
- 30/100
- Issue 类型
- 功能
- 描述清晰度
- 基本清楚
- 活跃度
- 冷清
- 技术栈
- javascript, node.js
- 领域
- networking, security
调研方向
先从 issue 中描述的 TLSWrap 以及现有的 net.Socket pipe/splice 行为入手,然后跟踪 tls.connect 会话入口点。在实现之前,确定原生 fd 桥接的设计和所有权语义。完成的标准是:双工转发无需为每个 chunk 使用 JavaScript 处理程序,能够传播 backpressure,保留会话设置,并满足所述的转发和 benchmark 标准。
由索引模型根据 Issue 内容生成。
描述
What is the problem this feature will solve?
net.Socket supports efficient kernel-level forwarding patterns (socket.pipe(), socket.pause()/resume() with native backpressure). There is no TLS equivalent — forwarding cleartext through TLSSocket always goes through the streams layer:
TCP (encrypted) → TLSWrap::ClearOut → JS Readable → user pump → JS Writable → sink
For full-duplex bridges (TLS ↔ TUN fd, TLS ↔ pipe, TLS ↔ another TCP socket), userland must:
- Implement two pumps (encrypt direction + decrypt direction) in JavaScript or duplicate logic in a native addon
- Manually coordinate backpressure (pause/resume, 'drain', TUN poll pause) across heterogeneous endpoints
- Absorb per-chunk copies and event-loop latency from TLSWrap (see related issues on SetImmediate deferral and read copies)
net has splice-style optimizations between fds; TLSWrap sits in the middle with no supported way to wire cleartext directly to a native sink/source while keeping session setup in Node.
This forces ecosystem projects (VPN helpers, transparent proxies, iOS tunnel tooling) to ship custom OpenSSL forwarders instead of composing built-in APIs.
What is the feature you are proposing to solve the problem?
Add native TLS pipe/splice primitives that pump cleartext between an established TLSSocket (or tls.connect session) and another native I/O endpoint without per-chunk JavaScript involvement.
Proposed API (sketch):
import tls from 'node:tls';
import net from 'node:net';
const tcp = await net.connect({ port });
const tlsSocket = await tls.connect({ socket: tcp, ... });
// Duplex: TLS cleartext ↔ numeric fd (TUN, pipe, etc.)
const handle = tlsSocket.spliceTo({
fd: tunFd,
direction: 'duplex', // 'in' | 'out' | 'duplex'
});
handle.start();
await handle.stop(); // idempotent cleanup
// Or one-shot helper:
await tlsSocket.pipeToNative(tunFd, { direction: 'duplex' });
Implementation outline (on TLSWrap):
- Decrypt path (TLS → sink): SSL_read loop → write cleartext to sink fd; on EAGAIN/EWOULDBLOCK, pause uv_read_start on the underlying TCP stream until sink is writable (native backpressure, not socket.pause() in JS).
- Encrypt path (source → TLS): read cleartext from source fd → SSL_write → EncOut → TCP; stall source read when SSL_write or TCP send buffer is full.
- Reuse existing TLSWrap session, handshake, cert/PSK options — only the payload pump is native.
- Clear ownership semantics for fds (caller retains TUN; bridge does not close unless autoClose: true).
Relation to other proposals:
- Lighter-weight than full tls.createBridge() when one side is already a TLSSocket and the other is an fd.
- Complements zero-copy onread for users who still want one direction in JS.
Success criteria:
- Bidirectional MTU-sized forwarding without socket.on('data') handlers.
- Backpressure propagates correctly (no unbounded buffering in pending_cleartext_input_ / userland).
- Benchmark shows lower CPU and event-loop utilization vs. an equivalent JS pump.
What alternatives have you considered?
- socket.pipe(otherSocket) through TLSSocket — Still routes every byte through JS streams; does not splice to raw fds; no TLS-aware backpressure.
- duplex streams + pipeline() — Same V8-boundary and allocation costs; popular but not a performance solution.
- tls.createBridge() (separate proposal) — Higher-level API that may subsume this for fd targets; spliceTo is a narrower addition for users who already have a TLSSocket and want fd bridging only.
- Custom N-API OpenSSL forwarder — Proven in production (e.g. iOS tunnel addons) but duplicates TLSWrap and OpenSSL linkage in every consumer.
- node:child_process + socat/openssl s_client — Operational hack, not embeddable in Node apps.
- Document manual pump patterns only — Insufficient; the gap is missing native wiring in TLSWrap, not developer skill.
- 主要语言
- JavaScript
- 星标
- 122k
- 派生
- 37.4k
- 平均合并
- 4 天 3 小时
- 30 天内合并 PR
- 279
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
nodejs/node 的其他 Issue
-
doc
难度 2/5 1-3 小时 新手友好度 65/100
-
build
难度 1/5 1 小时以内 新手友好度 88/100
-
难度 2/5 1-3 小时 新手友好度 84/100
-
难度 1/5 1 小时以内 新手友好度 90/100
-
feature request
难度 2/5 1-3 小时 新手友好度 68/100
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 65/100
-
难度 2/5 1-3 小时 新手友好度 75/100
docToolchain/docToolchain#1705 ·
-
Timezone select lists one option per character; duplicate "Failed" reason; shared tracker popover id 未关闭bug
难度 2/5 1-3 小时 新手友好度 75/100
nightscout/nocturne#1414 ·
-
bug v2
难度 2/5 1-3 小时 新手友好度 75/100
modelcontextprotocol/inspector#2458 · 1 条评论 ·
-
Mend: dependency security vulnerability status: needs triage 🕵️♀️
难度 2/5 1-3 小时 新手友好度 70/100
carbon-design-system/ibm-products#9907 ·