Skip to content

Commit

Permalink
Merge pull request #379 from tomas/default-agent
Browse files Browse the repository at this point in the history
Allow setting/using a default agent for all requests via defaults()
  • Loading branch information
tomas authored Apr 7, 2022
2 parents 9daa93f + 4fff222 commit 33e834f
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 30 deletions.
16 changes: 10 additions & 6 deletions lib/needle.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ var version = require('../package.json').version;
var user_agent = 'Needle/' + version;
user_agent += ' (Node.js ' + process.version + '; ' + process.platform + ' ' + process.arch + ')';

var tls_options = 'agent pfx key passphrase cert ca ciphers rejectUnauthorized secureProtocol checkServerIdentity family';
var tls_options = 'pfx key passphrase cert ca ciphers rejectUnauthorized secureProtocol checkServerIdentity family';

// older versions of node (< 0.11.4) prevent the runtime from exiting
// because of connections in keep-alive state. so if this is the case
Expand Down Expand Up @@ -83,7 +83,8 @@ var defaults = {
parse_response : 'all', // same as true. valid options: 'json', 'xml' or false/null
proxy : null,

// headers
// agent & headers
agent : null,
headers : {},
accept : '*/*',
user_agent : user_agent,
Expand Down Expand Up @@ -274,6 +275,7 @@ Needle.prototype.setup = function(uri, options) {

var config = {
http_opts : {
agent: get_option('agent', defaults.agent),
localAddress: get_option('localAddress', undefined),
lookup: get_option('lookup', undefined)
}, // passed later to http.request() directly
Expand All @@ -295,9 +297,11 @@ Needle.prototype.setup = function(uri, options) {
// populate http_opts with given TLS options
tls_options.split(' ').forEach(function(key) {
if (typeof options[key] != 'undefined') {
config.http_opts[key] = options[key];
if (typeof options.agent == 'undefined')
config.http_opts.agent = false; // otherwise tls options are skipped
if (config.http_opts.agent) { // pass option to existing agent
config.http_opts.agent.options[key] = options[key];
} else {
config.http_opts[key] = options[key];
}
}
});

Expand Down Expand Up @@ -902,7 +906,7 @@ module.exports.defaults = function(obj) {
var target_key = aliased.options[key] || key;

if (defaults.hasOwnProperty(target_key) && typeof obj[key] != 'undefined') {
if (target_key != 'parse_response' && target_key != 'proxy') {
if (target_key != 'parse_response' && target_key != 'proxy' && target_key != 'agent') {
// ensure type matches the original, except for proxy/parse_response that can be null/bool or string
var valid_type = defaults[target_key].constructor.name;

Expand Down
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

135 changes: 121 additions & 14 deletions test/headers_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ var http = require('http'),

var port = 54321;


describe('request headers', function() {

var needle,
Expand Down Expand Up @@ -38,6 +39,7 @@ describe('request headers', function() {

describe('old node versions (<0.11.4) with persistent keep-alive connections', function() {

// emulate old node behaviour
before(function() {
delete require.cache[require.resolve('..')] // in case it was already loaded
original_defaultMaxSockets = http.Agent.defaultMaxSockets;
Expand Down Expand Up @@ -133,23 +135,47 @@ describe('request headers', function() {

describe('default options', function() {

// TODO:
// this is weird. by default, new node versions set a 'close' header
// while older versions set a keep-alive header
var node_major_ver = process.version.split('.')[0].replace('v', '');

it.skip('sets a Connection header', function(done) {
send_request({}, function(err, resp) {
// should.not.exist(resp.body.headers['connection']);
// done();
if (parseInt(node_major_ver) >= 4) {

it('sets Connection header to close (> v4)', function(done) {
send_request({}, function(err, resp) {
resp.body.headers['connection'].should.eql('close');
done()
})
})
})

it.skip('one open sockets remain after request', function(done) {
send_request({}, function(err, resp) {
// get_active_sockets().length.should.eql(1);
// done();
});
})
} else {

it('sets Connection header to keep-alive (< v4)', function(done) {
send_request({}, function(err, resp) {
resp.body.headers['connection'].should.eql('keep-alive');
done();
})
})

}

if (parseInt(node_major_ver) >= 8 || parseInt(node_major_ver) == 0) {

it('one open socket remains after request (> v8 && v0.10)', function(done) {
send_request({}, function(err, resp) {
get_active_sockets().length.should.eql(existing_sockets + 1);
done();
});
})

} else {

it('no open sockets remain after request (> v0.10 && < v8)', function(done) {
send_request({}, function(err, resp) {
get_active_sockets().length.should.eql(existing_sockets);
done();
});
})

}

})

Expand Down Expand Up @@ -200,4 +226,85 @@ describe('request headers', function() {

})

describe('using shared keep-alive agent', function() {

before(function() {
needle.defaults({ agent: http.Agent({ keepAlive: true }) })
})

after(function() {
needle.defaults().agent.destroy(); // close existing connections
needle.defaults({ agent: null }); // and reset default value
})

describe('default options', function() {

it('sends a Connection: keep-alive header', function(done) {
send_request({}, function(err, resp) {
resp.body.headers['connection'].should.eql('keep-alive');
done();
})
})

it('one open socket remain after request', function(done) {
send_request({ connection: 'keep-alive' }, function(err, resp) {
setTimeout(function() {
get_active_sockets().length.should.eql(existing_sockets + 1);
done();
}, 10);
});
})

})

describe('passing connection: close', function() {

it('sends a Connection: close header', function(done) {
send_request({ connection: 'close' }, function(err, resp) {
resp.body.headers['connection'].should.eql('close');
done();
})
})

it('no open sockets remain after request', function(done) {
send_request({ connection: 'close' }, function(err, resp) {
setTimeout(function() {
get_active_sockets().length.should.eql(existing_sockets);
done();
}, 10)
});
})

})

describe('passing connection: keep-alive', function() {

it('sends a Connection: keep-alive header (using options.headers.connection)', function(done) {
send_request({ headers: { connection: 'keep-alive' }}, function(err, resp) {
resp.body.headers['connection'].should.eql('keep-alive');
done();
})
})

it('sends a Connection: keep-alive header (using options.connection)', function(done) {
send_request({ connection: 'keep-alive' }, function(err, resp) {
resp.body.headers['connection'].should.eql('keep-alive');
done();
})
})

it('one open socket remain after request', function(done) {
send_request({ connection: 'keep-alive' }, function(err, resp) {
setTimeout(function() {
get_active_sockets().length.should.eql(existing_sockets + 1);
done();
}, 10);
})
})

})


})

})
7 changes: 7 additions & 0 deletions test/output_spec.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
// this lets us run tests in ancient node versions (v0.10.x)
if (process.version.split('.')[0] == 'v0' && !Buffer.from) {
Buffer.from = function(args) {
return new Buffer(args);
}
}

var should = require('should'),
needle = require('./../'),
http = require('http'),
Expand Down
2 changes: 1 addition & 1 deletion test/redirect_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ describe('redirects', function() {
url = protocol + '://' + host + ':' + ports[protocol] + '/hello';

function send_request(opts, cb) {
opts.rejectUnauthorized = false;
if (protocol == 'https') opts.rejectUnauthorized = false;
// console.log(' -- sending request ' + url + ' -- redirect to ' + location);
needle.post(url, { foo: 'bar' }, opts, cb);
}
Expand Down
57 changes: 57 additions & 0 deletions test/tls_options_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
var needle = require('..'),
https = require('https'),
helpers = require('./helpers'),
should = require('should');

describe('tls options', function() {

describe('rejectUnauthorized: false', function() {

var url = 'https://expired-rsa-dv.ssl.com/';

it('is an expired cert', function(done) {
needle.get(url, function(err, resp) {
err.code.should.eql('CERT_HAS_EXPIRED')
should.not.exist(resp)
done()
})
})

it('allows fetching pages under expired certificates', function(done) {
needle.get(url, { rejectUnauthorized: false }, function(err, resp) {
should.not.exist(err);
resp.statusCode.should.eql(200);
done()
})
})

it('also works when using custom agent', function(done) {
var agent = new https.Agent({ rejectUnauthorized: true })

// should overwrite value from custom agent
needle.get(url, { rejectUnauthorized: false }, function(err, resp) {
should.not.exist(err);
resp.statusCode.should.eql(200);
done()
})

})

it('also works with shared/default agent', function(done) {
var agent = new https.Agent({ rejectUnauthorized: true })
needle.defaults({ agent: agent })

// should overwrite value from custom agent
needle.get(url, { rejectUnauthorized: false }, function(err, resp) {
should.not.exist(err);
resp.statusCode.should.eql(200);

needle.defaults({ agent: null })
done()
})

})

})

})

0 comments on commit 33e834f

Please sign in to comment.