Skip to content

Commit c3a90c4

Browse files
ywave620richardlau
authored andcommitted
http2: fix memory leak when nghttp2 hd threshold is reached
Refs: #45295 PR-URL: #41502 Backport-PR-URL: #45310 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
1 parent d6f1d71 commit c3a90c4

File tree

3 files changed

+72
-0
lines changed

3 files changed

+72
-0
lines changed

src/node_http2.cc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,21 @@ int Http2Session::OnInvalidFrame(nghttp2_session* handle,
923923
return 0;
924924
}
925925

926+
// Remove the headers reference.
927+
// Implicitly calls nghttp2_rcbuf_decref
928+
void Http2Session::DecrefHeaders(const nghttp2_frame* frame) {
929+
int32_t id = GetFrameID(frame);
930+
BaseObjectPtr<Http2Stream> stream = FindStream(id);
931+
932+
if (stream && !stream->is_destroyed() && stream->headers_count() > 0) {
933+
Debug(this, "freeing headers for stream %d", id);
934+
stream->ClearHeaders();
935+
CHECK_EQ(stream->headers_count(), 0);
936+
DecrementCurrentSessionMemory(stream->current_headers_length_);
937+
stream->current_headers_length_ = 0;
938+
}
939+
}
940+
926941
// If nghttp2 is unable to send a queued up frame, it will call this callback
927942
// to let us know. If the failure occurred because we are in the process of
928943
// closing down the session or stream, we go ahead and ignore it. We don't
@@ -943,6 +958,11 @@ int Http2Session::OnFrameNotSent(nghttp2_session* handle,
943958
error_code == NGHTTP2_ERR_STREAM_CLOSED ||
944959
error_code == NGHTTP2_ERR_STREAM_CLOSING ||
945960
session->js_fields_->frame_error_listener_count == 0) {
961+
// Nghttp2 contains header limit of 65536. When this value is exceeded the
962+
// pipeline is stopped and we should remove the current headers reference
963+
// to destroy the session completely.
964+
// Further information see: https://github.com/nodejs/node/issues/35233
965+
session->DecrefHeaders(frame);
946966
return 0;
947967
}
948968

src/node_http2.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,10 @@ class Http2Stream : public AsyncWrap,
401401
size_t i = 0;
402402
for (const auto& header : current_headers_ )
403403
fn(header, i++);
404+
ClearHeaders();
405+
}
406+
407+
void ClearHeaders() {
404408
current_headers_.clear();
405409
}
406410

@@ -784,6 +788,8 @@ class Http2Session : public AsyncWrap,
784788
void HandleAltSvcFrame(const nghttp2_frame* frame);
785789
void HandleOriginFrame(const nghttp2_frame* frame);
786790

791+
void DecrefHeaders(const nghttp2_frame* frame);
792+
787793
// nghttp2 callbacks
788794
static int OnBeginHeadersCallback(
789795
nghttp2_session* session,
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
const h2 = require('http2');
7+
8+
const server = h2.createServer();
9+
10+
server.on('stream', common.mustNotCall());
11+
server.on('error', common.mustNotCall());
12+
13+
server.listen(0, common.mustCall(() => {
14+
15+
// Setting the maxSendHeaderBlockLength > nghttp2 threshold
16+
// cause a 'sessionError' and no memory leak when session destroy
17+
const options = {
18+
maxSendHeaderBlockLength: 100000
19+
};
20+
21+
const client = h2.connect(`http://localhost:${server.address().port}`,
22+
options);
23+
client.on('error', common.expectsError({
24+
code: 'ERR_HTTP2_SESSION_ERROR',
25+
name: 'Error',
26+
message: 'Session closed with error code 9'
27+
}));
28+
29+
const req = client.request({
30+
// Greater than 65536 bytes
31+
'test-header': 'A'.repeat(90000)
32+
});
33+
req.on('response', common.mustNotCall());
34+
35+
req.on('close', common.mustCall(() => {
36+
client.close();
37+
server.close();
38+
}));
39+
40+
req.on('error', common.expectsError({
41+
code: 'ERR_HTTP2_SESSION_ERROR',
42+
name: 'Error',
43+
message: 'Session closed with error code 9'
44+
}));
45+
req.end();
46+
}));

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy