Skip to content

Commit d6ba106

Browse files
addaleaxtargos
authored andcommitted
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: #29353 Fixes: #29393 PR-URL: #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 4374d28 commit d6ba106

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
@@ -745,8 +745,10 @@ void Http2Session::Close(uint32_t code, bool socket_closed) {
745745
flags_ |= SESSION_STATE_CLOSING;
746746

747747
// Stop reading on the i/o stream
748-
if (stream_ != nullptr)
748+
if (stream_ != nullptr) {
749+
flags_ |= SESSION_STATE_READING_STOPPED;
749750
stream_->ReadStop();
751+
}
750752

751753
// If the socket is not closed, then attempt to send a closing GOAWAY
752754
// frame. There is no guarantee that this GOAWAY will be received by
@@ -1225,6 +1227,7 @@ int Http2Session::OnDataChunkReceived(nghttp2_session* handle,
12251227
// If we are currently waiting for a write operation to finish, we should
12261228
// tell nghttp2 that we want to wait before we process more input data.
12271229
if (session->flags_ & SESSION_STATE_WRITE_IN_PROGRESS) {
1230+
CHECK_NE(session->flags_ & SESSION_STATE_READING_STOPPED, 0);
12281231
session->flags_ |= SESSION_STATE_NGHTTP2_RECV_PAUSED;
12291232
return NGHTTP2_ERR_PAUSE;
12301233
}
@@ -1583,6 +1586,7 @@ void Http2Session::OnStreamAfterWrite(WriteWrap* w, int status) {
15831586
ClearOutgoing(status);
15841587

15851588
if ((flags_ & SESSION_STATE_READING_STOPPED) &&
1589+
!(flags_ & SESSION_STATE_WRITE_IN_PROGRESS) &&
15861590
nghttp2_session_want_read(session_)) {
15871591
flags_ &= ~SESSION_STATE_READING_STOPPED;
15881592
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)