diff --git a/package.json b/package.json index c9bc70175..415023116 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "dependencies": { "bluebird": "~1.2.4", "chalk": "~0.4", - "forever-agent": "0.5.2", + "forever-agent": "~0.5.2", "lodash-node": "~2.4" }, "repository": { diff --git a/src/lib/connectors/_custom_agent.js b/src/lib/connectors/_custom_agent.js new file mode 100644 index 000000000..1ce924171 --- /dev/null +++ b/src/lib/connectors/_custom_agent.js @@ -0,0 +1,48 @@ +var ForeverAgent = require('forever-agent'); +var ForeverSSLAgent = require('forever-agent').SSL; + +var NativeAgent = require('http').Agent; +var NativeSSLAgent = require('https').Agent; + +var inherits = require('util').inherits; +var nativeKeepAlive = (function () { + var a = new NativeAgent(); + return !!a.freeSockets; +}()); + +function WrapForeverAgent(opts) { + ForeverAgent.call(this, opts); + var _addRequest = this.addRequest; + this.addRequest = function (req, host, port) { + req.useChunkedEncodingByDefault = false; + _addRequest.call(this, req, host, port); + }; +} +inherits(WrapForeverAgent, ForeverAgent); + +function WrapForeverSSLAgent(opts) { + ForeverSSLAgent.call(this, opts); + var _addRequest = this.addRequest; + this.addRequest = function (req, host, port) { + req.useChunkedEncodingByDefault = false; + _addRequest.call(this, req, host, port); + }; +} +inherits(WrapForeverSSLAgent, ForeverSSLAgent); + +function WrapNativeAgent(opts) { NativeAgent.call(this, opts); } +inherits(WrapNativeAgent, NativeAgent); + +function WrapNativeSSLAgent(opts) { NativeSSLAgent.call(this, opts); } +inherits(WrapNativeSSLAgent, NativeSSLAgent); + + +if (nativeKeepAlive) { + module.exports = WrapNativeAgent; + module.exports.SSL = WrapNativeSSLAgent; +} else { + module.exports = WrapForeverAgent; + module.exports.SSL = WrapForeverSSLAgent; +} + +module.exports.supportsNativeKeepAlive = nativeKeepAlive; \ No newline at end of file diff --git a/src/lib/connectors/_custom_forever_agent.js b/src/lib/connectors/_custom_forever_agent.js deleted file mode 100644 index c40ba3a6e..000000000 --- a/src/lib/connectors/_custom_forever_agent.js +++ /dev/null @@ -1,17 +0,0 @@ -module.exports = CustomForeverAgent; - -var ForeverAgent = require('forever-agent'); -var inherits = require('util').inherits; - -function CustomForeverAgent(opts) { - ForeverAgent.call(this, opts); -} -inherits(CustomForeverAgent, ForeverAgent); - -CustomForeverAgent.prototype.addRequest = function (req, host, port) { - // force this, so that requests will always use the connection pool - req.useChunkedEncodingByDefault = false; - ForeverAgent.prototype.addRequest.call(this, req, host, port); -}; - -CustomForeverAgent.SSL = ForeverAgent.SSL; \ No newline at end of file diff --git a/src/lib/connectors/http.js b/src/lib/connectors/http.js index 92ef9c4a9..3bb0d98ea 100644 --- a/src/lib/connectors/http.js +++ b/src/lib/connectors/http.js @@ -14,7 +14,7 @@ var handles = { }; var _ = require('../utils'); var qs = require('querystring'); -var ForeverAgent = require('./_custom_forever_agent'); +var ForeverAgent = require('./_custom_agent'); var ConnectionAbstract = require('../connection'); /** diff --git a/test/unit/specs/http_connector.js b/test/unit/specs/http_connector.js index 6d5e3eadf..219219e0e 100644 --- a/test/unit/specs/http_connector.js +++ b/test/unit/specs/http_connector.js @@ -1,12 +1,13 @@ describe('Http Connector', function () { + var _ = require('lodash-node'); var expect = require('expect.js'); var nock = require('nock'); var sinon = require('sinon'); var util = require('util'); - var ForeverAgent = require('forever-agent'); var http = require('http'); var https = require('https'); + var CustomAgent = require('../../../src/lib/connectors/_custom_agent'); var Host = require('../../../src/lib/host'); var errors = require('../../../src/lib/errors'); @@ -161,7 +162,7 @@ describe('Http Connector', function () { con.request({}, function () { expect(http.request.callCount).to.be(1); expect(https.request.callCount).to.be(0); - expect(http.request.lastCall.args[0].agent).to.be.a(ForeverAgent); + expect(http.request.lastCall.args[0].agent).to.be.a(CustomAgent); done(); }); }); @@ -171,7 +172,7 @@ describe('Http Connector', function () { con.request({}, function () { expect(http.request.callCount).to.be(0); expect(https.request.callCount).to.be(1); - expect(https.request.lastCall.args[0].agent).to.be.a(ForeverAgent.SSL); + expect(https.request.lastCall.args[0].agent).to.be.a(CustomAgent.SSL); done(); }); }); @@ -357,38 +358,47 @@ describe('Http Connector', function () { }); describe('Connection cleanup', function () { + // skip these tests if native keep alive requests are supported + if (CustomAgent.supportsNativeKeepAlive) { + return; + } + it('destroys any connections created', function (done) { this.timeout(null); var cp = require('child_process'); var path = require('path'); - var fixtures = path.join(__dirname, '../../fixtures/'); + var fixture = _.partial(path.join, __dirname, '../../fixtures'); var timeout; // start the timeout once we hear back from the client - var server = cp.fork(fixtures + 'keepalive_server.js'); - var client = cp.fork(fixtures + 'keepalive.js'); + var server = cp.fork(fixture('keepalive_server.js')) + .on('message', function (port) { + console.log('server sent port number', port); + client.send(port); + }) + .once('exit', function () { + console.log('server closed'); + }); - server.on('message', function (port) { - client.send(port); - }); + var client = cp.fork(fixture('keepalive.js')) + .on('message', function (output) { + console.log('client sent output', output); + expect(output).to.have.property('remaining', 0); + expect(output).to.have.property('timeouts', 0); + server.kill('SIGKILL'); + if (client.connected) { + client.disconnect(); + } - client.on('message', function (output) { - expect(output).to.have.property('remaining', 0); - expect(output).to.have.property('timeouts', 0); - server.kill('SIGKILL'); - if (client.connected) { - client.disconnect(); - } - - timeout = setTimeout(function () { - client.removeListener('exit'); - done(new Error('process should have closed by now')); - }, 2000); - }); - - client.on('exit', function () { - clearTimeout(timeout); - done(); - }); + timeout = setTimeout(function () { + client.removeListener('exit'); + done(new Error('process should have closed by now')); + }, 2000); + }) + .on('exit', function () { + console.log('client closed'); + clearTimeout(timeout); + done(); + }); }); it('properly removes all elements from the socket', function () {