Description
- Version: v10.6.0
- Platform: Linux localhost.localdomain 4.17.3-200.fc28.x86_64 deps: update openssl to 1.0.1j #1 SMP Tue Jun 26 14:17:07 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux (Fedora 28)
- Subsystem: http2
Hi!
I'm playing around with http2 module and server push and faced with a strange error.
Invoking of res.end()
method after res.writeHeader(204)
causes error:
internal/http2/compat.js:377
this.sendTrailers(this[kResponse][kTrailers]);
^
TypeError: Cannot read property 'Symbol(trailers)' of undefined
at ServerHttp2Stream.onStreamTrailersReady (internal/http2/compat.js:377:36)
at ServerHttp2Stream.emit (events.js:182:13)
at Http2Stream.onStreamTrailers [as ontrailers] (internal/http2/core.js:311:15)
Code to reproduce:
"use strict";
const http2 = require('http2');
const fs = require('fs');
const cert = {
key: fs.readFileSync('./cert/key.pem'),
cert: fs.readFileSync('./cert/cert.pem')
};
const html =
`
<!doctype html>
<head>
<title>HTTP/2 Test</title>
<link rel="stylesheet" href="/styles.css" type="text/css">
<script src="/bundle.js" async></script>
</head>
<body>
<h1>Hello</h1>
</body>
</html>
`;
http2.createSecureServer(cert, (req, res) => {
const {url} = req;
if (['/index.html', '/'].includes(url)) {
res.writeHead(200, {'content-type':'text/html'});
res.end(html);
} else {
res.writeHead(204);
res.end();
}
}).listen(8443, err => {
if (!err) {
console.log(`Server started at ${8443}.`)
}
});
After digging into the issue, I found out that if we set 204, 205 and 304 status codes the stream are closed automatically - see https://github.com/nodejs/node/blob/master/lib/internal/http2/core.js#L2266 and https://github.com/nodejs/node/blob/master/lib/internal/http2/core.js#L2281
Calling of req.end
method after that causes an error (see above).
According to the documentation req.end()
MUST be called for each request: https://nodejs.org/api/http2.html#http2_response_end_data_encoding_callback
but it is impossible due to closing the stream caused by calling writeHeader
method.
I believe, in order to keep backward compatibility with http
/https
modules, the res.end
method shouldn't throw an error in such case.