Skip to content

Commit

Permalink
[fix] Allow URL instances as URL in WebSocket constructor
Browse files Browse the repository at this point in the history
Fixes #1143
  • Loading branch information
3rd-Eden authored and lpinca committed Mar 16, 2018
1 parent 57d3dbb commit 7d2699b
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 19 deletions.
2 changes: 1 addition & 1 deletion doc/ws.md
Expand Up @@ -183,7 +183,7 @@ This class represents a WebSocket. It extends the `EventEmitter`.

### new WebSocket(address[, protocols][, options])

- `address` {String} The URL to which to connect.
- `address` {String|url.Url|url.URL} The URL to which to connect.
- `protocols` {String|Array} The list of subprotocols.
- `options` {Object}
- `protocol` {String} Value of the `Sec-WebSocket-Protocol` header.
Expand Down
39 changes: 22 additions & 17 deletions lib/websocket.js
Expand Up @@ -27,7 +27,7 @@ class WebSocket extends EventEmitter {
/**
* Create a new `WebSocket`.
*
* @param {String} address The URL to which to connect
* @param {(String|url.Url|url.URL)} address The URL to which to connect
* @param {(String|String[])} protocols The subprotocols
* @param {Object} options Connection options
*/
Expand Down Expand Up @@ -404,7 +404,7 @@ module.exports = WebSocket;
/**
* Initialize a WebSocket client.
*
* @param {String} address The URL to which to connect
* @param {(String|url.Url|url.URL)} address The URL to which to connect
* @param {String[]} protocols The list of subprotocols
* @param {Object} options Connection options
* @param {String} options.protocol Value of the `Sec-WebSocket-Protocol` header
Expand Down Expand Up @@ -463,24 +463,35 @@ function initAsClient (address, protocols, options) {
}

this._isServer = false;
this.url = address;

const serverUrl = url.parse(address);
var serverUrl;

if (typeof address === 'object' && address.href !== undefined) {
serverUrl = address;
this.url = address.href;
} else {
serverUrl = url.parse(address);
this.url = address;
}

const isUnixSocket = serverUrl.protocol === 'ws+unix:';

if (!serverUrl.host && (!isUnixSocket || !serverUrl.path)) {
throw new Error(`Invalid URL: ${address}`);
if (!serverUrl.host && (!isUnixSocket || !serverUrl.pathname)) {
throw new Error(`Invalid URL: ${this.url}`);
}

const isSecure = serverUrl.protocol === 'wss:' || serverUrl.protocol === 'https:';
const key = crypto.randomBytes(16).toString('base64');
const httpObj = isSecure ? https : http;
const path = serverUrl.search
? `${serverUrl.pathname || '/'}${serverUrl.search}`
: serverUrl.pathname || '/';
var perMessageDeflate;

const requestOptions = {
port: serverUrl.port || (isSecure ? 443 : 80),
host: serverUrl.hostname,
path: '/',
path: path,
headers: {
'Sec-WebSocket-Version': options.protocolVersion,
'Sec-WebSocket-Key': key,
Expand Down Expand Up @@ -511,24 +522,18 @@ function initAsClient (address, protocols, options) {
}
if (options.host) requestOptions.headers.Host = options.host;
if (serverUrl.auth) requestOptions.auth = serverUrl.auth;
else if (serverUrl.username || serverUrl.password) {
requestOptions.auth = `${serverUrl.username}:${serverUrl.password}`;
}

if (options.localAddress) requestOptions.localAddress = options.localAddress;
if (options.family) requestOptions.family = options.family;

if (isUnixSocket) {
const parts = serverUrl.path.split(':');
const parts = path.split(':');

requestOptions.socketPath = parts[0];
requestOptions.path = parts[1];
} else if (serverUrl.path) {
//
// Make sure that path starts with `/`.
//
if (serverUrl.path.charAt(0) !== '/') {
requestOptions.path = `/${serverUrl.path}`;
} else {
requestOptions.path = serverUrl.path;
}
}

var agent = options.agent;
Expand Down
46 changes: 45 additions & 1 deletion test/websocket.test.js
Expand Up @@ -7,6 +7,7 @@ const crypto = require('crypto');
const https = require('https');
const http = require('http');
const dns = require('dns');
const url = require('url');
const fs = require('fs');
const os = require('os');

Expand All @@ -26,6 +27,30 @@ describe('WebSocket', function () {
);
});

it('accepts `url.Url` objects as url', function (done) {
const agent = new CustomAgent();

agent.addRequest = (req) => {
assert.strictEqual(req.path, '/');
done();
};

const ws = new WebSocket(url.parse('ws://localhost'), { agent });
});

it('accepts `url.URL` objects as url', function (done) {
if (!url.URL) return this.skip();

const agent = new CustomAgent();

agent.addRequest = (req) => {
assert.strictEqual(req.path, '/');
done();
};

const ws = new WebSocket(new url.URL('ws://localhost'), { agent });
});

describe('options', function () {
it('accepts an `agent` option', function (done) {
const agent = new CustomAgent();
Expand Down Expand Up @@ -1781,7 +1806,7 @@ describe('WebSocket', function () {
});

describe('Request headers', function () {
it('adds the authorization header if userinfo is present', function (done) {
it('adds the authorization header if the url has userinfo (1/2)', function (done) {
const agent = new CustomAgent();
const auth = 'test:testpass';

Expand All @@ -1796,6 +1821,25 @@ describe('WebSocket', function () {
const ws = new WebSocket(`ws://${auth}@localhost`, { agent });
});

it('adds the authorization header if the url has userinfo (2/2)', function (done) {
if (!url.URL) return this.skip();

const agent = new CustomAgent();
const auth = 'test:testpass';

agent.addRequest = (req) => {
assert.strictEqual(
req._headers.authorization,
`Basic ${Buffer.from(auth).toString('base64')}`
);
done();
};

const ws = new WebSocket(new url.URL(`ws://${auth}@localhost`), {
agent
});
});

it('adds custom headers', function (done) {
const agent = new CustomAgent();

Expand Down

0 comments on commit 7d2699b

Please sign in to comment.