GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — http: lazy create IncomingMessage.headers by ronag · Pull Request #35281 · nodejs/node

Conversation

@ronag

@ronag ronag commented Sep 21, 2020 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

When rawHeaders is enough don't create the headers object.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

bricss reacted with eyes emoji
All reactions
  • 👀 1 reaction
@ronag
ronag requested a review from mcollina September 21, 2020 10:26
@ronag
ronag requested a review from a team as a code owner September 21, 2020 10:26
@nodejs-github-bot

nodejs-github-bot commented Sep 21, 2020

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/http
  • @nodejs/net
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nodejs-github-bot nodejs-github-bot added the http Issues and PRs related to the http subsystem. label Sep 21, 2020
@ronag ronag changed the title http: lazy create OutgoingMessage.headers http: lazy create IncomingMessage.headers Sep 21, 2020
@ronag
ronag force-pushed the http-lazy-headers branch 2 times, most recently from 52e6d16 to e839ab2 Compare September 21, 2020 10:27
@ronag
http: lazy create IncomingMessage.headers
3417e0b
When rawHeaders is enough don't create the headers object.
@ronag
ronag force-pushed the http-lazy-headers branch from e839ab2 to 3417e0b Compare September 21, 2020 10:28
@ronag

This comment has been minimized.

Sign in to view
@ronag
fixup
a9cc8f8
@ronag
ronag force-pushed the http-lazy-headers branch from 9280724 to a9cc8f8 Compare September 21, 2020 12:07
@ronag
fixup
ed40951
@lpinca

lpinca commented Sep 21, 2020

Copy link
Copy Markdown
Member

What are the advantages? I think working with raw headers is not common in user land and creating IncomingMessage.headers lazily might cause a performance loss.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ronag

ronag commented Sep 21, 2020

Copy link
Copy Markdown
Member Author

I’m working a proxy library that can make use of this.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ronag

ronag commented Sep 21, 2020 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

I suppose e.g. fastify could also make use of it? @mcollina

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mcollina

mcollina commented Sep 21, 2020

Copy link
Copy Markdown
Member

Can we measure if there is a performance loss in adding this with express or fastify as they are right now? I think we might have a boost in postponing the parsing to where it's needed.

Overall I don't think this would change much our performance profile as in most cases those headers must be parsed. Proving rawHeaders enables the ecosystem to provide faster parsing options.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mscdex

mscdex commented Sep 21, 2020

Copy link
Copy Markdown
Contributor

The other thing to be aware of is that with this change, modifying .rawHeaders/.rawTrailers (before accessing .headers) can now affect the content of .headers.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ronag

ronag commented Sep 21, 2020

Copy link
Copy Markdown
Member Author

The other thing to be aware of is that with this change, modifying .rawHeaders/.rawTrailers (before accessing .headers) can now affect the content of .headers.

Good point.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ronag

ronag commented Sep 21, 2020

Copy link
Copy Markdown
Member Author

For context this is the scenario I'm trying to optimize for. Note how it only uses rawHeaders.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ronag

ronag commented Oct 9, 2020

Copy link
Copy Markdown
Member Author

@nodejs/http

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ronag

ronag commented Oct 9, 2020

Copy link
Copy Markdown
Member Author

Anyone willing to help with benchmarking this?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@jasnell

jasnell commented Oct 22, 2020

Copy link
Copy Markdown
Member

I'm not able to help with benchmarking this at the moment, but overall I'm fine with this basic idea

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

mcollina
mcollina approved these changes Oct 23, 2020
View reviewed changes

@mcollina mcollina left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

I do not see any improvement or regressions in any of the cases I tried. For the vast majority of the apps, this just shifts CPU work from one place to another.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ronag and kibertoad reacted with heart emoji
All reactions
  • ❤️ 2 reactions
Comment thread lib/_http_incoming.js

for (let i = 0; i < n; i += 2) {
this._addHeaderLine(headers[i], headers[i + 1], dest);
if (dest) {

@mcollina mcollina Oct 23, 2020

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (dest) {
if (dest !== null) {

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

janstuemmel reacted with laugh emoji
All reactions
  • 😄 1 reaction
@mcollina mcollina added the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Oct 23, 2020
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Oct 23, 2020
@nodejs-github-bot

This comment has been minimized.

Sign in to view
@ronag ronag added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Oct 23, 2020
@nodejs-github-bot

This comment has been minimized.

Sign in to view
@nodejs-github-bot

nodejs-github-bot commented Oct 25, 2020

Copy link
Copy Markdown
Collaborator

CI: https://ci.nodejs.org/job/node-test-pull-request/33850/

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ExE-Boss

ExE-Boss commented Dec 17, 2020

Copy link
Copy Markdown
Contributor

@ronag That won’t fix the issue, as Object.assign and object spread only copies own enumerable properties, whereas this made IncomingMessage.headers into an accessor property on the prototype, and thus not an own property of the instance.

ronag and bernard-leech reacted with thumbs up emoji
All reactions
  • 👍 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@BethGriggs BethGriggs added the dont-land-on-v14.x label Dec 17, 2020
@BethGriggs BethGriggs mentioned this pull request Dec 17, 2020
[v14.x] Revert "http: lazy create IncomingMessage.headers" #36553
Closed
2 tasks done
@mcollina

mcollina commented Dec 17, 2020

Copy link
Copy Markdown
Member

This is relevant #36550 (comment)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

BethGriggs added a commit that referenced this pull request Dec 17, 2020
@BethGriggs
Revert "http: lazy create IncomingMessage.headers"
4264d9a
This reverts commit b58725c.

Fixes: #36550

PR-URL: #36553
Refs: #35281
Reviewed-By: Richard Lau <[email protected]>
Reviewed-By: Myles Borins <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
@ExE-Boss ExE-Boss mentioned this pull request Dec 22, 2020
doc: document "http: lazy create IncomingMessage.headers" #36601
Merged
2 tasks done
Trott pushed a commit to ExE-Boss/node that referenced this pull request Dec 25, 2020
@ExE-Boss @Trott
doc: document "http: lazy create IncomingMessage.headers"
88fb8e4
Refs: nodejs#35281
Refs: nodejs#36550

Co-authored-by: raisinten <[email protected]>

PR-URL: nodejs#36601
Reviewed-By: Rich Trott <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
danielleadams pushed a commit that referenced this pull request Jan 12, 2021
@ExE-Boss @danielleadams
doc: document "http: lazy create IncomingMessage.headers"
5645b21
Refs: #35281
Refs: #36550

Co-authored-by: raisinten <[email protected]>

PR-URL: #36601
Reviewed-By: Rich Trott <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
@ronag

ronag commented Apr 16, 2021

Copy link
Copy Markdown
Member Author

The headers are always calculated due to

node/lib/_http_server.js

Line 910 in d3162da

if (req.headers.expect !== undefined &&
😞

mcollina reacted with confused emoji
All reactions
  • 😕 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@svkoskin svkoskin mentioned this pull request Aug 27, 2021
Credentials provider: request headers undefined in authorize callback on new Nodes nextauthjs/next-auth#2612
Closed
@targos targos removed the commit-queue-failed PRs whose Commit Queue landing failed and need manual intervention before retrying. label Sep 5, 2021
@ArnoldZokas ArnoldZokas mentioned this pull request Feb 23, 2022
doc: document change to IncomingMessage.headers enumerability #42095
Merged
ArnoldZokas added a commit to ArnoldZokas/node that referenced this pull request Feb 23, 2022
@ArnoldZokas
doc: document change to IncomingMessage.headers enumerability
2acd93b
The IncomingMessage.headers property was made non-enumerable
in PR nodejs#35281.
nodejs-github-bot pushed a commit that referenced this pull request Feb 23, 2022
@ArnoldZokas
doc: document change to IncomingMessage.headers enumerability
debd87c
The IncomingMessage.headers property was made non-enumerable
in PR #35281.

PR-URL: #42095
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Robert Nagy <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Harshitha K P <[email protected]>
@lionralfs lionralfs mentioned this pull request Feb 26, 2022
cloneIncomingMessage does not handle headers field in Node.js v16 mswjs/interceptors#212
Closed
sxa pushed a commit to sxa/node that referenced this pull request Mar 7, 2022
@ArnoldZokas @sxa
doc: document change to IncomingMessage.headers enumerability
26fe61b
The IncomingMessage.headers property was made non-enumerable
in PR nodejs#35281.

PR-URL: nodejs#42095
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Robert Nagy <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Harshitha K P <[email protected]>
danielleadams pushed a commit to danielleadams/node that referenced this pull request Apr 21, 2022
@ArnoldZokas @danielleadams
doc: document change to IncomingMessage.headers enumerability
ebd61f0
The IncomingMessage.headers property was made non-enumerable
in PR nodejs#35281.

PR-URL: nodejs#42095
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Robert Nagy <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Harshitha K P <[email protected]>
danielleadams pushed a commit that referenced this pull request Apr 24, 2022
@ArnoldZokas @danielleadams
doc: document change to IncomingMessage.headers enumerability
bc457c2
The IncomingMessage.headers property was made non-enumerable
in PR #35281.

PR-URL: #42095
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Robert Nagy <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Harshitha K P <[email protected]>
xtx1130 pushed a commit to xtx1130/node that referenced this pull request Apr 25, 2022
@ArnoldZokas @xtx1130
doc: document change to IncomingMessage.headers enumerability
424cdb9
The IncomingMessage.headers property was made non-enumerable
in PR nodejs#35281.

PR-URL: nodejs#42095
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Robert Nagy <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Harshitha K P <[email protected]>
@aortmannm aortmannm mentioned this pull request Jul 2, 2022
[Bug]: Since the update to node.js 16 playing radio stations via script does not work iobroker-community-adapters/ioBroker.chromecast#124
Closed
@efdali efdali mentioned this pull request Oct 23, 2023
fix: forward-compatibility adaptation for request headers puzzle-js/puzzle-js#407
Merged
kyptin added a commit to kyptin/paperplane that referenced this pull request Dec 12, 2023
@kyptin
Fix mount for Node.js >= 15.1.0
aaaea4b
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"
kyptin added a commit to kyptin/paperplane that referenced this pull request Dec 12, 2023
@kyptin
Fix mount for Node.js >= 15.1.0
b4f06c4
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"
@kyptin kyptin mentioned this pull request Dec 12, 2023
Fix mount for Node.js >= 15.1.0 articulate/paperplane#66
Open
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@mcollina mcollina mcollina approved these changes
@jasnell jasnell jasnell approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

author ready PRs with CI started, the required approvals, and no outstanding review comments. http Issues and PRs related to the http subsystem.

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

10 participants

@ronag @nodejs-github-bot @lpinca @mcollina @mscdex @jasnell @aduh95 @ExE-Boss @targos @BethGriggs
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.