Skip to content

Commit 5bc94b5

Browse files
fix: properly report timeout error when connecting
In some specific cases (Node.js client with WebSocket only), the reason attached to the "connect_error" event was "websocket error" instead of "timeout". Related: socketio/socket.io#4062
1 parent 781d753 commit 5bc94b5

File tree

3 files changed

+45
-12
lines changed

3 files changed

+45
-12
lines changed

lib/manager.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -328,35 +328,32 @@ export class Manager<
328328
fn && fn();
329329
});
330330

331-
// emit `error`
332-
const errorSub = on(socket, "error", (err) => {
331+
const onError = (err) => {
333332
debug("error");
334-
self.cleanup();
335-
self._readyState = "closed";
333+
this.cleanup();
334+
this._readyState = "closed";
336335
this.emitReserved("error", err);
337336
if (fn) {
338337
fn(err);
339338
} else {
340339
// Only do this if there is no fn to handle the error
341-
self.maybeReconnectOnOpen();
340+
this.maybeReconnectOnOpen();
342341
}
343-
});
342+
};
343+
344+
// emit `error`
345+
const errorSub = on(socket, "error", onError);
344346

345347
if (false !== this._timeout) {
346348
const timeout = this._timeout;
347349
debug("connect attempt will timeout after %d", timeout);
348350

349-
if (timeout === 0) {
350-
openSubDestroy(); // prevents a race condition with the 'open' event
351-
}
352-
353351
// set timer
354352
const timer = this.setTimeoutFn(() => {
355353
debug("connect attempt timed out after %d", timeout);
356354
openSubDestroy();
355+
onError(new Error("timeout"));
357356
socket.close();
358-
// @ts-ignore
359-
socket.emit("error", new Error("timeout"));
360357
}, timeout);
361358

362359
if (this.opts.autoUnref) {

test/socket.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,38 @@ describe("socket", () => {
7070
});
7171
});
7272

73+
it("fire a connect_error event on open timeout (polling)", () => {
74+
return wrap((done) => {
75+
const socket = io(BASE_URL, {
76+
forceNew: true,
77+
transports: ["polling"],
78+
timeout: 0,
79+
});
80+
81+
socket.on("connect_error", (err) => {
82+
expect(err.message).to.eql("timeout");
83+
socket.disconnect();
84+
done();
85+
});
86+
});
87+
});
88+
89+
it("fire a connect_error event on open timeout (websocket)", () => {
90+
return wrap((done) => {
91+
const socket = io(BASE_URL, {
92+
forceNew: true,
93+
transports: ["websocket"],
94+
timeout: 0,
95+
});
96+
97+
socket.on("connect_error", (err) => {
98+
expect(err.message).to.eql("timeout");
99+
socket.disconnect();
100+
done();
101+
});
102+
});
103+
});
104+
73105
it("doesn't fire a connect_error event when the connection is already established", () => {
74106
return wrap((done) => {
75107
const socket = io(BASE_URL, { forceNew: true });

test/support/server.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ export function createServer() {
55
const server = new Server(3210, {
66
pingInterval: 2000,
77
connectionStateRecovery: {},
8+
allowRequest: (req, callback) => {
9+
// add a fixed delay to test the connection timeout on the client side
10+
setTimeout(() => callback(null, true), 10);
11+
},
812
});
913

1014
server.of("/foo").on("connection", (socket) => {

0 commit comments

Comments
 (0)