Skip to content

HTTP/1 request destroy behavior change on framing error #24586

Closed
@dougwilson

Description

@dougwilson
  • Version: v10.13.0
  • Platform: Windows 10
  • Subsystem: http

I noticed a different in how the HTTP/1 request objects are left after a HTTP/1 framing error (which causes an error in the underlying http_parser). In Node.js 8 and below, the request object would be destroyed and be left in non-readable state (i.e. req.readable === false). It seems like in Node.js 10+ this is no longer the case. Is this an expected change? Express.js has underlying machinery that is looking to see if a request body should be read (since the request starts out in a pasued state since 0.10) and it doesn't attempt to read when it's no longer readable (since events like 'end' will not be emitted, leaving things hanging around forever).

Here is an example that reproduces the scenario:

var http = require('http')
var net = require('net')

var server = http.createServer(function (req, res) {
  setTimeout(function () {
    if (!req.socket.readable) {
      console.log('req already terminated')
      server.close()
      return
    }

    console.log('req read start')

    var bufs = []

    req.on('data', function (chunk) {
      console.log('request recv %d bytes', chunk.length)
      bufs.push(chunk)
    })

    req.on('end', function () {
      var data = Buffer.concat(bufs)
      console.log('request got %d bytes', data.length)
      res.end('OK')
    })

    req.on('close', function () {
      console.log('clean up here...')
      server.close()
    })
  }, 5000)
})

server.listen(0, function () {
  var port = server.address().port
  var sock = net.createConnection(port, '127.0.0.1')

  sock.on('connect', function () {
    sock.write('POST / HTTP/1.1\r\n')
    sock.write('Host: localhost\r\n')
    sock.write('Transfer-Encoding: chunked\r\n')
    sock.write('\r\n')
    sock.write('W')
    sock.on('close', function () {
      console.log('client socket closed')
    })
  })
})

setTimeout(function () {
  console.log('timeout!')
  process.exit(1)
}, 10000).unref()

In Node.js 8 and lower you get the following output:

client socket closed
req already terminated

In Node.js 10 you get the following output:

req read start
timeout!

I'm not sure if this was an expected change or not. If it is an expected change, I'm just looking for what the correct way to know if a bit of code (the part inside setTimeout) that runs at an arbitrary time after the request came in should know if it's actually ever going to get a complete read or not. The only thing I've found so far in Node.js 10 is that you have to try and read no matter what and just rely on coding in a read timeout to know when to give up instead of knowing earlier.

Metadata

Metadata

Assignees

No one assigned

    Labels

    httpIssues or PRs related to the http subsystem.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions