Skip to content

Commit f1359fd

Browse files
committed
http2: do not start reading after write if new write is on wire
Don’t start reading more input data if we’re still busy writing output. This was overlooked in 8a4a193. Fixes: nodejs#29353 Fixes: nodejs#29393 PR-URL: nodejs#29399 Reviewed-By: David Carlier <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent 2f878d2 commit f1359fd

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

src/node_http2.cc

+5-1
Original file line numberDiff line numberDiff line change
@@ -746,8 +746,10 @@ void Http2Session::Close(uint32_t code, bool socket_closed) {
746746
flags_ |= SESSION_STATE_CLOSING;
747747

748748
// Stop reading on the i/o stream
749-
if (stream_ != nullptr)
749+
if (stream_ != nullptr) {
750+
flags_ |= SESSION_STATE_READING_STOPPED;
750751
stream_->ReadStop();
752+
}
751753

752754
// If the socket is not closed, then attempt to send a closing GOAWAY
753755
// frame. There is no guarantee that this GOAWAY will be received by
@@ -1228,6 +1230,7 @@ int Http2Session::OnDataChunkReceived(nghttp2_session* handle,
12281230
// If we are currently waiting for a write operation to finish, we should
12291231
// tell nghttp2 that we want to wait before we process more input data.
12301232
if (session->flags_ & SESSION_STATE_WRITE_IN_PROGRESS) {
1233+
CHECK_NE(session->flags_ & SESSION_STATE_READING_STOPPED, 0);
12311234
session->flags_ |= SESSION_STATE_NGHTTP2_RECV_PAUSED;
12321235
return NGHTTP2_ERR_PAUSE;
12331236
}
@@ -1616,6 +1619,7 @@ void Http2Session::OnStreamAfterWrite(WriteWrap* w, int status) {
16161619
ClearOutgoing(status);
16171620

16181621
if ((flags_ & SESSION_STATE_READING_STOPPED) &&
1622+
!(flags_ & SESSION_STATE_WRITE_IN_PROGRESS) &&
16191623
nghttp2_session_want_read(session_)) {
16201624
flags_ &= ~SESSION_STATE_READING_STOPPED;
16211625
stream_->ReadStart();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
const common = require('../common');
3+
if (!common.hasCrypto)
4+
common.skip('missing crypto');
5+
const fixtures = require('../common/fixtures');
6+
const http2 = require('http2');
7+
8+
// Regression test for https://github.com/nodejs/node/issues/29353.
9+
// Test that it’s okay for an HTTP2 + TLS server to destroy a stream instance
10+
// while reading it.
11+
12+
const server = http2.createSecureServer({
13+
key: fixtures.readKey('agent2-key.pem'),
14+
cert: fixtures.readKey('agent2-cert.pem')
15+
});
16+
17+
const filenames = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
18+
19+
server.on('stream', common.mustCall((stream) => {
20+
function write() {
21+
stream.write('a'.repeat(10240));
22+
stream.once('drain', write);
23+
}
24+
write();
25+
}, filenames.length));
26+
27+
server.listen(0, common.mustCall(() => {
28+
const client = http2.connect(`https://localhost:${server.address().port}`, {
29+
ca: fixtures.readKey('agent2-cert.pem'),
30+
servername: 'agent2'
31+
});
32+
33+
let destroyed = 0;
34+
for (const entry of filenames) {
35+
const stream = client.request({
36+
':path': `/${entry}`
37+
});
38+
stream.once('data', common.mustCall(() => {
39+
stream.destroy();
40+
41+
if (++destroyed === filenames.length) {
42+
client.destroy();
43+
server.close();
44+
}
45+
}));
46+
}
47+
}));

0 commit comments

Comments
 (0)