tls: zero-copy / static-buffer read path through TLSWrap (bypass Readable chunk allocation)
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 42/100
- Loại issue
- Tính năng
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Ít trao đổi
- Công nghệ
- cpp, javascript, node.js
- Lĩnh vực
- backend, networking, performance
Hướng nghiên cứu
Bắt đầu bằng cách đọc TLSWrap::ClearOut() trong src/crypto/crypto_tls.cc và kClearOutChunkSize trong src/crypto/crypto_tls.h, sau đó so sánh đường dẫn TCP dùng static-buffer trong stream_wrap/StreamBase. Xem lại test/parallel/test-tls-onread-static-buffer.js và xác định callback, vòng đời của buffer và hành vi của emitData nên được kiểm thử như thế nào. Được xem là hoàn tất khi đạt được cải thiện đo được về việc cấp phát/sao chép với onread, đồng thời vẫn giữ nguyên đường dẫn streams hiện có.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
What is the problem this feature will solve?
Node supports onread: { buffer, callback } on TCP sockets to avoid the streams 'data' path and reuse a fixed buffer. For TLS, cleartext still goes through TLSWrap::ClearOut() which:
- Reads with SSL_read() into a stack buffer (kClearOutChunkSize = 16384 in src/crypto/crypto_tls.h)
- memcpy into a buffer from EmitAlloc()
- Delivers via EmitRead() → stream listener → JS (src/crypto/crypto_tls.cc)
So TLS consumers doing pump-style I/O (forward proxies, protocol bridges, tunnel helpers) pay at least one copy per read chunk plus Buffer/stream allocation pressure, even when they pass onread to tls.connect().
For sustained bidirectional traffic at path-MTU sizes (1280–1500 bytes for many tunnels):
- Extra copies and allocations raise CPU use and GC pauses on long-lived connections
- 16 KiB chunking can split L3 frames across reads, pushing framing/reassembly into userland
- 'data' event mode is even worse (multiple Buffer objects per second under load)
The stream Readable API is the right default for applications; pump workloads need a documented fast path that stays in native code until the user callback.
What is the feature you are proposing to solve the problem?
Extend TLSWrap so that when onread is configured on a TLSSocket, cleartext can be delivered directly into the caller's buffer without an intermediate EmitAlloc copy or Readable queue.
Proposed API / behavior:
const buf = Buffer.alloc(2048);
const socket = tls.connect({
host,
port,
rejectUnauthorized: false,
onread: {
buffer: buf,
callback(nread, buf) {
// nread > 0: process cleartext in-place
// nread < 0: error/EOF handling
},
},
// optional: suppress 'data' events when onread is used
emitData: false,
});
Implementation outline:
- In TLSWrap::ClearOut(), if an onread buffer is registered on the wrap, SSL_read() into that buffer (or a TLS-owned ring buffer with the same lifetime rules as TCP onread) and invoke the C++ → JS callback directly — mirror the TCP static-buffer path in stream_wrap / StreamBase.
- Skip EmitRead() / Readable enqueue when onread mode is active and emitData: false.
- Make read chunk size configurable or MTU-aware (e.g. default max(16384, suggestedSize) or tlsOptions.readSize) for tunnel-friendly framing.
- Document lifetime rules: callback must not retain references past return if buffer is reused; same contract as TCP onread.
Success criteria:
- Benchmark: fewer allocations and copies vs. current TLSSocket + 'data' or current onread + TLS for sustained receive workload.
- Existing test/parallel/test-tls-onread-static-buffer.js extended to assert TLS cleartext lands in the static buffer without extra Buffer churn.
- No regression when onread is not set (streams path unchanged).
What alternatives have you considered?
- Keep using 'data' events — Simple but allocates per chunk and runs through the full Readable state machine; unsuitable for high-QPS forwarding.
- TCP onread only (TLS disabled at Node layer) — Not viable for TLS-protected protocols (lockdown, HTTPS, CDTunnel over TLS); users must use TLSSocket.
- Native TLS bridge (tls.createBridge) — Best for kernel-adjacent forwarding where JS should never see bytes; heavier API. Zero-copy onread helps users who still orchestrate in JS but want a faster pump.
- Larger kClearOutChunkSize alone — Reduces SSL_read loop iterations but does not remove the memcpy/alloc to JS; does not fix frame splitting for MTU-sized traffic.
- WASM / addon OpenSSL — Works but duplicates session management; core should offer the fast path on existing TLSWrap.
- Ngôn ngữ chính
- JavaScript
- Star
- 122k
- Fork
- 37.4k
- Merge trung bình
- 4 ngày 3 giờ
- Pull request đã merge (30 ngày)
- 279
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của nodejs/node
-
doc
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
-
build
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100
-
feature request
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
-
Theme loads third-party resources on every page (jsdelivr web font, cdnjs Font Awesome) – GDPR Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
docToolchain/docToolchain#1705 ·
-
Timezone select lists one option per character; duplicate "Failed" reason; shared tracker popover id Đang mởbug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
nightscout/nocturne#1414 ·
-
bug v2
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
modelcontextprotocol/inspector#2458 · 1 bình luận ·
-
Mend: dependency security vulnerability status: needs triage 🕵️♀️
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
carbon-design-system/ibm-products#9907 ·