Skip to content

Conversation

@GroophyLifefor
Copy link
Member

@GroophyLifefor GroophyLifefor commented Jan 11, 2026

Core Approach:

  • Detected HTTP/2 streams using instanceof http2.Http2Stream
  • HTTP/2 streams already emit 'close' events naturally - we just needed to listen for them
  • Used stream.destroyed and stream.closed properties to check if stream is finished

Related to #57, can you review @Phillip9587 ?

Copy link
Member

@bjohansebas bjohansebas left a comment

Choose a reason for hiding this comment

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

Okay, I haven’t fully reviewed your PR yet, although I’ve also been working on this (see #87). I’m implementing tests using compatibility mode, while yours uses native HTTP/2

@GroophyLifefor
Copy link
Member Author

Okay, I haven’t fully reviewed your PR yet, although I’ve also been working on this (see #87). I’m implementing tests using compatibility mode, while yours uses native HTTP/2

Hi @bjohansebas , thanks for the note and for working on this as well. I want to better understand your approach. When you mention compatibility mode, is the main goal to cover how most users run HTTP two in Node, or are there specific lifecycle or edge cases that native Http2Stream based handling might miss. I am wondering if supporting both native HTTP two and compatibility mode would make sense here.

@bjohansebas
Copy link
Member

bjohansebas commented Jan 11, 2026

I feel we could handle both cases. For Express HTTP/2 support, it will be done through the compatibility layer, which is why I’ve mainly focused on that in my other PR (the reason being that this is exactly why the compatibility mode exists; it’s also how Fastify handles it, and it saves us from having to re-implement many of our middlewares and Express itself).

I think we could support both possible cases and merge our two PRs. That’s how I’m currently thinking about it, although I’m still testing things, since I’ve discovered some specific edge cases that affect both Node.js’s implementation and ours.

Edit: I still haven’t found an edge case, or at least I think so. I was just doing some of the tests incorrectly in #87 .

index.js Outdated
Comment on lines 72 to 91
if (http2.Http2Stream && msg instanceof http2.Http2Stream) {
return Boolean(msg.destroyed || msg.closed)
}

Copy link
Member

@jonchurch jonchurch Jan 12, 2026

Choose a reason for hiding this comment

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

These added lines will never execute because both Http2ServerResponse and Http2Stream have the writeableEnded property, so we will return early in the first condition which checks for it on line 72

I don't think any functional changes are required to master to get http2 support, just tests.

Looks like the request abort scenario does need stream.closed check, but it need to come first or else we will return writeablEnded which is not sufficient for all cases in http2

Copy link
Member Author

Choose a reason for hiding this comment

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

Hi @jonchurch , I did some changes on that with more correct handling, it's now not uses native http2 because not needing, just checks with executive.

Console Test Output
 npm run test                                                                                                                                                                  01/12/26 18:50:11 PM

> on-finished@2.4.1 test
> mocha --reporter spec --check-leaks test/



  HTTP/2
    onFinished(res, listener)
      with ServerResponse
http2
        ✔ should fire when response finishes
http2
        ✔ should include the stream object
http2
        ✔ should fire when response is destroyed
        when called after finish
http2
http2
          ✔ should fire immediately
          with async local storage
http2
http2
            ✔ should persist store in callback
        with async local storage
http2
          ✔ should persist store in callback
      with multiple streams
http2
http2
        ✔ should fire for each stream independently
      when client closes connection
http2
        ✔ should fire the callback
      when stream errors
http2
        ✔ should fire the callback
    isFinished(res)
http2
      ✔ should return false for unfinished stream
http2
http2
      ✔ should return true for finished stream
http2
      ✔ should return true for destroyed stream

  onFinished(res, listener)
    ✔ should invoke listener given an unknown object
    ✔ should throw TypeError if listener is not a function
    when the response finishes
http1
      ✔ should fire the callback
http1
      ✔ should include the response object
      when called after finish
http1
http1
        ✔ should fire when called after finish
        when async local storage
http1
http1
          ✔ should presist store in callback
      when async local storage
http1
        ✔ should presist store in callback
    when using keep-alive
http1
http1
      ✔ should fire for each response
    when requests pipelined
http1
http1
http1
http1
      ✔ should fire for each request
    when response errors
http1
      ✔ should fire with error
http1
      ✔ should include the response object
    when the response aborts
http1
      ✔ should execute the callback
    when calling many times on same response
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
      ✔ should not print warnings

  isFinished(res)
    ✔ should return undefined for unknown object
http1
    ✔ should be false before response finishes
http1
http1
    ✔ should be true after response finishes
    when requests pipelined
http1
http1
http1
http1
      ✔ should have correct state when socket shared
http1
http1
      ✔ should handle aborted requests
    when response errors
http1
http1
      ✔ should return true
    when the response aborts
http1
http1
      ✔ should return true

  onFinished(req, listener)
    ✔ should throw TypeError if listener is not a function
    when the request finishes
http1
      ✔ should fire the callback
http1
      ✔ should include the request object
      when called after finish
http1
http1
        ✔ should fire when called after finish
        when async local storage
http1
http1
          ✔ should presist store in callback
      when async local storage
http1
        ✔ should presist store in callback
    when using keep-alive
http1
http1
      ✔ should fire for each request
    when request errors
http1
      ✔ should fire with error
http1
      ✔ should include the request object
    when requests pipelined
http1
http1
      ✔ should handle socket errors
    when the request aborts
http1
      ✔ should execute the callback
    when calling many times on same request
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
http1
      ✔ should not print warnings
    when CONNECT method
http1
      ✔ should fire when request finishes
http1
http1
      ✔ should fire when called after finish
    when Upgrade request
http1
      ✔ should fire when request finishes
http1
http1
      ✔ should fire when called after finish

  isFinished(req)
    ✔ should return undefined for unknown object
http1
    ✔ should be false before request finishes
http1
http1
    ✔ should be true after request finishes
    when request data buffered
http1
http1
      ✔ should be false before request finishes
    when request errors
http1
http1
      ✔ should return true
    when the request aborts
http1
http1
      ✔ should return true
    when CONNECT method
http1
      ✔ should be true immediately
http1
http1
      ✔ should be true after request finishes
    when Upgrade request
http1
      ✔ should be true immediately
http1
http1
      ✔ should be true after request finishes


  58 passing (410ms)

@GroophyLifefor
Copy link
Member Author

Do not merge this PR, moved to #87

Will closed after #87 merged.

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