diff --git a/doc/api/http.md b/doc/api/http.md index e83c507bf2ee0a..9240b40fa8598b 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -1794,15 +1794,20 @@ The `requestListener` is a function which is automatically added to the [`'request'`][] event. ## http.get(options[, callback]) +## http.get(url[, options][, callback]) -* `options` {Object | string | URL} Accepts the same `options` as +* `url` {string | URL} +* `options` {Object} Accepts the same `options` as [`http.request()`][], with the `method` always set to `GET`. Properties that are inherited from the prototype are ignored. * `callback` {Function} @@ -1866,15 +1871,20 @@ Global instance of `Agent` which is used as the default for all HTTP client requests. ## http.request(options[, callback]) +## http.request(url[, options][, callback]) -* `options` {Object | string | URL} +* `url` {string | URL} +* `options` {Object} * `protocol` {string} Protocol to use. **Default:** `'http:'`. * `host` {string} A domain name or IP address of the server to issue the request to. **Default:** `'localhost'`. @@ -1916,10 +1926,13 @@ changes: Node.js maintains several connections per server to make HTTP requests. This function allows one to transparently issue requests. -`options` can be an object, a string, or a [`URL`][] object. If `options` is a +`url` can be a string or a [`URL`][] object. If `url` is a string, it is automatically parsed with [`url.parse()`][]. If it is a [`URL`][] object, it will be automatically converted to an ordinary `options` object. +If both `url` and `options` are specified, the objects are merged, with the +`options` properties taking precedence. + The optional `callback` parameter will be added as a one-time listener for the [`'response'`][] event. diff --git a/doc/api/https.md b/doc/api/https.md index 160967d1eb60bf..fdae60c13a7c18 100644 --- a/doc/api/https.md +++ b/doc/api/https.md @@ -112,14 +112,19 @@ https.createServer(options, (req, res) => { ``` ## https.get(options[, callback]) +## https.get(url[, options][, callback]) -- `options` {Object | string | URL} Accepts the same `options` as +- `url` {string | URL} +- `options` {Object} Accepts the same `options` as [`https.request()`][], with the `method` always set to `GET`. - `callback` {Function} @@ -155,9 +160,13 @@ added: v0.5.9 Global instance of [`https.Agent`][] for all HTTPS client requests. ## https.request(options[, callback]) +## https.request(url[, options][, callback]) -- `options` {Object | string | URL} Accepts all `options` from +- `url` {string | URL} +- `options` {Object} Accepts all `options` from [`http.request()`][], with some differences in default values: - `protocol` **Default:** `'https:'` - `port` **Default:** `443` diff --git a/lib/_http_client.js b/lib/_http_client.js index 0612f5822a303f..462245174ed6f6 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -60,22 +60,31 @@ function validateHost(host, name) { return host; } -function ClientRequest(options, cb) { +function ClientRequest(input, options, cb) { OutgoingMessage.call(this); - if (typeof options === 'string') { - options = url.parse(options); - if (!options.hostname) { + if (typeof input === 'string') { + input = url.parse(input); + if (!input.hostname) { throw new ERR_INVALID_DOMAIN_NAME(); } - } else if (options && options[searchParamsSymbol] && - options[searchParamsSymbol][searchParamsSymbol]) { + } else if (input && input[searchParamsSymbol] && + input[searchParamsSymbol][searchParamsSymbol]) { // url.URL instance - options = urlToOptions(options); + input = urlToOptions(input); } else { - options = util._extend({}, options); + cb = options; + options = input; + input = null; } + if (typeof options === 'function') { + cb = options; + options = null; + } + + options = util._extend(input || {}, options || {}); + var agent = options.agent; var defaultAgent = options._defaultAgent || Agent.globalAgent; if (agent === false) { diff --git a/lib/http.js b/lib/http.js index 9ed6b3d1de8721..660de78650f45c 100644 --- a/lib/http.js +++ b/lib/http.js @@ -37,12 +37,12 @@ function createServer(opts, requestListener) { return new Server(opts, requestListener); } -function request(options, cb) { - return new ClientRequest(options, cb); +function request(url, options, cb) { + return new ClientRequest(url, options, cb); } -function get(options, cb) { - var req = request(options, cb); +function get(url, options, cb) { + var req = request(url, options, cb); req.end(); return req; } diff --git a/lib/https.js b/lib/https.js index 195a33fb0de4bc..15970c182ea56f 100644 --- a/lib/https.js +++ b/lib/https.js @@ -255,25 +255,33 @@ Agent.prototype._evictSession = function _evictSession(key) { const globalAgent = new Agent(); -function request(options, cb) { - if (typeof options === 'string') { - options = url.parse(options); +function request(...args) { + let options = {}; + + if (typeof args[0] === 'string') { + const urlStr = args.shift(); + options = url.parse(urlStr); if (!options.hostname) { throw new ERR_INVALID_DOMAIN_NAME(); } - } else if (options && options[searchParamsSymbol] && - options[searchParamsSymbol][searchParamsSymbol]) { + } else if (args[0] && args[0][searchParamsSymbol] && + args[0][searchParamsSymbol][searchParamsSymbol]) { // url.URL instance - options = urlToOptions(options); - } else { - options = util._extend({}, options); + options = urlToOptions(args.shift()); + } + + if (args[0] && typeof args[0] !== 'function') { + options = util._extend(options, args.shift()); } + options._defaultAgent = globalAgent; - return new ClientRequest(options, cb); + args.unshift(options); + + return new ClientRequest(...args); } -function get(options, cb) { - const req = request(options, cb); +function get(input, options, cb) { + const req = request(input, options, cb); req.end(); return req; } diff --git a/test/parallel/test-http-request-arguments.js b/test/parallel/test-http-request-arguments.js new file mode 100644 index 00000000000000..5cdd514fd50685 --- /dev/null +++ b/test/parallel/test-http-request-arguments.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +// Test providing both a url and options, with the options partially +// replacing address and port portions of the URL provided. +{ + const server = http.createServer( + common.mustCall((req, res) => { + assert.strictEqual(req.url, '/testpath'); + res.end(); + server.close(); + }) + ); + server.listen( + 0, + common.mustCall(() => { + http.get( + 'http://example.com/testpath', + { hostname: 'localhost', port: server.address().port }, + common.mustCall((res) => { + res.resume(); + }) + ); + }) + ); +} diff --git a/test/parallel/test-https-request-arguments.js b/test/parallel/test-https-request-arguments.js new file mode 100644 index 00000000000000..44037ddd6de90b --- /dev/null +++ b/test/parallel/test-https-request-arguments.js @@ -0,0 +1,46 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const https = require('https'); +const fixtures = require('../common/fixtures'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const options = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem'), + ca: fixtures.readKey('ca1-cert.pem') +}; + +// Test providing both a url and options, with the options partially +// replacing address and port portions of the URL provided. +{ + const server = https.createServer( + options, + common.mustCall((req, res) => { + assert.strictEqual(req.url, '/testpath'); + res.end(); + server.close(); + }) + ); + + server.listen( + 0, + common.mustCall(() => { + https.get( + 'https://example.com/testpath', + + { + hostname: 'localhost', + port: server.address().port, + rejectUnauthorized: false + }, + + common.mustCall((res) => { + res.resume(); + }) + ); + }) + ); +}