Skip to content

Socket setTimeout behaviour not working as expected for HTTP requests #5899

@mtharrison

Description

@mtharrison

Summary

timeout events are fired on sockets assigned to an HTTP request even when there is activity (data being written) within the timeout period. This is unexpected and not explicitly documented.

This did not used to be the case, it used to work like a normal net socket, with the timeout being essentially "reset" on any activity. The behaviour changed after this commit 607bbd3 without any obvious doc changes.

Full background

A TCP (net) socket has a setTimeout method which sets the socket to emit a timeout event after a certain number of ms of inactivity. This currently works correctly on all tested versions. An example is below:

var net = require('net');

var server = net.createServer(function (socket) {
    socket.setTimeout(200, socket.destroy);              // Setting to less than 100 will cause timeout
    socket.pipe(process.stdout);
});

server.listen(function () {
    var client = net.connect(server.address().port, function () {
        var interval = setInterval(function () {
            client.write('a');
        }, 100);
        setTimeout(function () {
            clearInterval(interval);
            client.end();
            server.close();
        }, 1000);
    });
});

There’s also a setTimeout method available on a HTTP request which proxies the setTimeout call through to the underlying TCP socket. So I expected it to work the same. But it does not, it will timeout even when data is being written actively to the socket.

var http = require('http');

var server = http.createServer(function (req, res) {

    req.setTimeout(200);                              // this will timeout even though data is being written
    req.pipe(process.stdout);
    req.on('end', function () { res.end() });
});

server.listen(function () {

    var address = server.address();
    var req = http.request({
        port: address.port,
        method: 'POST'
    });

    var interval = setInterval(function () {
        req.write('a');
    }, 100);
    setTimeout(function () {
        clearInterval(interval);
        req.end();
        server.close();
    }, 1000);
});

It was an issue posted at hapijs/hapi#3099 that led me to this digging. I used the script above to git bisect the Node core repo back to the commit where the behaviour changed 607bbd3 which is part of PR #2355.

Conclusion

I think this is either a bug or If the new behaviour is intended, a documentation update is probably needed to explicitly state that the timeout on an http socket works differently than a regular net socket.

  • Version: 607bbd3 - master
  • Platform: Darwin 15.3.0 Darwin Kernel Version 15.3.0: Thu Dec 10 18:40:58 PST 2015; root:xnu-3248.30.4~1/RELEASE_X86_64 x86_64
  • Subsystem: net, http

Metadata

Metadata

Assignees

No one assigned

    Labels

    httpIssues or PRs related to the http subsystem.netIssues and PRs related to the net subsystem.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions