-
Notifications
You must be signed in to change notification settings - Fork 30k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Object.assign / spread of http request object difference between v14.15.1 and v14.15.2 #36550
Comments
As @ExE-Boss indicated here (#36023 (comment)), the |
const target = {};
Object.assign(
target,
JSON.parse(`{
"__proto__": null
}`,
); results in Whereas: const target = {
...(JSON.parse(`{
"__proto__": null
}`)),
}; Results in an object with an own |
This likely related to #35281. |
This is definitely caused by #35281. |
I think a quick fix would be to do something like: const dummyReq = { ...req, get headers() { return req.headers }, get trailers() { return req.trailers; } }; |
Yep, thanks for that! I have already implemented something similar. |
I'd just revert it in v14, not v15. |
I agree with @mcollina that a revert in v14 would amke sense, and it would be good to get a release out. |
This reverts commit b58725c. Fixes: nodejs#36550
Raised a revert PR for Node.js 14 (#36553). Hope to get this into a v14.15.3 very soon so that there are no barriers to adopting the upcoming security release on January 4th. |
I've investigated this with a bit more detail, and I don't think it's a bug. I would recommend against using the spread operator on any stream. Nevertheless, this should be reverted in v14.x as it was probably a significant breaking change that slipped in a patch release. |
This reverts commit b58725c. Fixes: #36550 PR-URL: #36553 Refs: #35281 Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Myles Borins <myles.borins@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This reverts commit b58725c. Fixes: #36550 PR-URL: #36553 Refs: #35281 Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Myles Borins <myles.borins@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
v14.15.3 has been released reverting the behaviour change. I think we can close this. Thanks a lot for the report and the repro code @rubenstolk! |
How do we continue regarding this in NodeJs 15? The same behavior change is between 15.0.0 and 15.1.0. Should we document this somehow? |
Totally agree here! |
This change have been out for more than a month and I would not revert it there. I think it would create more friction to revert it there.
I would document that |
Well, it's an uneven version and still quite new. More or less any production setup runs on LTS versions only. Node 15 (and other uneven versions) are more or less not existing there. So it's likely that we will see this popping up with 16. |
What would you recommend then? |
If #35281 had been labeled Note that if it was reverted on v15, the revert would not be a breaking change, I don't think it would result in friction. I'm fine letting it slide for v15 though, the fact that we didn't get any bug report since it shipped seems to show it doesn't really matter there. |
I think marking According to #35281 (review) the change is mostly a nop from performance point of view therefore I think reverting this also on master is also an option. But if general consensus is to keep it in 15 and master as is I'm also ok. |
Version 14.15.3 'Fermium' (LTS) Notable Changes Node.js v14.15.2 included a commit that has caused reported breakages when cloning request objects. This release reverts the commit that introduced the behaviour change. See nodejs/node#36550 for more details.
https://nodejs.org/en/blog/release/v14.15.3/ Node.js v14.15.2 included a commit that has caused reported breakages when cloning request objects. This release reverts the commit that introduced the behaviour change. See nodejs/node#36550 for more details. Sponsored by: Miles AS git-svn-id: svn+ssh://svn.freebsd.org/ports/head@558895 35697150-7ecd-e111-bb59-0022644237b5
https://nodejs.org/en/blog/release/v14.15.3/ Node.js v14.15.2 included a commit that has caused reported breakages when cloning request objects. This release reverts the commit that introduced the behaviour change. See nodejs/node#36550 for more details. Sponsored by: Miles AS
https://nodejs.org/en/blog/release/v14.15.3/ Node.js v14.15.2 included a commit that has caused reported breakages when cloning request objects. This release reverts the commit that introduced the behaviour change. See nodejs/node#36550 for more details. Sponsored by: Miles AS git-svn-id: svn+ssh://svn.freebsd.org/ports/head@558895 35697150-7ecd-e111-bb59-0022644237b5
Refs: nodejs#35281 Refs: nodejs#36550 Co-authored-by: raisinten <raisinten@gmail.com> PR-URL: nodejs#36601 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Due to nodejs/node#36550 (it was reverted in Node 14 but it is back in Node 16). req.headers and other properties are not own properties of requests so they are not cloned. Node documentation (see discussion on nodejs/node#36550) advices against clonning the req (or any stream object). This PR fixes that without modifying the original request object by creating a new object which prototype is the original request object instead of trying to clone it.
Due to nodejs/node#36550 (it was reverted in Node 14 but it is back in Node 16). req.headers and other properties are not own properties of requests so they are not cloned. Node documentation (see discussion on nodejs/node#36550) advices against clonning the req (or any stream object). This PR fixes that without modifying the original request object by creating a new object which prototype is the original request object instead of trying to clone it.
Due to nodejs/node#36550 (it was reverted in Node 14 but it is back in Node 16). req.headers and other properties are not own properties of requests so they are not cloned. Node documentation (see discussion on nodejs/node#36550) advices against clonning the req (or any stream object). This PR fixes that without modifying the original request object by creating a new object which prototype is the original request object instead of trying to clone it.
Node.js v14.15.2 included a commit that has caused reported breakages when cloning request objects. This release reverts the commit that introduced the behaviour change. See nodejs/node#36550 for more details.
I tried the basic example in the README. It failed with a 500 Internal Server Error and the following JSON body: { "message": "Cannot read properties of undefined (reading 'x-forwarded-proto')", "name": "TypeError" } The output from the server process includes a stack trace. Sanitized a bit, it is: TypeError: Cannot read properties of undefined (reading 'x-forwarded-proto') at protocol (.../node_modules/paperplane/lib/parseUrl.js:18:14) at .../node_modules/ramda/src/converge.js:47:17 at _map (.../node_modules/ramda/src/internal/_map.js:6:19) at .../node_modules/ramda/src/converge.js:46:33 at f1 (.../node_modules/ramda/src/internal/_curry1.js:18:17) at .../node_modules/ramda/src/uncurryN.js:34:21 at .../node_modules/ramda/src/internal/_curryN.js:37:27 at .../node_modules/ramda/src/internal/_arity.js:10:19 at .../node_modules/ramda/src/internal/_pipe.js:3:14 at .../node_modules/ramda/src/internal/_arity.js:10:19 Yet the docker-based demo app works. That uses Node.js v12.22.12. When I tried that Node version on the basic example, it also worked. So, I used `nvm` to do a binary search on versions. I identified that Node.js v15.1.0 is the first failing version, and every version I tried that was newer (up to v21.2.0) also failed. Scouring the Node.js change log revealed that the http module of v15.1.0 started calculating req.headers lazily. There's a full discussion in nodejs/node#35281 [1]. Note the referenced issue that identifies the bug [2]. [1] nodejs/node#35281 [2] nodejs/node#36550 The workaround identified in this comment [3] is not to use the spread operator or `Object.assign` on the request object. "These objects are essentially uncloneable." [3] nodejs/node#36550 (comment) This is surprisingly tricky to do right. Various Ramda functions like `merge` (and I think `converge`) do this implicitly, as does funky's `assocWith`. So to work around this limitation, while preserving all behavior, I had to resort to (gasp) mutating procedures instead of pure functions. Not ideal, I know. I'm open to better ideas. So, maybe this isn't the best solution, but it least it gets the example working again on modern Node versions. If it never gets merged, at least it will be findable via the repo on GitHub. For reference, to test this, I used a fresh NPM project with only the paperplane dependency: mkdir paperplane cd paperplane npm init -y npm i -S paperplane In which I added an index.js file with these contents: const { compose } = require('ramda') const http = require('http') const { json, logger, methods, mount, routes } = require('paperplane') const cookies = req => ({ cookies: req.cookies || 'none?' }) const hello = req => ({ message: `Hello ${req.params.name}!` }) const app = routes({ '/' : methods({ GET: _ => ({ body: 'Hello anon.' }) }), '/cookies' : methods({ GET: compose(json, cookies) }), '/hello/:name': methods({ GET: compose(json, hello) }) }) http.createServer(mount({ app })).listen(3000, logger) And finally (with `fd` and `entr` and `httpie` installed), I started a file-watching auto-test process: fd -e js | entr -rcc bash -c "node index.js & http -v get http://localhost:3000/cookies Cookie:foo=bar"
I tried the basic example in the README. It failed with a 500 Internal Server Error and the following JSON body: { "message": "Cannot read properties of undefined (reading 'x-forwarded-proto')", "name": "TypeError" } The output from the server process includes a stack trace. Sanitized a bit, it is: TypeError: Cannot read properties of undefined (reading 'x-forwarded-proto') at protocol (.../node_modules/paperplane/lib/parseUrl.js:18:14) at .../node_modules/ramda/src/converge.js:47:17 at _map (.../node_modules/ramda/src/internal/_map.js:6:19) at .../node_modules/ramda/src/converge.js:46:33 at f1 (.../node_modules/ramda/src/internal/_curry1.js:18:17) at .../node_modules/ramda/src/uncurryN.js:34:21 at .../node_modules/ramda/src/internal/_curryN.js:37:27 at .../node_modules/ramda/src/internal/_arity.js:10:19 at .../node_modules/ramda/src/internal/_pipe.js:3:14 at .../node_modules/ramda/src/internal/_arity.js:10:19 Yet the docker-based demo app works. That uses Node.js v12.22.12. When I tried that Node version on the basic example, it also worked. So, I used `nvm` to do a binary search on versions. I identified that Node.js v15.1.0 is the first failing version, and every version I tried that was newer (up to v21.2.0) also failed. Scouring the Node.js change log revealed that the http module of v15.1.0 started calculating req.headers lazily. There's a full discussion in nodejs/node#35281 [1]. Note the referenced issue that identifies the bug [2]. [1] nodejs/node#35281 [2] nodejs/node#36550 The workaround identified in this comment [3] is not to use the spread operator or `Object.assign` on the request object. "These objects are essentially uncloneable." [3] nodejs/node#36550 (comment) This is surprisingly tricky to do right. Various Ramda functions like `merge` (and I think `converge`) do this implicitly, as does funky's `assocWith`. So to work around this limitation, while preserving all behavior, I had to resort to (gasp) mutating procedures instead of pure functions. Not ideal, I know. I'm open to better ideas. So, maybe this isn't the best solution, but it least it gets the example working again on modern Node versions. If it never gets merged, at least it will be findable via the repo on GitHub. For reference, to test this, I used a fresh NPM project with only the paperplane dependency: mkdir paperplane cd paperplane npm init -y npm i -S paperplane In which I added an index.js file with these contents: const { compose } = require('ramda') const http = require('http') const { json, logger, methods, mount, routes } = require('paperplane') const cookies = req => ({ cookies: req.cookies || 'none?' }) const hello = req => ({ message: `Hello ${req.params.name}!` }) const app = routes({ '/' : methods({ GET: _ => ({ body: 'Hello anon.' }) }), '/cookies' : methods({ GET: compose(json, cookies) }), '/hello/:name': methods({ GET: compose(json, hello) }) }) http.createServer(mount({ app })).listen(3000, logger) And finally (with `fd` and `entr` and `httpie` installed), I started a file-watching auto-test process: fd -e js | entr -rcc bash -c "node index.js & http -v get http://localhost:3000/cookies Cookie:foo=bar"
What steps will reproduce the bug?
While trying to create a cloned version of an http request object, prototype properties/methods such as
headers
andget
get lost.What is the expected behavior?
I honestly don't know if the behavior from 14.15.1 or from 14.15.2 is expected.
Behavior until 14.15.1:
dummyReq.headers
is notundefined
.What do you see instead?
Output in 14.15.2:
dummyReq.headers
isundefined
.Additional info
The same happens while using
Object.assign
The text was updated successfully, but these errors were encountered: