镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

http2: confusion with how aborted ClientHttp2Stream is reported #56627

Description

@santigimeno

Take this sample code:

const h2 = require('http2');

const server = h2.createServer();
server.on('stream', (stream) => {
  stream.session.destroy();
});

server.listen(0, () => {
  const client = h2.connect(`http://localhost:${server.address().port}`);

  client.on('close', () => {
    server.close();;
  });

  const clientStream = client.request();
  clientStream.on('aborted', () => {
    // Never called
    console.log('aborted');
  });

  clientStream.on('close', () => {
    // `rstCode === 8 (NGHTTP2_CANCEL) 
    console.log('close', clientStream.rstCode);
  });
  
  clientStream.on('error', (err) => {
    // Never called
    throw err;
  });
});

in which clientStream is aborted before any response is generated via the stream.session.destroy() call. I would expect in this case clientStream to emit an error, but by looking at the code, this specific case is explicitly not reporting the error because:

// RST code 8 not emitted as an error as its used by clients to signify
// abort and is already covered by aborted event, also allows more
// seamless compatibility with http1

but, as demonstrated in the example, the aborted event is not reported. I don't know exactly what the http1 compatibility means in this context.

Also, if we look at the documentation of the Http2Stream close event, it seems to contradict the following statement:

The HTTP/2 error code used when closing the stream can be retrieved using the http2stream.rstCode property. If the code is any value other than NGHTTP2_NO_ERROR (0), an 'error' event will have also been emitted.

Any thoughts whether this is a bug in the code, the documentation, both?.

Thanks!

Activity

  1. added
    http2Issues and PRs related to the http2 subsystem.
    on Jan 16, 2025
  2. RafaelGSS commented on Jan 16, 2025

    @RafaelGSS
    Member

    cc: @nodejs/http2

    IMO, this seems to be a bug in the code, but it might be a workaround instead due to the number of tests that break if we revert that operation. I'd defer the suggestion to @mcollina @jasnell @addaleax, but in case of no response, I'd try to emit a stream error in that scenario.

  3. RafaelGSS commented on Feb 28, 2025

    @RafaelGSS
    Member

    I was looking at it today, and it seems expected fromthe server-side point of view. The logs from server indicates the RST with code 0 (success)

    send: reset nghttp2_active_outbound_item
    send: aob->item = (nil)
    Http2Session server (13) session created
    Http2Session server (13) i/o stream consumed
    Http2Session server (13) scheduling write
    Http2Session server (13) sending pending data
    stream: adjusting kept idle streams num_idle_streams=0, max=100
    send: next frame: payloadlen=0, type=4, flags=0x00, stream_id=0
    send: start transmitting frame type=4, length=9
    Http2Session server (13) nghttp2 has 9 bytes to send
    stream: adjusting kept idle streams num_idle_streams=0, max=100
    send: end transmission of a frame
    send: reset nghttp2_active_outbound_item
    send: aob->item = 0x5de10080b928
    Http2Session server (13) wants read? 1
    Http2Session server (13) receiving 57 bytes, offset 0
    Http2Session server (13) receiving 57 bytes [wants data? 1]
    recv: connection recv_window_size=0, local_window=65535
    stream: adjusting kept idle streams num_idle_streams=0, max=100
    recv: [IB_READ_FIRST_SETTINGS]
    recv: [IB_READ_HEAD]
    recv: payloadlen=0, type=4, flags=0x00, stream_id=0
    recv: SETTINGS
    recv: [IB_READ_SETTINGS]
    recv: readlen=0, payloadleft=0
    Http2Session server (13) complete frame received: type: 4
    Http2Session server (13) handling settings frame
    recv: [IB_READ_HEAD]
    recv: payloadlen=15, type=1, flags=0x05, stream_id=1
    recv: HEADERS
    recv: no padding in payload
    stream: dep_add dep_stream(0x5de10098b278)=0, stream(0x5de100966bd8)=1
    stream: adjusting kept closed streams num_closed_streams=0, num_incoming_streams=1, max_concurrent_streams=4294967295
    recv: call on_begin_headers callback stream_id=1
    Http2Session server (13) beginning headers for stream 1
    Http2Session server (13) Adding stream: 1
    recv: [IB_READ_HEADER_BLOCK]
    recv: readlen=15, payloadleft=0
    recv: block final=1
    recv: decoding header block 15 bytes
    inflatehd: start state=1
    inflatehd: indexed repr
    inflatehd: decoded integer is 2
    inflatehd: index=2
    inflatehd: header emission: :method: GET
    recv: proclen=1
    Http2Session server (13) handling header key/pair for stream 1
    inflatehd: start state=2
    inflatehd: literal header repr - indexed name
    inflatehd: indexing required=1, no_index=0
    inflatehd: decoded integer is 1
    inflatehd: index=1
    inflatehd: huffman encoded=1
    inflatehd: decoded integer is 10
    inflatehd: valuelen=10
    inflatehd: 10 bytes read
    inflatehd: header emission: :authority: localhost:1244
    recv: proclen=12
    Http2Session server (13) handling header key/pair for stream 1
    inflatehd: start state=2
    inflatehd: indexed repr
    inflatehd: decoded integer is 6
    inflatehd: index=6
    inflatehd: header emission: :scheme: http
    recv: proclen=1
    Http2Session server (13) handling header key/pair for stream 1
    inflatehd: start state=2
    inflatehd: indexed repr
    inflatehd: decoded integer is 4
    inflatehd: index=4
    inflatehd: header emission: :path: /
    recv: proclen=1
    Http2Session server (13) handling header key/pair for stream 1
    inflatehd: start state=2
    inflatehd: all input bytes were processed
    inflatehd: in_final set
    recv: proclen=0
    Http2Session server (13) complete frame received: type: 1
    Http2Session server (13) handle headers frame for stream 1
    Destroying new stream...
    HttpStream 1 (17) [Http2Session server (13)] sending rst_stream with code 0
    Http2Session server (13) sending pending data
    stream: adjusting kept idle streams num_idle_streams=0, max=100
    send: next frame: payloadlen=0, type=4, flags=0x01, stream_id=0
    send: start transmitting frame type=4, length=9
    Http2Session server (13) nghttp2 has 9 bytes to send
    stream: adjusting kept idle streams num_idle_streams=0, max=100
    send: end transmission of a frame
    send: reset nghttp2_active_outbound_item
    send: aob->item = 0x5de10080b928
    Http2Session server (13) wants read? 1
    HttpStream 1 (17) [Http2Session server (13)] destroying stream
    HttpStream 1 (17) [Http2Session server (13)] destroying stream
    Http2Session server (13) Removing stream: 1
    Http2Session server (13) destroying session
    Http2Session server (13) closing session
    Http2Session server (13) terminating session with code 0
    Http2Session server (13) sending pending data
    stream: adjusting kept idle streams num_idle_streams=0, max=100
    send: frame preparation failed with The current session is closing
    Http2Session server (13) frame type 3 was not sent, code: -530
    send: reset nghttp2_active_outbound_item
    send: aob->item = (nil)
    send: next frame: payloadlen=8, type=7, flags=0x00, stream_id=0
    send: start transmitting frame type=7, length=17
    Http2Session server (13) nghttp2 has 17 bytes to send
    stream: adjusting kept idle streams num_idle_streams=0, max=100
    send: end transmission of a frame
    send: reset nghttp2_active_outbound_item
    send: aob->item = 0x5de10098bfa8
    Http2Session server (13) make done session callback
    HttpStream 1 (17) [Http2Session server (13)] tearing down stream
    Http2Session server (13) freeing nghttp2 session
    send: reset nghttp2_active_outbound_item
    send: aob->item = (nil)
    

    Note the nghttp2 can send the code 8 without calling Node.js handler (which, unfortunately, happens). I tried to remove the NGHTTP_CANCEL bypass but 16 tests broke after that. According to the SPEC it seems we are doing it wrong, but I don't know the impact of changing this behavior.

  4. github-actions commented on Apr 23, 2026

    @github-actions
    Contributor

    This issue has been marked as stale due to 210 days of inactivity.
    It will be automatically closed in 30 days if no further activity occurs. If this is still relevant, please leave a comment or update it to keep it open.

  5. added
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Apr 23, 2026
  6. removed
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    http2Issues and PRs related to the http2 subsystem.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions