diff --git a/lib/asset.js b/lib/asset.js index f778f10..79b6c62 100644 --- a/lib/asset.js +++ b/lib/asset.js @@ -4,6 +4,7 @@ var co = require('co'), request = require('./request'), + url = require('./url'), validation = require('./validation'); const @@ -41,6 +42,13 @@ module.exports = function (assetOptions, ensureAuthHeaders, self) { // apply additional optional settings if supplied settings = validation.applyOptionalParameters(assetOptions, settings); + const result = url.parseURL(settings.host); + if (!validation.isEmpty(result)) { + settings.host = result.host; + settings.port = result.port; + settings.secure = result.secure; + } + // ensure request is setup req = new request.Request(settings); req.on(EVENT_REQUEST, (data) => (self.emit(EVENT_REQUEST, data))); diff --git a/lib/campaign.js b/lib/campaign.js index 74ebb29..b5718a7 100644 --- a/lib/campaign.js +++ b/lib/campaign.js @@ -4,6 +4,7 @@ var co = require('co'), fs = require('fs'), request = require('./request'), + url = require('./url'), validation = require('./validation'); const @@ -40,6 +41,13 @@ module.exports = function (campaignOptions, ensureAuthHeaders, self) { // apply additional optional settings if supplied settings = validation.applyOptionalParameters(campaignOptions, settings); + const result = url.parseURL(settings.host); + if (!validation.isEmpty(result)) { + settings.host = result.host; + settings.port = result.port; + settings.secure = result.secure; + } + // ensure request is setup req = new request.Request(settings); req.on(EVENT_REQUEST, (data) => (self.emit(EVENT_REQUEST, data))); diff --git a/lib/claim.js b/lib/claim.js index ad18303..966bbc5 100644 --- a/lib/claim.js +++ b/lib/claim.js @@ -4,6 +4,7 @@ var co = require('co'), request = require('./request'), + url = require('./url'), validation = require('./validation'); const @@ -39,6 +40,13 @@ module.exports = function (claimOptions, ensureAuthHeaders, self) { // apply additional optional settings if supplied settings = validation.applyOptionalParameters(claimOptions, settings); + const result = url.parseURL(settings.host); + if (!validation.isEmpty(result)) { + settings.host = result.host; + settings.port = result.port; + settings.secure = result.secure; + } + // ensure request is setup req = new request.Request(settings); req.on(EVENT_REQUEST, (data) => (self.emit(EVENT_REQUEST, data))); diff --git a/lib/content.js b/lib/content.js index 303f8d5..ce5e8a3 100644 --- a/lib/content.js +++ b/lib/content.js @@ -5,6 +5,7 @@ var co = require('co'), request = require('./request'), + url = require('./url'), validation = require('./validation'); const @@ -45,6 +46,13 @@ module.exports = function (contentOptions, ensureAuthHeaders, self) { // apply additional optional settings if supplied settings = validation.applyOptionalParameters(contentOptions, settings); + const result = url.parseURL(settings.host); + if (!validation.isEmpty(result)) { + settings.host = result.host; + settings.port = result.port; + settings.secure = result.secure; + } + // ensure request is setup req = new request.Request(settings); req.on(EVENT_REDIRECT, (data) => (self.emit(EVENT_REDIRECT, data))); diff --git a/lib/device.js b/lib/device.js index f65e60c..9ba9d47 100644 --- a/lib/device.js +++ b/lib/device.js @@ -4,6 +4,7 @@ var co = require('co'), request = require('./request'), + url = require('./url'), validation = require('./validation'); const @@ -41,6 +42,13 @@ module.exports = function (deviceOptions, ensureAuthHeaders, self) { // apply additional optional settings if supplied settings = validation.applyOptionalParameters(deviceOptions, settings); + const result = url.parseURL(settings.host); + if (!validation.isEmpty(result)) { + settings.host = result.host; + settings.port = result.port; + settings.secure = result.secure; + } + // ensure request is setup req = new request.Request(settings); req.on(EVENT_REQUEST, (data) => (self.emit(EVENT_REQUEST, data))); diff --git a/lib/key.js b/lib/key.js index e74a49a..83277b3 100644 --- a/lib/key.js +++ b/lib/key.js @@ -6,6 +6,7 @@ var co = require('co'), request = require('./request'), + url = require('./url'), validation = require('./validation'); const @@ -58,6 +59,13 @@ module.exports = function (keyOptions, clientId, secret, self) { // apply additional optional settings if supplied settings = validation.applyOptionalParameters(keyOptions, settings); + const result = url.parseURL(settings.host); + if (!validation.isEmpty(result)) { + settings.host = result.host; + settings.port = result.port; + settings.secure = result.secure; + } + // ensure request is setup req = new request.Request(settings); req.on(EVENT_REQUEST, (data) => (self.emit(EVENT_REQUEST, data))); diff --git a/lib/location.js b/lib/location.js index 1754f49..99ee613 100644 --- a/lib/location.js +++ b/lib/location.js @@ -4,6 +4,7 @@ var co = require('co'), request = require('./request'), + url = require('./url'), validation = require('./validation'); const @@ -39,6 +40,13 @@ module.exports = function (locationOptions, ensureAuthHeaders, self) { // apply additional optional settings if supplied settings = validation.applyOptionalParameters(locationOptions, settings); + const result = url.parseURL(settings.host); + if (!validation.isEmpty(result)) { + settings.host = result.host; + settings.port = result.port; + settings.secure = result.secure; + } + // ensure request is setup req = new request.Request(settings); req.on(EVENT_REQUEST, (data) => (self.emit(EVENT_REQUEST, data))); diff --git a/lib/music.js b/lib/music.js index f16b456..8231534 100644 --- a/lib/music.js +++ b/lib/music.js @@ -4,6 +4,7 @@ var co = require('co'), request = require('./request'), + url = require('./url'), validation = require('./validation'); const @@ -43,6 +44,13 @@ module.exports = function (musicOptions, ensureAuthHeaders, self) { // apply additional optional settings if supplied settings = validation.applyOptionalParameters(musicOptions, settings); + const result = url.parseURL(settings.host); + if (!validation.isEmpty(result)) { + settings.host = result.host; + settings.port = result.port; + settings.secure = result.secure; + } + // ensure request is setup req = new request.Request(settings); req.on(EVENT_REQUEST, (data) => (self.emit(EVENT_REQUEST, data))); diff --git a/lib/playback.js b/lib/playback.js index 483397f..4adfdff 100644 --- a/lib/playback.js +++ b/lib/playback.js @@ -4,6 +4,7 @@ var co = require('co'), request = require('./request'), + url = require('./url'), validation = require('./validation'); const @@ -39,6 +40,13 @@ module.exports = function (playbackOptions, ensureAuthHeaders, self) { // apply additional optional settings if supplied settings = validation.applyOptionalParameters(playbackOptions, settings); + const result = url.parseURL(settings.host); + if (!validation.isEmpty(result)) { + settings.host = result.host; + settings.port = result.port; + settings.secure = result.secure; + } + // ensure request is setup req = new request.Request(settings); req.on(EVENT_REQUEST, (data) => (self.emit(EVENT_REQUEST, data))); diff --git a/lib/player.js b/lib/player.js index b06e0c6..1164f13 100644 --- a/lib/player.js +++ b/lib/player.js @@ -2,6 +2,7 @@ var co = require('co'), events = require('events'), io = require('socket.io-client'), + url = require('./url'), validation = require('./validation'); let @@ -46,6 +47,13 @@ module.exports = function (playerOptions, ensureAuthHeaders, self) { // apply additional optional settings if supplied settings = validation.applyOptionalParameters(playerOptions, settings); + const result = url.parseURL(settings.host); + if (!validation.isEmpty(result)) { + settings.host = result.host; + settings.port = result.port; + settings.secure = result.secure; + } + self.settings = () => (settings); self.connect = (options, socketEventSubscriber) => co(function *() { diff --git a/lib/provision.js b/lib/provision.js index e351224..4a30597 100644 --- a/lib/provision.js +++ b/lib/provision.js @@ -4,6 +4,7 @@ var co = require('co'), request = require('./request'), + url = require('./url'), validation = require('./validation'); const @@ -42,6 +43,13 @@ module.exports = function (provisionOptions, ensureAuthHeaders, self) { // apply additional optional settings if supplied settings = validation.applyOptionalParameters(provisionOptions, settings); + const result = url.parseURL(settings.host); + if (!validation.isEmpty(result)) { + settings.host = result.host; + settings.port = result.port; + settings.secure = result.secure; + } + // ensure request is setup req = new request.Request(settings); req.on(EVENT_REQUEST, (data) => (self.emit(EVENT_REQUEST, data))); diff --git a/lib/relationship.js b/lib/relationship.js index 752d458..1979f7c 100644 --- a/lib/relationship.js +++ b/lib/relationship.js @@ -4,6 +4,7 @@ var co = require('co'), request = require('./request'), + url = require('./url'), validation = require('./validation'); const @@ -39,6 +40,13 @@ module.exports = function (relationshipsOptions, ensureAuthHeaders, self) { // apply additional optional settings if supplied settings = validation.applyOptionalParameters(relationshipsOptions, settings); + const result = url.parseURL(settings.host); + if (!validation.isEmpty(result)) { + settings.host = result.host; + settings.port = result.port; + settings.secure = result.secure; + } + // ensure request is setup req = new request.Request(settings); req.on(EVENT_REQUEST, (data) => (self.emit(EVENT_REQUEST, data))); diff --git a/lib/settings.js b/lib/settings.js index 2358177..79bf110 100644 --- a/lib/settings.js +++ b/lib/settings.js @@ -4,6 +4,7 @@ var co = require('co'), request = require('./request'), + url = require('./url'), validation = require('./validation'); const @@ -39,6 +40,13 @@ module.exports = function (settingsOptions, ensureAuthHeaders, self) { // apply additional optional settings if supplied settings = validation.applyOptionalParameters(settingsOptions, settings); + const result = url.parseURL(settings.host); + if (!validation.isEmpty(result)) { + settings.host = result.host; + settings.port = result.port; + settings.secure = result.secure; + } + // ensure request is setup req = new request.Request(settings); req.on(EVENT_REQUEST, (data) => (self.emit(EVENT_REQUEST, data))); diff --git a/lib/url.js b/lib/url.js new file mode 100644 index 0000000..5c608e1 --- /dev/null +++ b/lib/url.js @@ -0,0 +1,49 @@ +const { URL } = require('url'); + +const PROTOCOL_HTTP = 'http:'; +const PROTOCOL_HTTPS = 'https:'; + +/** + * Parse the input (URL string) to get its hostname, port, and protocol. + * In the case that it's not parsable and it throws an error, return an empty object. + * We do this to preserve backward compatibility. + * Note: this method assumes you are using either HTTP or HTTPS protocols. It assumes HTTPS if protocol isn't provided. + * @param {String} input The URL. + * @param {Object} output An object containing host, port, and secure properties. + * @returns {Object} The object containing valid host, port, and secure keys based on the input. + */ +const parseURL = (input) => { + const output = {}; + + if (typeof(input) !== 'string') { + return output; + } + + input = input.trim(); + if (input === '') { + return output; + } + + if (!input.startsWith(PROTOCOL_HTTP) && !input.startsWith(PROTOCOL_HTTPS)) { + input = PROTOCOL_HTTPS + input; + } + + let url; + try { + url = new URL(input); + } catch (e) { + return output; + } + + const { hostname, port, protocol } = url; + + output.host = hostname; + output.port = port; + output.secure = protocol === PROTOCOL_HTTPS; + + return output; +}; + +module.exports = { + parseURL +}; diff --git a/test/lib/url.js b/test/lib/url.js new file mode 100644 index 0000000..91ceefb --- /dev/null +++ b/test/lib/url.js @@ -0,0 +1,64 @@ +/* eslint no-unused-expressions: 0 */ + +const url = require('../../lib/url'); + +describe('validation', () => { + describe('#parseURL', function() { + it('should return an object with host, port, and secure keys', function() { + let result = url.parseURL('http://localhost:3000'); + result.host.should.equal('localhost'); + result.port.should.equal('3000'); + result.secure.should.be.false; + + result = url.parseURL('https://localhost'); + result.host.should.equal('localhost'); + result.port.should.equal(''); + result.secure.should.be.true; + + result = url.parseURL('https://example.com:12345'); + result.host.should.equal('example.com'); + result.port.should.equal('12345'); + result.secure.should.be.true; + + result = url.parseURL(''); + result.should.deep.equal({}); + + result = url.parseURL(null); + result.should.deep.equal({}); + }); + + it('should return an empty object on invalid input', function() { + let result = url.parseURL(''); + result.should.deep.equal({}); + + result = url.parseURL(' '); + result.should.deep.equal({}); + + result = url.parseURL('this is not a url'); + result.should.deep.equal({}); + + result = url.parseURL(':8080'); + result.should.deep.equal({}); + + result = url.parseURL(null); + result.should.deep.equal({}); + }); + + it('assumes secure protocol if it is not provided in the url', function() { + let result = url.parseURL('example.com:12345'); + result.host.should.equal('example.com'); + result.port.should.equal('12345'); + result.secure.should.be.true; + + result = url.parseURL('localhost'); + result.host.should.equal('localhost'); + result.port.should.equal(''); + result.secure.should.be.true; + + result = url.parseURL('127.0.0.1'); + result.host.should.equal('127.0.0.1'); + result.port.should.equal(''); + result.secure.should.be.true; + }); + }); +});