Skip to content

HTTP2: Calling res.end after res.writeHead(204) cause an error #21740

Closed
@RidgeA

Description

@RidgeA
  • 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    http2Issues or PRs related to the http2 subsystem.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions