Description
- Version: v10.10.0
- Platform: Linux david-Latitude-E6440 4.15.0-34-generic logo ideas #37-Ubuntu SMP Mon Aug 27 15:21:48 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
- Subsystem: http2
Here's a simple server app which reads HTTP/2 POST request bodies to their end and then responds:
const http2 = require('http2');
const fs = require('fs');
const path = require('path');
const { Writable } = require('stream');
const server = http2.createSecureServer({
key: fs.readFileSync(path.join(__dirname, 'server.key')),
cert: fs.readFileSync(path.join(__dirname, 'server.crt'))
});
server.on('session', function (session) {
session.on('stream', function (stream, headers) {
stream.on('end', function () {
this.respond({
':status': 200,
'Access-Control-Allow-Origin': 'http://localhost:8000'
}, {
endStream: true
});
});
stream.pipe(new Writable({
write: (chunk, encoding, cb) => cb()
}));
});
});
server.listen(7000, function () {
console.log('READY.');
});
For this test, the cert and key are self-issued and my browser trusts them.
Here's a Web page which makes requests in series to the server:
<html>
<head>
<script>
async function test() {
while (true) {
response = await fetch('https://localhost:7000', { method: 'POST' });
await response.arrayBuffer();
}
}
</script>
</head>
<body onload='test()'>
</body>
</html>
I expect this to continue indefinitely.
However, what happens is I get the following error after 40701 iterations:
memtest.html:6 POST https://localhost:7000/ net::ERR_SPDY_PROTOCOL_ERROR
I did some debugging using Wireshark and found the error was ENHANCE_YOUR_CALM
.
After adding some tracing to node_http2.cc
, I found the error being produced here: https://github.com/nodejs/node/blob/v10.10.0/src/node_http2.cc#L863
I've tracked this down to current_nghttp2_memory_
continually growing.
The cause for this is nghttp2 allocation of 232 bytes for each stream (https://github.com/nodejs/node/blob/v10.10.0/deps/nghttp2/lib/nghttp2_session.c#L1029) is never being released.
This is because nghttp2 keeps closed streams around (up to the concurrent connection limit).
If I add the following line to Http2Options::Http2Options
in src/node_http2.cc
:
nghttp2_option_set_no_closed_streams(options_, 1);
then the test works as expected and doesn't fail at 40701 iterations.