Skip to content

Commit b8186dd

Browse files
authored
[fix] Do not throw if the redirect URL is invalid (#1980)
If the redirect URL is invalid, then emit the error instead of throwing it, otherwise there is no way to handle it. Closes #1975
1 parent 5a905e4 commit b8186dd

File tree

2 files changed

+85
-12
lines changed

2 files changed

+85
-12
lines changed

lib/websocket.js

+38-12
Original file line numberDiff line numberDiff line change
@@ -630,19 +630,26 @@ function initAsClient(websocket, address, protocols, options) {
630630

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

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

640-
if (isUnixSocket && !parsedUrl.pathname) {
641-
throw new SyntaxError("The URL's pathname is empty");
642-
}
644+
if (invalidURLMessage) {
645+
const err = new SyntaxError(invalidURLMessage);
643646

644-
if (parsedUrl.hash) {
645-
throw new SyntaxError('The URL contains a fragment identifier');
647+
if (websocket._redirects === 0) {
648+
throw err;
649+
} else {
650+
emitErrorAndClose(websocket, err);
651+
return;
652+
}
646653
}
647654

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

726733
req = websocket._req = null;
727-
websocket._readyState = WebSocket.CLOSING;
728-
websocket.emit('error', err);
729-
websocket.emitClose();
734+
emitErrorAndClose(websocket, err);
730735
});
731736

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

747752
req.abort();
748753

749-
const addr = new URL(location, address);
754+
let addr;
755+
756+
try {
757+
addr = new URL(location, address);
758+
} catch (e) {
759+
const err = new SyntaxError(`Invalid URL: ${location}`);
760+
emitErrorAndClose(websocket, err);
761+
return;
762+
}
750763

751764
initAsClient(websocket, addr, protocols, options);
752765
} else if (!websocket.emit('unexpected-response', req, res)) {
@@ -849,6 +862,19 @@ function initAsClient(websocket, address, protocols, options) {
849862
});
850863
}
851864

865+
/**
866+
* Emit the `'error'` and `'close'` event.
867+
*
868+
* @param {WebSocket} websocket The WebSocket instance
869+
* @param {Error} The error to emit
870+
* @private
871+
*/
872+
function emitErrorAndClose(websocket, err) {
873+
websocket._readyState = WebSocket.CLOSING;
874+
websocket.emit('error', err);
875+
websocket.emitClose();
876+
}
877+
852878
/**
853879
* Create a `net.Socket` and initiate a connection.
854880
*

test/websocket.test.js

+47
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,53 @@ describe('WebSocket', () => {
10281028
ws.on('close', () => done());
10291029
});
10301030
});
1031+
1032+
it('emits an error if the redirect URL is invalid (1/2)', (done) => {
1033+
const onUpgrade = (req, socket) => {
1034+
socket.end('HTTP/1.1 302 Found\r\nLocation: ws://\r\n\r\n');
1035+
};
1036+
1037+
server.on('upgrade', onUpgrade);
1038+
1039+
const ws = new WebSocket(`ws://localhost:${server.address().port}`, {
1040+
followRedirects: true
1041+
});
1042+
1043+
ws.on('open', () => done(new Error("Unexpected 'open' event")));
1044+
ws.on('error', (err) => {
1045+
assert.ok(err instanceof SyntaxError);
1046+
assert.strictEqual(err.message, 'Invalid URL: ws://');
1047+
assert.strictEqual(ws._redirects, 1);
1048+
1049+
server.removeListener('upgrade', onUpgrade);
1050+
ws.on('close', () => done());
1051+
});
1052+
});
1053+
1054+
it('emits an error if the redirect URL is invalid (2/2)', (done) => {
1055+
const onUpgrade = (req, socket) => {
1056+
socket.end('HTTP/1.1 302 Found\r\nLocation: http://localhost\r\n\r\n');
1057+
};
1058+
1059+
server.on('upgrade', onUpgrade);
1060+
1061+
const ws = new WebSocket(`ws://localhost:${server.address().port}`, {
1062+
followRedirects: true
1063+
});
1064+
1065+
ws.on('open', () => done(new Error("Unexpected 'open' event")));
1066+
ws.on('error', (err) => {
1067+
assert.ok(err instanceof SyntaxError);
1068+
assert.strictEqual(
1069+
err.message,
1070+
'The URL\'s protocol must be one of "ws:", "wss:", or "ws+unix:"'
1071+
);
1072+
assert.strictEqual(ws._redirects, 1);
1073+
1074+
server.removeListener('upgrade', onUpgrade);
1075+
ws.on('close', () => done());
1076+
});
1077+
});
10311078
});
10321079

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

0 commit comments

Comments
 (0)