Skip to content

Commit 923a12e

Browse files
fix(eio): discard all pending packets when the server is closed
In some specific cases, the transport was not closed right away, leaving the Node.js process alive even after closing the server. The HTTP long-polling transport would be closed after the heartbeat failure and the `closeTimeout` delay (20 + 25 + 30 seconds). Example: ```js io.on("connection", (socket) => { // the writeBuffer is not empty, so the transport is not closed right away io.close(); }); ``` Related: #5088
1 parent 13c6d2e commit 923a12e

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

packages/engine.io/lib/socket.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,13 @@ export class Socket extends EventEmitter {
554554
* @return {Socket} for chaining
555555
*/
556556
public close(discard?: boolean) {
557+
if (
558+
discard &&
559+
(this.readyState === "open" || this.readyState === "closing")
560+
) {
561+
return this.closeTransport(discard);
562+
}
563+
557564
if ("open" !== this.readyState) return;
558565

559566
this.readyState = "closing";
@@ -570,7 +577,7 @@ export class Socket extends EventEmitter {
570577
return;
571578
}
572579

573-
debug("the buffer is empty, closing the transport right away", discard);
580+
debug("the buffer is empty, closing the transport right away");
574581
this.closeTransport(discard);
575582
}
576583

@@ -581,7 +588,7 @@ export class Socket extends EventEmitter {
581588
* @private
582589
*/
583590
private closeTransport(discard: boolean) {
584-
debug("closing the transport (discard? %s)", discard);
591+
debug("closing the transport (discard? %s)", !!discard);
585592
if (discard) this.transport.discard();
586593
this.transport.close(this.onClose.bind(this, "forced close"));
587594
}

packages/engine.io/test/server.js

+61
Original file line numberDiff line numberDiff line change
@@ -1634,6 +1634,67 @@ describe("server", () => {
16341634
});
16351635
});
16361636

1637+
it("should discard the packets in the writeBuffer when stopping the server", (done) => {
1638+
engine = listen((port) => {
1639+
const clientSocket = new ClientSocket(`ws://localhost:${port}`);
1640+
1641+
clientSocket.on("data", () => {
1642+
done(new Error("should not happen"));
1643+
});
1644+
1645+
clientSocket.on("close", (reason) => {
1646+
expect(reason).to.eql("transport error");
1647+
1648+
clientSocket.close();
1649+
done();
1650+
});
1651+
1652+
engine.on("connection", (socket) => {
1653+
socket.write("hello");
1654+
engine.close();
1655+
});
1656+
});
1657+
});
1658+
1659+
it("should discard the packets in the writeBuffer when stopping the server (2)", (done) => {
1660+
engine = listen((port) => {
1661+
const clientSocket = new ClientSocket(`ws://localhost:${port}`);
1662+
1663+
clientSocket.on("data", () => {
1664+
done(new Error("should not happen"));
1665+
});
1666+
1667+
clientSocket.on("close", (reason) => {
1668+
expect(reason).to.eql("transport error");
1669+
1670+
clientSocket.close();
1671+
done();
1672+
});
1673+
1674+
engine.on("connection", (socket) => {
1675+
socket.write("hello");
1676+
socket.close(); // readyState is now "closing"
1677+
engine.close();
1678+
});
1679+
});
1680+
});
1681+
1682+
it("should not discard the packets in the writeBuffer when closing gracefully", (done) => {
1683+
engine = listen((port) => {
1684+
const clientSocket = new ClientSocket(`ws://localhost:${port}`);
1685+
1686+
clientSocket.on("data", (val) => {
1687+
expect(val).to.eql("hello");
1688+
done();
1689+
});
1690+
1691+
engine.on("connection", (socket) => {
1692+
socket.write("hello");
1693+
socket.close();
1694+
});
1695+
});
1696+
});
1697+
16371698
describe("graceful close", () => {
16381699
before(function () {
16391700
if (process.env.EIO_WS_ENGINE === "uws") {

0 commit comments

Comments
 (0)