Skip to content

Commit 18cb437

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 a3307ea commit 18cb437

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

src/node_http2.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -742,8 +742,10 @@ void Http2Session::Close(uint32_t code, bool socket_closed) {
742742
flags_ |= SESSION_STATE_CLOSING;
743743

744744
// Stop reading on the i/o stream
745-
if (stream_ != nullptr)
745+
if (stream_ != nullptr) {
746+
flags_ |= SESSION_STATE_READING_STOPPED;
746747
stream_->ReadStop();
748+
}
747749

748750
// If the socket is not closed, then attempt to send a closing GOAWAY
749751
// frame. There is no guarantee that this GOAWAY will be received by
@@ -1192,6 +1194,7 @@ int Http2Session::OnDataChunkReceived(nghttp2_session* handle,
11921194
// If we are currently waiting for a write operation to finish, we should
11931195
// tell nghttp2 that we want to wait before we process more input data.
11941196
if (session->flags_ & SESSION_STATE_WRITE_IN_PROGRESS) {
1197+
CHECK_NE(session->flags_ & SESSION_STATE_READING_STOPPED, 0);
11951198
session->flags_ |= SESSION_STATE_NGHTTP2_RECV_PAUSED;
11961199
return NGHTTP2_ERR_PAUSE;
11971200
}
@@ -1546,6 +1549,7 @@ void Http2Session::OnStreamAfterWrite(WriteWrap* w, int status) {
15461549
ClearOutgoing(status);
15471550

15481551
if ((flags_ & SESSION_STATE_READING_STOPPED) &&
1552+
!(flags_ & SESSION_STATE_WRITE_IN_PROGRESS) &&
15491553
nghttp2_session_want_read(session_)) {
15501554
flags_ &= ~SESSION_STATE_READING_STOPPED;
15511555
stream_->ReadStart();
Lines changed: 47 additions & 0 deletions
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)