From 5cdfe8efbb4e54613214555ecfa9d1f7fb28e336 Mon Sep 17 00:00:00 2001 From: simov Date: Fri, 10 Jul 2015 17:30:56 +0300 Subject: [PATCH 1/2] Add tests and docs for using the agent, agentClass and agentOptions Forever option defaults to using http(s).Agent in node 0.12+ --- README.md | 13 +++--- lib/helpers.js | 10 +++++ request.js | 13 +++++- tests/test-agent.js | 99 ++++++++++++++++++++++++++++++++++++++------- 4 files changed, 114 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 21e6d79dc..77e3e1e27 100644 --- a/README.md +++ b/README.md @@ -772,7 +772,11 @@ The first argument can be either a `url` or an `options` object. The only requir --- -- `pool` - An object describing which agents to use for the request. If this option is omitted the request will use the global agent (as long as [your options allow for it](request.js#L747)). Otherwise, request will search the pool for your custom agent. If no custom agent is found, a new agent will be created and added to the pool. +- `agent` - `http(s).Agent` instance to use +- `agentClass` - alternatively specify your agent's class name +- `agentOptions` - and pass its options. **Note:** for HTTPS see [tls API doc for TLS/SSL options](http://nodejs.org/api/tls.html#tls_tls_connect_options_callback) and the [documentation above](#using-optionsagentoptions). +- `forever` - set to `true` to use the [forever-agent](https://github.com/request/forever-agent) **Use only with node 0.10-** +- `pool` - An object describing which agents to use for the request. If this option is omitted the request will use the global agent (as long as your options allow for it). Otherwise, request will search the pool for your custom agent. If no custom agent is found, a new agent will be created and added to the pool. **Note:** `pool` is used only when the `agent` option is not specified. - A `maxSockets` property can also be provided on the `pool` object to set the max number of sockets for all agents created (ex: `pool: {maxSockets: Infinity}`). - Note that if you are sending multiple requests in a loop and creating multiple new `pool` objects, `maxSockets` will not work as intended. To @@ -783,10 +787,12 @@ The first argument can be either a `url` or an `options` object. The only requir request to respond before aborting the request. Note that if the underlying TCP connection cannot be established, the OS-wide TCP connection timeout will overrule the `timeout` option ([the default in Linux is around 20 seconds](http://www.sekuda.com/overriding_the_default_linux_kernel_20_second_tcp_socket_connect_timeout)). + +--- + - `localAddress` - Local interface to bind for network connections. - `proxy` - An HTTP proxy to be used. Supports proxy Auth with Basic Auth, identical to support for the `url` parameter (by embedding the auth info in the `uri`) - `strictSSL` - If `true`, requires SSL certificates be valid. **Note:** to use your own certificate authority, you need to specify an agent that was created with that CA as an option. -- `agentOptions` - Object containing user agent options. See documentation above. **Note:** [see tls API doc for TLS/SSL options](http://nodejs.org/api/tls.html#tls_tls_connect_options_callback). - `tunnel` - controls the behavior of [HTTP `CONNECT` tunneling](https://en.wikipedia.org/wiki/HTTP_tunnel#HTTP_CONNECT_tunneling) as follows: @@ -803,9 +809,6 @@ The first argument can be either a `url` or an `options` object. The only requir --- - `time` - If `true`, the request-response cycle (including all redirects) is timed at millisecond resolution, and the result provided on the response's `elapsedTime` property. - ---- - - `har` - A [HAR 1.2 Request Object](http://www.softwareishard.com/blog/har-12-spec/#request), will be processed from HAR format into options overwriting matching values *(see the [HAR 1.2 section](#support-for-har-1.2) for details)* The callback argument gets 3 arguments: diff --git a/lib/helpers.js b/lib/helpers.js index 5cc79da86..5e8594606 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -54,6 +54,15 @@ function copy (obj) { return o } +function version () { + var numbers = process.version.replace('v', '').split('.') + return { + major: parseInt(numbers[0], 10), + minor: parseInt(numbers[1], 10), + patch: parseInt(numbers[2], 10) + } +} + exports.isFunction = isFunction exports.paramsHaveRequestBody = paramsHaveRequestBody exports.safeStringify = safeStringify @@ -61,4 +70,5 @@ exports.md5 = md5 exports.isReadStream = isReadStream exports.toBase64 = toBase64 exports.copy = copy +exports.version = version exports.defer = deferMethod() diff --git a/request.js b/request.js index e61db5e1b..3664f9ea3 100644 --- a/request.js +++ b/request.js @@ -31,6 +31,7 @@ var safeStringify = helpers.safeStringify , toBase64 = helpers.toBase64 , defer = helpers.defer , copy = helpers.copy + , version = helpers.version , globalCookieJar = cookies.jar() @@ -477,7 +478,17 @@ Request.prototype.init = function (options) { if (options.agentClass) { self.agentClass = options.agentClass } else if (options.forever) { - self.agentClass = protocol === 'http:' ? ForeverAgent : ForeverAgent.SSL + var v = version() + // use ForeverAgent in node 0.10- only + if (v.major === 0 && v.minor <= 10) { + self.agentClass = protocol === 'http:' ? ForeverAgent : ForeverAgent.SSL + } else { + console.warn('The forever option defaults to using http(s).Agent in 0.12+') + self.agent = new self.httpModule.Agent({ + keepAlive: true, + maxSockets: (options.pool && options.pool.maxSockets) || Infinity + }) + } } else { self.agentClass = self.httpModule.Agent } diff --git a/tests/test-agent.js b/tests/test-agent.js index cca03d410..8d18d35c6 100644 --- a/tests/test-agent.js +++ b/tests/test-agent.js @@ -1,34 +1,103 @@ 'use strict' var request = require('../index') - , http = require('http') - , tape = require('tape') + , version = require('../lib/helpers').version + , http = require('http') + , ForeverAgent = require('forever-agent') + , tape = require('tape') -var s = http.createServer(function(req, res) { +var s = http.createServer(function (req, res) { res.statusCode = 200 - res.end('ok') + res.end() }) -tape('setup', function(t) { +tape('setup', function (t) { s.listen(6767, function() { t.end() }) }) -tape('should work with forever agent', function(t) { - var r = request.forever({maxSockets: 1}) +function httpAgent (t, options, req) { + var r = (req || request)(options, function (_err, res, body) { - r({ - url: 'http://localhost:6767', - headers: { 'Connection':'Close' } - }, function(err, resp, body) { - t.equal(err, null) - t.equal(body, 'ok') - t.end() + t.ok(r.agent instanceof http.Agent, 'is http.Agent') + t.equal(r.agent.options.keepAlive, true, 'is keepAlive') + t.equal(Object.keys(r.agent.sockets).length, 1, '1 socket name') + + var name = (typeof r.agent.getName === 'function') + ? r.agent.getName({port:6767}) + : 'localhost:6767' // node 0.10- + t.equal(r.agent.sockets[name].length, 1, '1 open socket') + + var socket = r.agent.sockets[name][0] + socket.on('close', function () { + t.equal(Object.keys(r.agent.sockets).length, 0, '0 open sockets') + t.end() + }) + socket.end() + }) +} + +function foreverAgent (t, options, req) { + var r = (req || request)(options, function (_err, res, body) { + + t.ok(r.agent instanceof ForeverAgent, 'is ForeverAgent') + t.equal(Object.keys(r.agent.sockets).length, 1, '1 socket name') + + var name = 'localhost:6767' // node 0.10- + t.equal(r.agent.sockets[name].length, 1, '1 open socket') + + var socket = r.agent.sockets[name][0] + socket.on('close', function () { + t.equal(Object.keys(r.agent.sockets[name]).length, 0, '0 open sockets') + t.end() + }) + socket.end() + }) +} + +// http.Agent + +tape('options.agent', function (t) { + httpAgent(t, { + uri: 'http://localhost:6767', + agent: new http.Agent({keepAlive: true}) }) }) -tape('cleanup', function(t) { +tape('options.agentClass + options.agentOptions', function (t) { + httpAgent(t, { + uri: 'http://localhost:6767', + agentClass: http.Agent, + agentOptions: {keepAlive: true} + }) +}) + +// forever-agent + +tape('options.forever = true', function (t) { + var v = version() + var options = { + uri: 'http://localhost:6767', + forever: true + } + + if (v.major === 0 && v.minor <= 10) {foreverAgent(t, options)} + else {httpAgent(t, options)} +}) + +tape('forever() method', function (t) { + var v = version() + var options = { + uri: 'http://localhost:6767' + } + var r = request.forever({maxSockets: 1}) + + if (v.major === 0 && v.minor <= 10) {foreverAgent(t, options, r)} + else {httpAgent(t, options, r)} +}) + +tape('cleanup', function (t) { s.close(function() { t.end() }) From 391b80bd3397dcf6125a7aa7f5338ebee8723b54 Mon Sep 17 00:00:00 2001 From: simov Date: Fri, 17 Jul 2015 17:57:37 +0300 Subject: [PATCH 2/2] Remove forever option warning and improve docs --- README.md | 2 +- request.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 77e3e1e27..b72276798 100644 --- a/README.md +++ b/README.md @@ -775,7 +775,7 @@ The first argument can be either a `url` or an `options` object. The only requir - `agent` - `http(s).Agent` instance to use - `agentClass` - alternatively specify your agent's class name - `agentOptions` - and pass its options. **Note:** for HTTPS see [tls API doc for TLS/SSL options](http://nodejs.org/api/tls.html#tls_tls_connect_options_callback) and the [documentation above](#using-optionsagentoptions). -- `forever` - set to `true` to use the [forever-agent](https://github.com/request/forever-agent) **Use only with node 0.10-** +- `forever` - set to `true` to use the [forever-agent](https://github.com/request/forever-agent) **Note:** Defaults to `http(s).Agent({keepAlive:true})` in node 0.12+ - `pool` - An object describing which agents to use for the request. If this option is omitted the request will use the global agent (as long as your options allow for it). Otherwise, request will search the pool for your custom agent. If no custom agent is found, a new agent will be created and added to the pool. **Note:** `pool` is used only when the `agent` option is not specified. - A `maxSockets` property can also be provided on the `pool` object to set the max number of sockets for all agents created (ex: `pool: {maxSockets: Infinity}`). - Note that if you are sending multiple requests in a loop and creating diff --git a/request.js b/request.js index 3664f9ea3..6932741c5 100644 --- a/request.js +++ b/request.js @@ -483,7 +483,6 @@ Request.prototype.init = function (options) { if (v.major === 0 && v.minor <= 10) { self.agentClass = protocol === 'http:' ? ForeverAgent : ForeverAgent.SSL } else { - console.warn('The forever option defaults to using http(s).Agent in 0.12+') self.agent = new self.httpModule.Agent({ keepAlive: true, maxSockets: (options.pool && options.pool.maxSockets) || Infinity