Repository navigation
Conversation
|
e2e same like: #62915 (comment) |
|
Review requested:
|
7865a8d to
4bd2033
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #64369 +/- ##
==========================================
- Coverage 90.71% 90.70% -0.01%
==========================================
Files 801 803 +2
Lines 281889 282296 +407
Branches 54315 54391 +76
==========================================
+ Hits 255709 256066 +357
- Misses 16307 16375 +68
+ Partials 9873 9855 -18
🚀 New features to boost your workflow:
|
929ddfe to
eaa29a9
Compare
d499741 to
8fa14bc
Compare
|
I think it's time to conduct a re-review. Thank you. @metcoder95 @ShogunPanda @mcollina |
|
push |
|
push |
|
Friendly ping on the next step. The existing review feedback has been addressed, @metcoder95 has approved, and the PR is currently mergeable. It is still labeled "needs-ci" — could a collaborator please trigger the full CI? If CI is green, could @nodejs/http and @nodejs/inspector give it a final look or let me know if anything else is needed before landing? Happy to address any follow-up feedback promptly. Thanks! |
|
push |
|
push... @jasnell Can you help me add the label to run the CI |
0cb2dec to
71b18a4
Compare
|
Hi @mcollina , I’ve rebased #64369 onto the latest main and resolved the HTTP outgoing conflict. The PR is mergeable again, and CI is running. The targeted HTTP/Inspector tests pass locally. When you have a chance, could you review the HTTP integration, particularly the request-body tracking and raw response-body hook? I’d appreciate your feedback and will address any comments promptly. Thanks! |
|
Can you do a run of the benchmarks? |
I tried comparing the main branch and found that the newly added features resulted in a performance drop of around 18%. Due to the collection of body data, this decrease is actually within expectations. But did CI just fail? I think while investigating, I can also see if there are any ways to optimize.
I can’t access the Jenkins results due to the CI read permissions. Both node-test-commit/92841 and node-test-pull-request/78032 failed, although the reported platform checks appear green. Could someone with CI access share the failing job/platform, test name, and relevant console output? If the failure is unrelated to this change, could you please rerun the affected job? Thanks! @mcollina |
|
I reduced the inspection overhead by encoding initiator stacks directly into CBOR, removing a redundant request-body copy, and simplifying the body-completion notification. Async caller frames and validation checks are preserved. With Network inspection enabled on both sides, local benchmarks showed an 8.1%–9.7% throughput improvement over the previous implementation; throughput remains approximately 10%–11% below the main baseline. Regression tests and the remote Inspector E2E passed locally. |
|
@mcollina plz cc again. |
Thank you, sir. I have been troubled by this for a long time, and I never expected to make such rapid progress after meeting you. Thank you |
|
Correction: The linting error was due to a bug in #66837 which has been corrected. Jenkins CI rebases on |
That sounds like good news - it means the issue isn't due to this PR, so we can move forward more smoothly |
|
The gating factor is the availability of a collaborator to review, approve and land your PR. |
|
@mcollina @legendecas @jasnell The CI (including Jenkins) for this PR has been completed. Please help review the code. Thank you |
This seems non-trivial. Which benchmark did you run? |
Hello~ Thank you for taking the time to review this PR! Let me explain why I consider some additional overhead expected, and how I ran the benchmarks. First, the approximately 18% figure in my earlier comment predates the optimizations. The latest recorded local benchmark results show a 10.0%–11.0% throughput reduction compared with the main baseline, with Network inspection enabled on both sides. The additional overhead is expected because the PR extends the work performed during inspection: it retains request-body data for I used Node’s existing
The results after optimization were:
The optimizations improved throughput by 8.1%–9.7% over the previous implementation, through direct CBOR encoding of initiator stacks, removal of a redundant request-body copy, and simplification of body-completion notification. Separately, the inspection-disabled HTTP benchmarks in the original run showed no statistically significant regression. These measurements were taken on September 30, before the later rebases. I have retained the benchmark commands, preload script, and raw CSV results for reproducibility. |
|
@marcastben-hash Thanks for reviewing the HTTP integration. These are useful points. My preference would be to address them in focused follow-up PRs rather than make all four prerequisites for landing, if the HTTP and Inspector maintainers are comfortable with that.
My expectation is that these additions are unlikely to cause a substantial regression with past test. |
|
Sorry if I was not clear. I was curious about the performance cost on regular mode, that is when inspector not enabled, and how much cost do these new changes incur? |
Please don't worry! With inspection disabled, we do not expect a measurable performance regression. Across 19 benchmark configurations, mean throughput changes ranged from −1.62% to +1.83%, with none statistically significant—consistent with normal run-to-run variation. Body buffering and initiator-stack capture remain inactive in this case This is my test conclusion on 9.29 with |
Expose diagnostics channels for HTTP request body chunks and completion, and use them to cache request data for Network.getRequestPostData. Capture response bytes before stream decoding so inspection works with response.setEncoding(). Capture initiator stacks through V8 when requests are created. Preserve the native snapshots in GC-managed handles until deferred events are emitted, retaining script IDs and creation sites for every session. Cover request and response bodies, empty chunks, split UTF-8 sequences, reused buffers, delayed callers, multiple sessions, and snapshot GC. Assisted-by: Codex Signed-off-by: GrinZero <774933704@qq.com>
c140813 to
68d8126
Compare
|
@legendecas Summary of Changes
BenchmarksBenchmarked the full PR against
(Note: The PR emits two fewer stack frames per event, so these numbers reflect end-to-end throughput rather than isolated stack-capture cost.) Mean window-end RSS increased in a few scenarios (e.g., 313 → 419 MiB for 64 KiB POST and 207 → 248 MiB for 16 KiB GET) due to retaining the GC-managed stack handles and buffered response bytes until event emission. Raw results, CIs, CPU/RSS data, and reproduction scripts are available in the benchmark archives. Local tests and lint pass (including coverage for script-source lookup, delayed callers, multi-session handling, body edge cases, and snapshot GC). Waiting on cross-platform CI. |
Use the cached request URL to track whether requestWillBeSent has already been emitted, instead of storing a separate boolean flag. Assisted-by: Codex Signed-off-by: GrinZero <774933704@qq.com>
Cache and reuse the NetworkInitiator constructor template for instance checks instead of maintaining a private brand on each object. Omit memory accounting for opaque V8StackTrace allocations until their actual size can be tracked reliably. Keep the wrapper self size. Assisted-by: Codex Signed-off-by: GrinZero <774933704@qq.com>
NetworkInitiator already sets its class name when creating its cached constructor template. Register the constructor without setting the name again, since V8 rejects template mutation after instantiation. This allows the inspector binding to initialize in multiple environments sharing the same isolate. Assisted-by: Codex Signed-off-by: GrinZero <774933704@qq.com>
| * `request` {http.ClientRequest} | ||
| * `chunk` {Buffer|string|Uint8Array} | ||
| * `encoding` {string|null|undefined} |
There was a problem hiding this comment.
This should describe what each of these are. It's not clear what chunk actually is.
I assume it's the actual chunk of data... and I'd have to question if this is actually wise. It means this is either a copy of the chunk, in which case we're copying every chunk of data in memory (doubling the memory usage) or we're exposing a mutable reference to the chunk.
There's also likely a range of security/privacy concerns with this.
There was a problem hiding this comment.
Thanks for raising this—the concern around mutable body data makes total sense. To help align on the right design here, could you clarify what makes this HTTP/1 integration different from the existing pattern?
For context, Undici #4289 (motivated by #4166 specifically for Node’s Network Inspector) already publishes raw request and response chunks via Diagnostics Channel, and Node consumes them directly. Core HTTP/2 took the same approach after review feedback explicitly recommended preserving the original chunk type and leaving copying/conversion to subscribers to avoid extra allocation overhead.
This PR follows that exact producer/consumer split: the publish path stays zero-copy (and is completely skipped when there are no subscribers), while the Inspector handles copying on its own when body retention is active.
Is there something specific to HTTP/1's lifecycle or buffer handling that makes this risky here, or are we reconsidering this zero-copy DC pattern more broadly?
There was a problem hiding this comment.
To verify the current baseline behavior, I ran a control test on main (without this PR and with the Inspector disabled) across the existing body DC paths:
| Existing Path | Observed Behavior |
|---|---|
| HTTP/2 body DC | Mutating the published chunk buffer alters the actual request payload received by the server. |
| Undici response DC | Mutating the published chunk buffer alters the response body read by the application. |
| Undici streaming request (5 MiB) | Even when published after socket.write(), ~4.69 MiB of the payload was still mutated by the subscriber before being flushed over the socket. |
This confirms that publishing raw, mutable chunks over DC—and trusting subscribers not to mutate them—is already the established runtime contract across both core HTTP/2 and Undici, rather than a new invariant introduced by HTTP/1.
There was a problem hiding this comment.
I have done some research, and indeed, the early documentation for the diagnostic channels of http and http2 only listed field names and types. However, the newly incorporated core channels (such as crypto.fips.indicator, sqlite.db.query) all require the semantics of each field to be specified. So, is the focus of this review on whether I need to complete this section?
jasnell
left a comment
There was a problem hiding this comment.
Not at all convinced the diagnostic channel integration here is a good idea.

Summary
This PR supersedes #62915 with the same feature work rebased onto current
main.It adds builtin
http/httpsrequest-body support to network inspection, soNetwork.getRequestPostDatacan return text request bodies while preserving the existing rejection behavior for binary request bodies.It also moves builtin
httpresponse-body tracking to a raw-byte hook beforeIncomingMessagedecoding, so response inspection remains correct even when user code callsresponse.setEncoding(...).In addition, this PR lets
Network.requestWillBeSentcarry JS-provided initiator data captured at request construction time. The C++ inspector side validates structuredinitiator.stackobjects against the inspector protocol schema before forwarding them to DevTools.Problem
Builtin
http/httpsnetwork inspection currently emits request and response lifecycle events, but the builtin client path does not expose request-body bytes to the inspector. As a result,Network.getRequestPostDatacannot return POST data for builtinhttp/httpsrequests.There are two related gaps:
IncomingMessage'data'events are not a stable raw-byte source. If user code callsresponse.setEncoding('utf8'), chunks observed through userland become strings, while the inspector protocol expects byte-oriented payloads.requestWillBeSentuntil request-body state is known means a C++ stack captured at emission time would point into Node.js internals rather than the user call site. The initiator needs to be captured when the request is created and validated before being emitted.Approach
Network.dataSent(...) -> NetworkAgent::getRequestPostData(...)buffering pipeline instead of changing the CDP schema.http.client.request.bodyChunkSenthttp.client.request.bodySenthttp.client.response.bodyChunkReceivedrequestWillBeSentuntil body state is known, sohasPostDatais accurate for both body and body-less requests.initiator.stackwith generated inspector protocol schema support in C++.Behavior
After this change:
httpandhttpsPOST requests with UTF-8 text bodies are available throughNetwork.getRequestPostDatahttpresponse inspection continues to work even if user code callsresponse.setEncoding('utf8')Network.requestWillBeSentreports accuratehasPostDataNetwork.requestWillBeSentcan carry JS-captured initiator stack data from the request construction siteTests
This PR adds and extends coverage in:
test/parallel/test-diagnostics-channel-http.jstest/parallel/test-inspector-network-http.jstest/parallel/test-inspector-emit-protocol-event.jstest/parallel/test-inspector-emit-protocol-event-errors.jsThe updated tests cover:
write()andend()httpandhttpsNetwork.getRequestPostDataresponse.setEncoding('utf8')hasPostDatafor body-less requestsVerification
Validated with the targeted test suite:
Also validated manually with an end-to-end inspector run:
Refs