Skip to content

Commit 67e25ff

Browse files
committed
[fix] Fix case where abortHandshake() does not close the connection
On Node.js >= 14.3.0 `request.abort()` does not destroy the socket if called after the request completed. Fixes #1869
1 parent 23ba6b2 commit 67e25ff

File tree

2 files changed

+60
-2
lines changed

2 files changed

+60
-2
lines changed

lib/websocket.js

+10
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,16 @@ function abortHandshake(websocket, stream, message) {
718718

719719
if (stream.setHeader) {
720720
stream.abort();
721+
722+
if (stream.socket && !stream.socket.destroyed) {
723+
//
724+
// On Node.js >= 14.3.0 `request.abort()` does not destroy the socket if
725+
// called after the request completed. See
726+
// https://github.com/websockets/ws/issues/1869.
727+
//
728+
stream.socket.destroy();
729+
}
730+
721731
stream.once('abort', websocket.emitClose.bind(websocket));
722732
websocket.emit('error', err);
723733
} else {

test/websocket.test.js

+50-2
Original file line numberDiff line numberDiff line change
@@ -1444,7 +1444,7 @@ describe('WebSocket', () => {
14441444
});
14451445

14461446
describe('#close', () => {
1447-
it('closes the connection if called while connecting (1/2)', (done) => {
1447+
it('closes the connection if called while connecting (1/3)', (done) => {
14481448
const wss = new WebSocket.Server({ port: 0 }, () => {
14491449
const ws = new WebSocket(`ws://localhost:${wss.address().port}`);
14501450

@@ -1461,7 +1461,7 @@ describe('WebSocket', () => {
14611461
});
14621462
});
14631463

1464-
it('closes the connection if called while connecting (2/2)', (done) => {
1464+
it('closes the connection if called while connecting (2/3)', (done) => {
14651465
const wss = new WebSocket.Server(
14661466
{
14671467
verifyClient: (info, cb) => setTimeout(cb, 300, true),
@@ -1484,6 +1484,54 @@ describe('WebSocket', () => {
14841484
);
14851485
});
14861486

1487+
it('closes the connection if called while connecting (3/3)', (done) => {
1488+
const server = http.createServer();
1489+
1490+
server.listen(0, function () {
1491+
const ws = new WebSocket(`ws://localhost:${server.address().port}`);
1492+
1493+
ws.on('open', () => done(new Error("Unexpected 'open' event")));
1494+
ws.on('error', (err) => {
1495+
assert.ok(err instanceof Error);
1496+
assert.strictEqual(
1497+
err.message,
1498+
'WebSocket was closed before the connection was established'
1499+
);
1500+
ws.on('close', () => {
1501+
server.close(done);
1502+
});
1503+
});
1504+
1505+
ws.on('unexpected-response', (req, res) => {
1506+
assert.strictEqual(res.statusCode, 502);
1507+
1508+
const chunks = [];
1509+
1510+
res.on('data', (chunk) => {
1511+
chunks.push(chunk);
1512+
});
1513+
1514+
res.on('end', () => {
1515+
assert.strictEqual(Buffer.concat(chunks).toString(), 'foo');
1516+
ws.close();
1517+
});
1518+
});
1519+
});
1520+
1521+
server.on('upgrade', (req, socket) => {
1522+
socket.on('end', socket.end);
1523+
1524+
socket.write(
1525+
`HTTP/1.1 502 ${http.STATUS_CODES[502]}\r\n` +
1526+
'Connection: keep-alive\r\n' +
1527+
'Content-type: text/html\r\n' +
1528+
'Content-Length: 3\r\n' +
1529+
'\r\n' +
1530+
'foo'
1531+
);
1532+
});
1533+
});
1534+
14871535
it('can be called from an error listener while connecting', (done) => {
14881536
const ws = new WebSocket('ws://localhost:1337');
14891537

0 commit comments

Comments
 (0)