Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not throw if the redirect URL is invalid #1980

Merged
merged 1 commit into from Nov 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
50 changes: 38 additions & 12 deletions lib/websocket.js
Expand Up @@ -630,19 +630,26 @@ function initAsClient(websocket, address, protocols, options) {

const isSecure = parsedUrl.protocol === 'wss:';
const isUnixSocket = parsedUrl.protocol === 'ws+unix:';
let invalidURLMessage;

if (parsedUrl.protocol !== 'ws:' && !isSecure && !isUnixSocket) {
throw new SyntaxError(
'The URL\'s protocol must be one of "ws:", "wss:", or "ws+unix:"'
);
invalidURLMessage =
'The URL\'s protocol must be one of "ws:", "wss:", or "ws+unix:"';
} else if (isUnixSocket && !parsedUrl.pathname) {
invalidURLMessage = "The URL's pathname is empty";
} else if (parsedUrl.hash) {
invalidURLMessage = 'The URL contains a fragment identifier';
}

if (isUnixSocket && !parsedUrl.pathname) {
throw new SyntaxError("The URL's pathname is empty");
}
if (invalidURLMessage) {
const err = new SyntaxError(invalidURLMessage);

if (parsedUrl.hash) {
throw new SyntaxError('The URL contains a fragment identifier');
if (websocket._redirects === 0) {
throw err;
} else {
emitErrorAndClose(websocket, err);
return;
}
}

const defaultPort = isSecure ? 443 : 80;
Expand Down Expand Up @@ -724,9 +731,7 @@ function initAsClient(websocket, address, protocols, options) {
if (req === null || req.aborted) return;

req = websocket._req = null;
websocket._readyState = WebSocket.CLOSING;
websocket.emit('error', err);
websocket.emitClose();
emitErrorAndClose(websocket, err);
});

req.on('response', (res) => {
Expand All @@ -746,7 +751,15 @@ function initAsClient(websocket, address, protocols, options) {

req.abort();

const addr = new URL(location, address);
let addr;

try {
addr = new URL(location, address);
} catch (e) {
const err = new SyntaxError(`Invalid URL: ${location}`);
emitErrorAndClose(websocket, err);
return;
}

initAsClient(websocket, addr, protocols, options);
} else if (!websocket.emit('unexpected-response', req, res)) {
Expand Down Expand Up @@ -849,6 +862,19 @@ function initAsClient(websocket, address, protocols, options) {
});
}

/**
* Emit the `'error'` and `'close'` event.
*
* @param {WebSocket} websocket The WebSocket instance
* @param {Error} The error to emit
* @private
*/
function emitErrorAndClose(websocket, err) {
websocket._readyState = WebSocket.CLOSING;
websocket.emit('error', err);
websocket.emitClose();
}

/**
* Create a `net.Socket` and initiate a connection.
*
Expand Down
47 changes: 47 additions & 0 deletions test/websocket.test.js
Expand Up @@ -1028,6 +1028,53 @@ describe('WebSocket', () => {
ws.on('close', () => done());
});
});

it('emits an error if the redirect URL is invalid (1/2)', (done) => {
const onUpgrade = (req, socket) => {
socket.end('HTTP/1.1 302 Found\r\nLocation: ws://\r\n\r\n');
};

server.on('upgrade', onUpgrade);

const ws = new WebSocket(`ws://localhost:${server.address().port}`, {
followRedirects: true
});

ws.on('open', () => done(new Error("Unexpected 'open' event")));
ws.on('error', (err) => {
assert.ok(err instanceof SyntaxError);
assert.strictEqual(err.message, 'Invalid URL: ws://');
assert.strictEqual(ws._redirects, 1);

server.removeListener('upgrade', onUpgrade);
ws.on('close', () => done());
});
});

it('emits an error if the redirect URL is invalid (2/2)', (done) => {
const onUpgrade = (req, socket) => {
socket.end('HTTP/1.1 302 Found\r\nLocation: http://localhost\r\n\r\n');
};

server.on('upgrade', onUpgrade);

const ws = new WebSocket(`ws://localhost:${server.address().port}`, {
followRedirects: true
});

ws.on('open', () => done(new Error("Unexpected 'open' event")));
ws.on('error', (err) => {
assert.ok(err instanceof SyntaxError);
assert.strictEqual(
err.message,
'The URL\'s protocol must be one of "ws:", "wss:", or "ws+unix:"'
);
assert.strictEqual(ws._redirects, 1);

server.removeListener('upgrade', onUpgrade);
ws.on('close', () => done());
});
});
});

describe('Connection with query string', () => {
Expand Down