fix: handle Node 10 style http requests#1233
Conversation
|
@kjin I believe this will unblock the got 10.x upgrade. |
|
CC @bcoe, if this could be reviewed and released it'd be great not to rely on a fork! |
|
@AaronFriel thanks for the patch 🙏 I'm not the most well versed in this library, and it would be helpful if we added a few tests. As it stands today, you can't use the currently published version of |
In Node 10 and above and libraries which use the `request(url, options, cb?)` overload of the request function such as `got` 10.x, request tracing fails. Environment details ------------------- - OS: any - Node.js version: 10.x - npm version: 6.x - `@google-cloud/trace-agent` version: 4.5.2 Steps to reproduce ------------------ 1. Use `got` version `10.x` or manually invoke a request with a separate URL and options argument 2. In a root span, try to do a `got.post` or any other request as above and log the `x-cloud-trace-header` in the server receiving the request 3. Observe that the request isn't traced and the trace header is absent Bug --- The bug is in three parts: 1. If condition filters out valid requests: https://github.com/googleapis/cloud-trace-nodejs/blob/ac7e886c178ca9c34502e9baa9eb190d23104347/src/plugins/plugin-http.ts#L95-L113 When got 10 makes a request, it sends three arguments: url (as an object), options (as an object), and callback (as a function). This if condition blocks tracing all requests from got 10. 2. `Object.assign` does not work for URL properties because URL properties are not enumerable. https://github.com/googleapis/cloud-trace-nodejs/blob/ac7e886c178ca9c34502e9baa9eb190d23104347/src/plugins/plugin-http.ts#L114-L126 If the if condition is changed above so that we can fall through to line 125, we hit a second bug. This last `options = Object.assign(...)` does not work because URL properties are not enumerable. The result is that properties such as "hostname", "host", etc. are not copied into the options. 3. The wrapped request is called with the deficient object https://github.com/googleapis/cloud-trace-nodejs/blob/ac7e886c178ca9c34502e9baa9eb190d23104347/src/plugins/plugin-http.ts#L171 Given the incorrect Object.assign, even if we repair that defect, we end up sending this rewritten object to the destination and we destroy any non-enumerable symbols or other properties assigned to the url or options object we were passed. Fix --- Looking at the source for CreateRequest, the function called by `http.request`, we see that the "input" argument is always replaced by an object, as opposed to a URL instance, before being `ObjectAssigned` https://github.com/nodejs/node/blob/dccdc51788bd5337f9fd80441ef52932383a2441/lib/_http_client.js#L88-L123 We can do the same. Our needs are a bit different, but we can ensure that before the object assign the url value is always converted to a `RequestOptions` object.
Co-Authored-By: Kelvin Jin <[email protected]>
|
Rebased and resolved conflicts. |
|
@kjin it sounds like Kelvin has good technical feedback 😄 Might I still press you for a test, otherwise it looks like this is coming along great. |
|
@bcoe that is correct, I wrote three tests to be very explicit about how the request function is called, two of which failed (string and URL class instance) as expected, rebasing my branch on that failing test commit resulted in the tests passing. The "parsed URL" test never failed because |
kjin
left a comment
There was a problem hiding this comment.
Thanks! (I'm Kelvin, by the way)
|
Your PR has attempted to merge for 3 hours. Please check that all required checks have passed, you have an automerge label, and that all your reviewers have approved the PR |
1 similar comment
|
Your PR has attempted to merge for 3 hours. Please check that all required checks have passed, you have an automerge label, and that all your reviewers have approved the PR |
|
Merge-on-green attempted to merge your PR for 6 hours, but it was not mergeable because either one of your required status checks failed, or one of your required reviews was not approved. Learn more about your required status checks here: https://help.github.com/en/github/administering-a-repository/enabling-required-status-checks. You can remove and reapply the label to re-run the bot. |
|
@AaronFriel awesome, thank you for adding the regression tests, and for the contribution. |
In Node 10 and above and libraries which use the
request(url, options, cb?)overload of the request function such asgot10.x, request tracing fails.Environment details
@google-cloud/trace-agentversion: 4.5.2Steps to reproduce
gotversion10.xor manually invoke a request with a separate URL and options argumentgot.postor any other request as above and log thex-cloud-trace-headerin the server receiving the requestBug
The bug is in three parts:
cloud-trace-nodejs/src/plugins/plugin-http.ts
Lines 95 to 113 in ac7e886
When got 10 makes a request, it sends three arguments: url (as an object), options (as an object), and callback (as a function). This if condition blocks tracing all requests from got 10.
Object.assigndoes not work for URL properties because URL properties are not enumerable.cloud-trace-nodejs/src/plugins/plugin-http.ts
Lines 114 to 126 in ac7e886
If the if condition is changed above so that we can fall through to line 125, we hit a second bug. This last
options = Object.assign(...)does not work because URL properties are not enumerable. The result is that properties such as "hostname", "host", etc. are not copied into the options.cloud-trace-nodejs/src/plugins/plugin-http.ts
Line 171 in ac7e886
Given the incorrect Object.assign, even if we repair that defect, we end up sending this rewritten object to the destination and we destroy any non-enumerable symbols or other properties assigned to the url or options object we were passed.
Fix
Looking at the source for CreateRequest, the function called by
http.request, we see that the "input" argument is always replaced by an object, as opposed to a URL instance, before beingObjectAssignedhttps://github.com/nodejs/node/blob/dccdc51788bd5337f9fd80441ef52932383a2441/lib/_http_client.js#L88-L123
We can do the same. Our needs are a bit different, but we can ensure that before the object assign the url value is always converted to a
RequestOptionsobject.