From 1b61414ccdd0e1b5969219ba3ec7664d1f3ab495 Mon Sep 17 00:00:00 2001 From: Akshay K Date: Fri, 30 Jul 2021 18:46:45 -0400 Subject: [PATCH 1/2] http2: update handling of rst_stream with error code NGHTTP2_CANCEL --- src/node_http2.cc | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/node_http2.cc b/src/node_http2.cc index 625f3adb3b9a9e..c01fa4d5255077 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -2196,21 +2196,21 @@ void Http2Stream::SubmitRstStream(const uint32_t code) { CHECK(!this->is_destroyed()); code_ = code; - // If RST_STREAM frame is received and stream is not writable - // because it is busy reading data, don't try force purging it. - // Instead add the stream to pending stream list and process - // the pending data when it is safe to do so. This is to avoid - // double free error due to unwanted behavior of nghttp2. - // Ref:/~https://github.com/nodejs/node/issues/38964 - - // Add stream to the pending list if it is received with scope + auto is_stream_cancel = [](const uint32_t code) { + return code == NGHTTP2_CANCEL; + }; + + // If RST_STREAM frame is received with error code NGHTTP2_CANCEL, + // add it to the pending list and don't force purge the data. It is + // to avoids the double free error due to unwanted behavior of nghttp2. + + // Add stream to the pending list only if it is received with scope // below in the stack. The pending list may not get processed // if RST_STREAM received is not in scope and added to the list // causing endpoint to hang. - if (session_->is_in_scope() && - !is_writable() && is_reading()) { - session_->AddPendingRstStream(id_); - return; + if (session_->is_in_scope() && is_stream_cancel(code)) { + session_->AddPendingRstStream(id_); + return; } From 9b39a6b0c5e41a67c0fd180c884dca1b38e16b7b Mon Sep 17 00:00:00 2001 From: Akshay K Date: Thu, 5 Aug 2021 03:01:43 -0400 Subject: [PATCH 2/2] http2: add tests for cancel event while client is paused reading --- .../test-http2-cancel-while-client-reading.js | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 test/parallel/test-http2-cancel-while-client-reading.js diff --git a/test/parallel/test-http2-cancel-while-client-reading.js b/test/parallel/test-http2-cancel-while-client-reading.js new file mode 100644 index 00000000000000..0605a02e116626 --- /dev/null +++ b/test/parallel/test-http2-cancel-while-client-reading.js @@ -0,0 +1,36 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +if (!common.hasCrypto) { + common.skip('missing crypto'); +} + +const http2 = require('http2'); +const key = fixtures.readKey('agent1-key.pem', 'binary'); +const cert = fixtures.readKey('agent1-cert.pem', 'binary'); + +const server = http2.createSecureServer({ key, cert }); + +let client_stream; + +server.on('stream', common.mustCall(function(stream) { + stream.resume(); + stream.on('data', function(chunk) { + stream.write(chunk); + client_stream.pause(); + client_stream.close(http2.constants.NGHTTP2_CANCEL); + }); +})); + +server.listen(0, function() { + const client = http2.connect(`https://localhost:${server.address().port}`, + { rejectUnauthorized: false } + ); + client_stream = client.request({ ':method': 'POST' }); + client_stream.on('close', common.mustCall(() => { + client.close(); + server.close(); + })); + client_stream.resume(); + client_stream.write(Buffer.alloc(1024 * 1024)); +});