Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/engine.io-client/lib/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@ export abstract class Transport extends Emitter<
*/
protected onData(data: RawData) {
const packet = decodePacket(data, this.socket.binaryType);
if ("close" === packet.type) {
debug("got close packet");
if (this.readyState === "opening" || this.readyState === "open") {
this.doClose();
this.onClose({ description: "transport closed by the server" });
}
return;
}
this.onPacket(packet);
}

Expand Down
8 changes: 7 additions & 1 deletion packages/engine.io/lib/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,13 @@ export abstract class Transport extends EventEmitter {
* @protected
*/
protected onData(data: RawData) {
this.onPacket(this.parser.decodePacket(data));
const packet = this.parser.decodePacket(data);
if ("close" === packet.type) {
debug("got close packet");
this.close();
return;
}
this.onPacket(packet);
}

/**
Expand Down
50 changes: 50 additions & 0 deletions packages/engine.io/test/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -1604,6 +1604,56 @@ describe("server", () => {
});
});

it("should close transport upon receiving a close packet (ws)", (done) => {
const opts = { allowUpgrades: false, transports: ["websocket"] };
const engine = listen(opts, (port) => {
engine.on("connection", (conn) => {
conn.transport.on("close", done);
});
const socket = new ClientSocket(`ws://localhost:${port}`, {
transports: ["websocket"],
});
socket.on("open", () => {
// bypass the client library entirely and send a raw close packet
// ("1", per the Engine.IO protocol), to make sure the server
// reacts the same way regardless of which client implementation
// is on the other end
socket.transport.ws.send("1");
});
});
});

if (!IS_CLIENT_V3) {
// the "engine.io-client-v3" package used when IS_CLIENT_V3 is set is a
// separately published legacy dependency, not the "engine.io-client"
// package touched by this fix, so this test only applies to the
// current (v4) client
it("should make the client close its transport upon receiving a close packet (ws)", (done) => {
const opts = { allowUpgrades: false, transports: ["websocket"] };
const engine = listen(opts, (port) => {
engine.on("connection", (conn) => {
// simulate a server sending a close packet without necessarily
// closing the underlying connection itself right away (e.g. a
// different server implementation), to make sure the client
// reacts to the packet itself rather than relying on the
// underlying WebSocket connection being closed by the server
conn.transport.send([{ type: "close" }]);
});
const socket = new ClientSocket(`ws://localhost:${port}`, {
transports: ["websocket"],
});
socket.on("close", (reason, description) => {
expect(reason).to.eql("transport close");
// same wording as the polling transport, for consistency
expect(description.description).to.eql(
"transport closed by the server",
);
done();
});
});
});
}

it("should close transport upon parse error (polling)", (done) => {
const opts = { allowUpgrades: false, transports: ["polling"] };
const engine = listen(opts, (port) => {
Expand Down