Skip to content
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

fix(node-fetch/http2): handle Http2 streams correctly #2093

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

ardatan
Copy link
Owner

@ardatan ardatan commented Feb 22, 2025

Fixes dotansimha/graphql-yoga#3803

Fixes the TypeError: bodyInit.stream is not a function error thrown when @whatwg-node/server is used with node:http2 and attempts the incoming HTTP/2 request to parse with Request.json, Request.text, Request.formData, or Request.blob methods.

Summary by CodeRabbit

  • Bug Fixes

    • Resolved an issue causing errors during HTTP/2 request processing, ensuring reliable operation for various request methods.
    • Improved Blob validation to correctly identify and handle stream functionality.
  • Tests

    • Updated HTTP/2 test cases to confirm that responses now include complete, structured details such as body, headers, method, and URL.

Copy link
Contributor

github-actions bot commented Feb 22, 2025

🚀 Snapshot Release (alpha)

The latest changes of this PR are available as alpha on npm (based on the declared changesets):

Package Version Info
@whatwg-node/fetch 0.10.5-alpha-20250222121046-869be2474b8eedcb461dd935cb99971cc0bdab21 npm ↗︎ unpkg ↗︎
@whatwg-node/node-fetch 0.7.11-alpha-20250222121046-869be2474b8eedcb461dd935cb99971cc0bdab21 npm ↗︎ unpkg ↗︎
@whatwg-node/server 0.9.69-alpha-20250222121046-869be2474b8eedcb461dd935cb99971cc0bdab21 npm ↗︎ unpkg ↗︎

Copy link
Contributor

github-actions bot commented Feb 22, 2025

@benchmarks/node-fetch results (consumeBody)

   ✓ active_handles.................: avg=139.937997 min=22      med=141     max=189     p(90)=160     p(95)=165    
     data_received..................: 19 MB  631 kB/s
     data_sent......................: 12 MB  405 kB/s
     http_req_blocked...............: avg=3.9µs      min=652ns   med=1.34µs  max=6.07ms  p(90)=2.14µs  p(95)=2.53µs 
     http_req_connecting............: avg=2.08µs     min=0s      med=0s      max=5.69ms  p(90)=0s      p(95)=0s     
     http_req_duration..............: avg=24.15ms    min=4.25ms  med=23.56ms max=1.23s   p(90)=30.12ms p(95)=31.75ms
       { expected_response:true }...: avg=24.15ms    min=4.25ms  med=23.56ms max=1.23s   p(90)=30.12ms p(95)=31.75ms
     http_req_failed................: 0.00%  ✓ 0           ✗ 123823
     http_req_receiving.............: avg=36.76µs    min=11.66µs med=26.64µs max=19.33ms p(90)=41.46µs p(95)=48.48µs
     http_req_sending...............: avg=11.84µs    min=3.84µs  med=6.88µs  max=12.45ms p(90)=10.76µs p(95)=14.51µs
     http_req_tls_handshaking.......: avg=0s         min=0s      med=0s      max=0s      p(90)=0s      p(95)=0s     
     http_req_waiting...............: avg=24.1ms     min=4.22ms  med=23.51ms max=1.23s   p(90)=30.07ms p(95)=31.69ms
     http_reqs......................: 123823 4127.037753/s
     iteration_duration.............: avg=48.42ms    min=17.89ms med=46.73ms max=1.26s   p(90)=52.08ms p(95)=57.93ms
     iterations.....................: 61884  2062.602298/s
     vus............................: 100    min=100       max=100 
     vus_max........................: 100    min=100       max=100 

Copy link
Contributor

github-actions bot commented Feb 22, 2025

@benchmarks/server results (undici)

     ✓ no-errors
     ✓ expected-result

   ✓ checks.........................: 100.00% ✓ 218092      ✗ 0     
     data_received..................: 22 MB   731 kB/s
     data_sent......................: 8.7 MB  291 kB/s
     http_req_blocked...............: avg=1.43µs   min=892ns    med=1.21µs   max=170.71µs p(90)=1.92µs   p(95)=2.09µs  
     http_req_connecting............: avg=1ns      min=0s       med=0s       max=123.15µs p(90)=0s       p(95)=0s      
     http_req_duration..............: avg=212.14µs min=155.19µs med=200.42µs max=55.01ms  p(90)=226.57µs p(95)=235.53µs
       { expected_response:true }...: avg=212.14µs min=155.19µs med=200.42µs max=55.01ms  p(90)=226.57µs p(95)=235.53µs
     http_req_failed................: 0.00%   ✓ 0           ✗ 109046
     http_req_receiving.............: avg=25.67µs  min=13.74µs  med=24.01µs  max=2.76ms   p(90)=31.14µs  p(95)=33.7µs  
     http_req_sending...............: avg=6.44µs   min=3.97µs   med=5.61µs   max=330.07µs p(90)=8.28µs   p(95)=9.07µs  
     http_req_tls_handshaking.......: avg=0s       min=0s       med=0s       max=0s       p(90)=0s       p(95)=0s      
     http_req_waiting...............: avg=180.02µs min=133.2µs  med=168.61µs max=54.94ms  p(90)=191.45µs p(95)=199.94µs
     http_reqs......................: 109046  3634.699917/s
     iteration_duration.............: avg=270.61µs min=197.34µs med=257.77µs max=55.14ms  p(90)=287.49µs p(95)=299.12µs
     iterations.....................: 109046  3634.699917/s
     vus............................: 1       min=1         max=1   
     vus_max........................: 1       min=1         max=1   

Copy link
Contributor

github-actions bot commented Feb 22, 2025

@benchmarks/node-fetch results (noConsumeBody)

   ✓ active_handles.................: avg=139.463685 min=11     med=138     max=191     p(90)=160     p(95)=165    
     data_received..................: 20 MB  673 kB/s
     data_sent......................: 13 MB  436 kB/s
     http_req_blocked...............: avg=2.11µs     min=551ns  med=1.37µs  max=3.6ms   p(90)=2.09µs  p(95)=2.52µs 
     http_req_connecting............: avg=290ns      min=0s     med=0s      max=2.07ms  p(90)=0s      p(95)=0s     
     http_req_duration..............: avg=22.65ms    min=2.61ms med=21.88ms max=1.27s   p(90)=28.89ms p(95)=31.05ms
       { expected_response:true }...: avg=22.65ms    min=2.61ms med=21.88ms max=1.27s   p(90)=28.89ms p(95)=31.05ms
     http_req_failed................: 0.00%  ✓ 0           ✗ 132009
     http_req_receiving.............: avg=34.16µs    min=8.97µs med=24.87µs max=12.88ms p(90)=40.25µs p(95)=47.9µs 
     http_req_sending...............: avg=11.57µs    min=3.37µs med=6.9µs   max=12.99ms p(90)=10.67µs p(95)=15.05µs
     http_req_tls_handshaking.......: avg=0s         min=0s     med=0s      max=0s      p(90)=0s      p(95)=0s     
     http_req_waiting...............: avg=22.6ms     min=2.55ms med=21.84ms max=1.27s   p(90)=28.84ms p(95)=30.99ms
     http_reqs......................: 132009 4399.797175/s
     iteration_duration.............: avg=45.41ms    min=9.96ms med=43.68ms max=1.29s   p(90)=50.21ms p(95)=56.28ms
     iterations.....................: 65965  2198.582072/s
     vus............................: 71     min=71        max=100 
     vus_max........................: 100    min=100       max=100 

Copy link
Contributor

github-actions bot commented Feb 22, 2025

@benchmarks/server results (native)

     ✓ no-errors
     ✓ expected-result

   ✓ checks.........................: 100.00% ✓ 231854      ✗ 0     
     data_received..................: 23 MB   777 kB/s
     data_sent......................: 9.3 MB  309 kB/s
     http_req_blocked...............: avg=1.41µs   min=892ns    med=1.19µs   max=271.3µs  p(90)=1.89µs   p(95)=2.05µs  
     http_req_connecting............: avg=1ns      min=0s       med=0s       max=116.32µs p(90)=0s       p(95)=0s      
     http_req_duration..............: avg=195.74µs min=142.01µs med=185.57µs max=7.81ms   p(90)=211.54µs p(95)=221.29µs
       { expected_response:true }...: avg=195.74µs min=142.01µs med=185.57µs max=7.81ms   p(90)=211.54µs p(95)=221.29µs
     http_req_failed................: 0.00%   ✓ 0           ✗ 115927
     http_req_receiving.............: avg=25.19µs  min=13.61µs  med=23.4µs   max=2.62ms   p(90)=30.67µs  p(95)=33.52µs 
     http_req_sending...............: avg=6.36µs   min=4.1µs    med=5.5µs    max=216.66µs p(90)=8.16µs   p(95)=8.83µs  
     http_req_tls_handshaking.......: avg=0s       min=0s       med=0s       max=0s       p(90)=0s       p(95)=0s      
     http_req_waiting...............: avg=164.18µs min=117.1µs  med=153.97µs max=7.76ms   p(90)=176.87µs p(95)=185.66µs
     http_reqs......................: 115927  3864.096456/s
     iteration_duration.............: avg=254.28µs min=189.09µs med=243.28µs max=7.9ms    p(90)=272.18µs p(95)=284.52µs
     iterations.....................: 115927  3864.096456/s
     vus............................: 1       min=1         max=1   
     vus_max........................: 1       min=1         max=1   

Copy link
Contributor

@benchmarks/server results (ponyfill)

     ✓ no-errors
     ✓ expected-result

   ✓ checks.........................: 100.00% ✓ 298192      ✗ 0     
     data_received..................: 29 MB   979 kB/s
     data_sent......................: 12 MB   398 kB/s
     http_req_blocked...............: avg=1.39µs   min=912ns    med=1.19µs   max=190.22µs p(90)=1.85µs   p(95)=2.01µs  
     http_req_connecting............: avg=0ns      min=0s       med=0s       max=124.69µs p(90)=0s       p(95)=0s      
     http_req_duration..............: avg=137.97µs min=93.54µs  med=133.17µs max=5.9ms    p(90)=155.21µs p(95)=162.02µs
       { expected_response:true }...: avg=137.97µs min=93.54µs  med=133.17µs max=5.9ms    p(90)=155.21µs p(95)=162.02µs
     http_req_failed................: 0.00%   ✓ 0           ✗ 149096
     http_req_receiving.............: avg=24.24µs  min=11.99µs  med=22.82µs  max=2.77ms   p(90)=30.04µs  p(95)=32.73µs 
     http_req_sending...............: avg=6.33µs   min=4.02µs   med=5.49µs   max=354.51µs p(90)=8.07µs   p(95)=8.76µs  
     http_req_tls_handshaking.......: avg=0s       min=0s       med=0s       max=0s       p(90)=0s       p(95)=0s      
     http_req_waiting...............: avg=107.39µs min=66.08µs  med=102.23µs max=5.69ms   p(90)=121.18µs p(95)=126.81µs
     http_reqs......................: 149096  4969.697082/s
     iteration_duration.............: avg=196.61µs min=139.49µs med=191.36µs max=6.19ms   p(90)=216.36µs p(95)=225.2µs 
     iterations.....................: 149096  4969.697082/s
     vus............................: 1       min=1         max=1   
     vus_max........................: 1       min=1         max=1   

Copy link

coderabbitai bot commented Feb 22, 2025

Walkthrough

This pull request applies patches to address a specific HTTP/2 error, "TypeError: bodyInit.stream is not a function," occurring during the parsing of incoming HTTP/2 requests. It refines the verification logic for Blob objects in the node-fetch package and updates the HTTP/2 test suite to use asynchronous request handling with structured JSON responses. No public interface changes were made.

Changes

Files Change Summary
.changeset/mighty-worlds-smoke.md Introduces patches for @whatwg-node/node-fetch, @whatwg-node/server, and @whatwg-node/fetch to address the HTTP/2 error in request parsing.
packages/.../Body.ts Updates the isBlob function to include a type check ensuring value.stream is a function, refining the Blob type verification logic.
packages/.../test/http2.spec.ts Transforms the HTTP/2 test suite by converting synchronous request handling to an asynchronous process, generating a detailed JSON response.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Server
    participant Handler
    Client->>Server: Send HTTP/2 request (includes JSON payload)
    Server->>Handler: Forward request asynchronously
    Handler->>Handler: Parse request body as JSON
    alt Successful parse
        Handler->>Server: Return structured JSON response (body, headers, method, URL)
    else Parsing error
        Handler->>Server: Return error response
    end
    Server->>Client: Deliver JSON response
Loading

Possibly related PRs

Poem

I’m a rabbit hopping through new code,
Where streams now check they’re functions bold.
HTTP/2 errors lost in the binary night,
Async magic brings clarity to light.
With every patch, our code doth sing—
Carrots and bytes, a joyous spring!
🐰✨

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/server/test/http2.spec.ts (1)

94-111: Consider adding error case tests.

While the happy path is well tested, consider adding test cases for error scenarios such as invalid JSON or stream errors to ensure robust error handling.

 expect(receivedNodeRequest).toMatchObject({
   data: {
     body: {
       hello: 'world',
     },
     method: 'POST',
     url: expect.stringMatching(/^http:\/\/localhost:\d+\/hi$/),
     headers: {
       'content-type': 'application/json',
     },
   },
   headers: {
     ':status': 418,
     'content-type': 'text/plain;charset=UTF-8',
     'x-is-this-http2': 'yes',
   },
   status: 418,
 });
+
+it('should handle invalid JSON gracefully', async () => {
+  const handleRequest = jest.fn(async (request: Request) => {
+    try {
+      await request.json();
+    } catch (error) {
+      return new Response('Invalid JSON', { status: 400 });
+    }
+    return new Response('OK');
+  });
+  // ... rest of the test setup
+});
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 352fa71 and 869be24.

📒 Files selected for processing (3)
  • .changeset/mighty-worlds-smoke.md (1 hunks)
  • packages/node-fetch/src/Body.ts (1 hunks)
  • packages/server/test/http2.spec.ts (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: unit / node 18
🔇 Additional comments (4)
packages/server/test/http2.spec.ts (2)

30-46: LGTM! Enhanced test coverage for HTTP/2 request handling.

The async request handler now properly tests JSON parsing and response generation, which helps validate the fix for the bodyInit.stream TypeError.


61-65: LGTM! Proper content-type header setup.

Setting the content-type to 'application/json' and writing JSON data ensures the request is properly formatted for testing the stream handling.

packages/node-fetch/src/Body.ts (1)

511-511:

✅ Verification successful

LGTM! Robust type checking for Blob objects.

The enhanced type check ensures that stream is actually a function before treating the object as a Blob, effectively preventing the TypeError: bodyInit.stream is not a function error.

Let's verify this fixes all potential Blob-related issues:


🏁 Script executed:

#!/bin/bash
# Search for any other Blob-related type checks or stream usage
rg -l "stream.*function|instanceof.*Blob|isBlob"

Length of output: 189


Blob type checking update verified and approved.

The change in packages/node-fetch/src/Body.ts now properly ensures that the stream property is not only present but also a function, which effectively prevents the TypeError: bodyInit.stream is not a function error. A repository-wide search shows that similar Blob-related type checks and stream usages are present in:

  • packages/node-fetch/src/FormData.ts
  • packages/node-fetch/src/Blob.ts
  • packages/node-fetch/tests/Blob.spec.ts

This consistency across the codebase confirms that the updated check in Body.ts aligns with the overall Blob handling strategy.

.changeset/mighty-worlds-smoke.md (1)

1-7: LGTM! Clear and informative changeset.

The changeset properly documents the issue being fixed and its impact across multiple packages.

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.

Node.js HTTP2 error
1 participant