Skip to content

fix(ext/node): improve http server parser compat - #34094

Merged
bartlomieju merged 4 commits into
denoland:mainfrom
bartlomieju:fix-node-http-empty-requests-parser-errors
May 17, 2026
Merged

bartlomieju merged 4 commits into
denoland:mainfrom
bartlomieju:fix-node-http-empty-requests-parser-errors

Conversation

@bartlomieju

Copy link
Copy Markdown
Member

Summary

  • pass raw parser input to JS for consumed-handle parse errors so HPE_ERROR can be classified as HPE_INVALID_TRANSFER_ENCODING
  • retain the last raw parser packet for EOF parser errors
  • implement http.createServer({ optimizeEmptyRequests: true }) for known-empty requests
  • enable the now-passing Node compat tests

Tests

  • ./x fmt
  • ./x build
  • ./x test-compat test-http-server-reject-chunked-with-content-length.js
  • ./x test-compat test-http-client-reject-chunked-with-content-length.js
  • ./x test-compat test-http-server-optimize-empty-requests.js
  • ./x test-compat test-http-pause-no-dump.js

@bartlomieju bartlomieju changed the title fix(node): improve http server parser compat fix(ext/node): improve http server parser compat May 15, 2026
@bartlomieju

Copy link
Copy Markdown
Member Author

@lunadogbot please review

@fibibot fibibot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Found one behavioral mismatch with Node's optimizeEmptyRequests implementation.

isRequestKnownEmpty() returns true for all HEAD requests, even when the request has Content-Length or Transfer-Encoding. With createServer({ optimizeEmptyRequests: true }), a HEAD request with body framing headers will be passed through _dumpAndCloseReadable() before user code sees it, so applications can no longer read or observe that request stream. Upstream Node gates this optimization only on the absence of body headers (!hasBodyHeaders(req.headers)), not on the method. Please drop the HEAD special case and base this solely on missing body headers.

I could not run the targeted compat tests locally because this VM has no cargo in PATH; GitHub CI is green.

@fibibot fibibot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The revised head removes the HEAD shortcut, so isRequestKnownEmpty() now matches Node's documented condition: no Content-Length and no Transfer-Encoding. The raw-packet plumbing for collapsed llhttp errors is scoped to parse errors / upgrades, and the parser cleanup clears _lastRawPacket.

CI is green.

@lunadogbot lunadogbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The current head removes the HEAD shortcut in isRequestKnownEmpty(), so optimizeEmptyRequests now follows the node_test cases: requests with either Content-Length or Transfer-Encoding stay readable, regardless of method. The raw-packet plumbing feeds collapsed llhttp errors into prepareError() on both consume and socket paths, and cleanParser() clears _lastRawPacket before parser reuse.

CI is green.

Comment thread tests/node_compat/runner/suite Resolved

@lunadogbot lunadogbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  1. if (req.method === "HEAD") return true; reintroduces the Node mismatch from the previous review. Node 25.8.1's _http_server.js optimizes only when !hasBodyHeaders(req.headers), so a HEAD request with Content-Length or Transfer-Encoding must remain readable; this shortcut dumps it before user code can observe the stream. Drop the HEAD branch or guard it with the same body-header check.

The imported node_test currently only sends HEAD without body headers, so it does not cover this regression. CI is still running on this head.

@lunadogbot lunadogbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The current head removes the HEAD branch from isRequestKnownEmpty(), so optimizeEmptyRequests now leaves HEAD requests with Content-Length or Transfer-Encoding readable while still dumping known-empty requests. That resolves the Node compat blocker from the previous review.

Holding approval until CI finishes on this SHA.

@fibibot fibibot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The current head removes the HEAD branch from isRequestKnownEmpty(), so optimizeEmptyRequests now follows Node's !hasBodyHeaders(req.headers) rule: requests with Content-Length or Transfer-Encoding stay readable. Passing the raw packet into onParserExecute() for consume-path errors also gives prepareError() enough bytes to recover HPE_INVALID_TRANSFER_ENCODING, and cleanParser() clears _lastRawPacket before parser reuse.

Holding approval until test specs (2/2) release linux-x86_64 finishes.

@bartlomieju
bartlomieju enabled auto-merge (squash) May 17, 2026 08:34

@lunadogbot lunadogbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

CI is red because test specs (2/2) release linux-x86_64 was cancelled after the other shards passed; flake-watcher will triage. Holding approval until a green run lands.

@fibibot fibibot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

CI is green now, promoting prior review to APPROVE.

@bartlomieju
bartlomieju merged commit ff55026 into denoland:main May 17, 2026
268 of 270 checks passed
littledivy pushed a commit to crowlKats/deno that referenced this pull request Jun 10, 2026
- pass raw parser input to JS for consumed-handle parse errors so
HPE_ERROR can be classified as HPE_INVALID_TRANSFER_ENCODING
- retain the last raw parser packet for EOF parser errors
- implement http.createServer({ optimizeEmptyRequests: true }) for
known-empty requests
- enable the now-passing Node compat tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants