Repository navigation
Conversation
|
Review requested:
|
e478a71 to
8e53cea
Compare
| if (onClientRequestHeadersChannel.hasSubscribers) { | ||
| if (headers === undefined) | ||
| headers = { __proto__: null }; | ||
| onClientRequestHeadersChannel.publish({ headers, session: this }); |
There was a problem hiding this comment.
This exposes the caller's original headers param. If you mutate that, you'll unexpectedly mutate values in user code, not just this request. E.g. if a user builds headers then passes them into multiple requests or anywhere else, they'll be passing around the mutated values between separate requests unexpectedly. If the app is proxying and passes incoming headers to upstream requests, you've modified their incoming header data.
If you clone headers here instead you can avoid that. Would need to handle both arrays & object forms and preserve sensitiveHeaders in both cases.
There was a problem hiding this comment.
Good call. Fixed in latest rev.
|
|
||
| > Stability: 1 - Experimental | ||
|
|
||
| ##### Event: `'http2.client.request.headers'` |
There was a problem hiding this comment.
Why not http2.client.stream.headers, which would match all the other methods here? No strong feelings here myself, just a bit surprising.
There was a problem hiding this comment.
The channel fires before the stream exists and the payload has no stream, so http2.client.stream.* felt wrong for it. It also lines up with the http.client.request family on the http/1 side. Happy to rename if you'd rather.
f41b4ec to
8e53cea
Compare
The http2.client.stream.created and http2.client.stream.start channels publish the stream's sentHeaders object, but by that point the request headers have already been serialized in ClientHttp2Session#request(). Mutating the published object has no effect on the wire, so tracing instrumentation has no way to add context headers such as traceparent to outgoing HTTP/2 client requests. Add a new http2.client.request.headers channel, published when ClientHttp2Session#request() is called, before the headers are validated and serialized. Subscribers receive a shallow copy of the headers object passed by the caller and can mutate it in place to add or modify request headers. When request() is called without headers, a fresh object is published so subscribers can still inject headers. The caller's own headers object and its sensitiveHeaders list are never modified, so header objects can be safely reused or forwarded across requests. Injected headers go through the same validation as headers passed directly to request(). The existing created and start channels are unchanged. Assisted-by: pi Signed-off-by: Bryan English <bryan@bryanenglish.com>
f41b4ec to
fa8ffdb
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #67069 +/- ##
==========================================
- Coverage 92.97% 90.70% -2.27%
==========================================
Files 428 797 +369
Lines 195284 278797 +83513
Branches 30205 53619 +23414
==========================================
+ Hits 181560 252881 +71321
- Misses 13397 16284 +2887
- Partials 327 9632 +9305
🚀 New features to boost your workflow:
|
Summary
Adds an
http2.client.request.headersdiagnostics channel that lets subscribers add or modify outgoing HTTP/2 client request headers before they are serialized. This gives tracing instrumentation (OpenTelemetry and friends) a supported way to propagate context headers liketraceparentonnode:http2client requests, which today requires monkey-patchingClientHttp2Session#request().Why a new channel instead of changing the existing ones
http2.client.stream.createdandhttp2.client.stream.startcannot serve as the injection point:request()serializes the headers (prepareRequestHeadersObject/prepareRequestHeadersArraycallbuildNgHeaderString) before thecreatedpublish fires, and on an already-connected sessionrequestOnConnectsubmits the HEADERS frame before the publish too. Mutating the publishedsentHeadersobject has no effect on the wire.createdearlier with the live headers object would change what existing subscribers see. They currently get the post-defaults snapshot, including the:method,:scheme,:authority, and:pathdefaults. It would also require moving header validation after stream creation, which changes error timing. Existing subscribers that treat the payload as read-only must not observe a change, so those channels are left alone.The new channel is published at the top of
ClientHttp2Session#request(), before header validation and serialization, on both the connecting and already-connected session paths. Subscribers get{ headers, session }, whereheadersis a shallow copy of the object or array the caller passed torequest()(withsensitiveHeaderspreserved), or a newly created object when no headers argument was given. Headers must be mutated in place for the change to take effect. Injected headers go through the same validation as headers passed directly torequest(), so an injected unknown pseudo-header still throws synchronously.Behavior notes
createdandstartpayloads, timing, and contents are unchanged. Mutating their published headers still has no effect. The new channel is the intended injection point.sensitiveHeaderslist, is never modified. Header reuse across requests is unaffected.Tests
test/parallel/test-diagnostics-channel-http2-client-request-headers.jscovers:request()is called while the session is still connectingsensitiveHeaders) are not modified by the subscriber, in both object and raw array formrequest()is called with no headers argumentAlso ran the full
test-http2andtest-diagnostics-channelparallel suites locally (364 tests, all passing), plusmake lint-jsandmake lint-mdon the changed files.