diff --git a/doc/api/http.md b/doc/api/http.md index ebd4fae7f36aba..d4c15f6c4ee01a 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -1430,6 +1430,8 @@ Options: request when the `agent` option is not used. This can be used to avoid creating a custom Agent class just to override the default `createConnection` function. See [`agent.createConnection()`][] for more details. +- `timeout`: A number specifying the socket timeout in milliseconds. + This will set the timeout before the socket is connected. The optional `callback` parameter will be added as a one time listener for the [`'response'`][] event. diff --git a/doc/api/net.md b/doc/api/net.md index f1b206308ce3f8..394e6fb86ea1e6 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -765,7 +765,9 @@ A factory function, which returns a new [`net.Socket`][] and automatically connects with the supplied `options`. The options are passed to both the [`net.Socket`][] constructor and the -[`socket.connect`][] method. +[`socket.connect`][] method. + +Passing `timeout` as an option will call [`socket.setTimeout()`][] after the socket is created, but before it is connecting. The `connectListener` parameter will be added as a listener for the [`'connect'`][] event once. diff --git a/lib/_http_client.js b/lib/_http_client.js index 91855e5b87ac3e..423445d6a6f091 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -67,6 +67,7 @@ function ClientRequest(options, cb) { } self.socketPath = options.socketPath; + self.timeout = options.timeout; var method = self.method = (options.method || 'GET').toUpperCase(); if (!common._checkIsHttpToken(method)) { @@ -134,7 +135,8 @@ function ClientRequest(options, cb) { self._last = true; self.shouldKeepAlive = false; const optionsPath = { - path: self.socketPath + path: self.socketPath, + timeout: self.timeout }; const newSocket = self.agent.createConnection(optionsPath, oncreate); if (newSocket && !called) { @@ -559,6 +561,10 @@ function tickOnSocket(req, socket) { socket.on('data', socketOnData); socket.on('end', socketOnEnd); socket.on('close', socketCloseListener); + + if (req.timeout) { + socket.once('timeout', () => req.emit('timeout')); + } req.emit('socket', socket); } diff --git a/lib/net.js b/lib/net.js index 298e7186aa6d60..5caf7747194bc5 100644 --- a/lib/net.js +++ b/lib/net.js @@ -66,6 +66,11 @@ exports.connect = exports.createConnection = function() { args = normalizeConnectArgs(args); debug('createConnection', args); var s = new Socket(args[0]); + + if (args[0].timeout) { + s.setTimeout(args[0].timeout); + } + return Socket.prototype.connect.apply(s, args); }; diff --git a/test/parallel/test-http-client-timeout-event.js b/test/parallel/test-http-client-timeout-event.js index 03aa847a80b45a..8e3d034c31d857 100644 --- a/test/parallel/test-http-client-timeout-event.js +++ b/test/parallel/test-http-client-timeout-event.js @@ -1,7 +1,7 @@ 'use strict'; -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); var options = { method: 'GET', @@ -10,30 +10,22 @@ var options = { path: '/' }; -var server = http.createServer(function(req, res) { - // this space intentionally left blank -}); +var server = http.createServer(); server.listen(0, options.host, function() { options.port = this.address().port; - var req = http.request(options, function(res) { - // this space intentionally left blank - }); + var req = http.request(options); req.on('error', function() { // this space is intentionally left blank }); - req.on('close', function() { - server.close(); - }); + req.on('close', common.mustCall(() => server.close())); var timeout_events = 0; req.setTimeout(1); - req.on('timeout', function() { - timeout_events += 1; - }); + req.on('timeout', common.mustCall(() => timeout_events += 1)); setTimeout(function() { req.destroy(); - assert.equal(timeout_events, 1); + assert.strictEqual(timeout_events, 1); }, common.platformTimeout(100)); setTimeout(function() { req.end(); diff --git a/test/parallel/test-http-client-timeout-option.js b/test/parallel/test-http-client-timeout-option.js new file mode 100644 index 00000000000000..79aa6ba46b6579 --- /dev/null +++ b/test/parallel/test-http-client-timeout-option.js @@ -0,0 +1,33 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +var options = { + method: 'GET', + port: undefined, + host: '127.0.0.1', + path: '/', + timeout: 1 +}; + +var server = http.createServer(); + +server.listen(0, options.host, function() { + options.port = this.address().port; + var req = http.request(options); + req.on('error', function() { + // this space is intentionally left blank + }); + req.on('close', common.mustCall(() => server.close())); + + var timeout_events = 0; + req.on('timeout', common.mustCall(() => timeout_events += 1)); + setTimeout(function() { + req.destroy(); + assert.strictEqual(timeout_events, 1); + }, common.platformTimeout(100)); + setTimeout(function() { + req.end(); + }, common.platformTimeout(10)); +});