Skip to content

Commit 362bc78

Browse files
fix: properly call the send callback during upgrade
The "drain" event (added in [1]) had two different meanings: - the transport is ready to be written - the packets are sent over the wire For the WebSocket and the WebTransport transports, those two events happen at the same time, but this is not the case for the HTTP long-polling transport: - the transport is ready to be written when the client sends a GET request - the packets are sent over the wire when the server responds to the GET request Which caused an issue with send callbacks during an upgrade, since the packets were written but the client would not open a new GET request. There are now two distinct events: "ready" and "drain" Related: #695 [1]: 2a93f06
1 parent afd2934 commit 362bc78

File tree

8 files changed

+37
-8
lines changed

8 files changed

+37
-8
lines changed

lib/socket.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -221,18 +221,21 @@ export class Socket extends EventEmitter {
221221
*/
222222
private setTransport(transport) {
223223
const onError = this.onError.bind(this);
224+
const onReady = () => this.flush();
224225
const onPacket = this.onPacket.bind(this);
225226
const onDrain = this.onDrain.bind(this);
226227
const onClose = this.onClose.bind(this, "transport close");
227228

228229
this.transport = transport;
229230
this.transport.once("error", onError);
231+
this.transport.on("ready", onReady);
230232
this.transport.on("packet", onPacket);
231233
this.transport.on("drain", onDrain);
232234
this.transport.once("close", onClose);
233235

234236
this.cleanupFn.push(function () {
235237
transport.removeListener("error", onError);
238+
transport.removeListener("ready", onReady);
236239
transport.removeListener("packet", onPacket);
237240
transport.removeListener("drain", onDrain);
238241
transport.removeListener("close", onClose);
@@ -245,8 +248,6 @@ export class Socket extends EventEmitter {
245248
* @private
246249
*/
247250
private onDrain() {
248-
this.flush();
249-
250251
if (this.sentCallbackFn.length > 0) {
251252
debug("executing batch send callback");
252253
const seqFn = this.sentCallbackFn.shift();

lib/transports-uws/polling.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export class Polling extends Transport {
9797
res.onAborted(onClose);
9898

9999
this.writable = true;
100-
this.emit("drain");
100+
this.emit("ready");
101101

102102
// if we're still writable but had a pending close, trigger an empty send
103103
if (this.writable && this.shouldClose) {
@@ -291,6 +291,7 @@ export class Polling extends Transport {
291291
debug('writing "%s"', data);
292292
this.doWrite(data, options, () => {
293293
this.req.cleanup();
294+
this.emit("drain");
294295
});
295296
}
296297

lib/transports-uws/websocket.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,9 @@ export class WebSocket extends Transport {
6060
this.socket.send(data, isBinary, compress);
6161

6262
if (isLast) {
63-
this.writable = true;
6463
this.emit("drain");
64+
this.writable = true;
65+
this.emit("ready");
6566
}
6667
};
6768

lib/transports/polling.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ export class Polling extends Transport {
9696
req.on("close", onClose);
9797

9898
this.writable = true;
99-
this.emit("drain");
99+
this.emit("ready");
100100

101101
// if we're still writable but had a pending close, trigger an empty send
102102
if (this.writable && this.shouldClose) {
@@ -258,6 +258,7 @@ export class Polling extends Transport {
258258
debug('writing "%s"', data);
259259
this.doWrite(data, options, () => {
260260
this.req.cleanup();
261+
this.emit("drain");
261262
});
262263
}
263264

lib/transports/websocket.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,9 @@ export class WebSocket extends Transport {
103103
if (err) {
104104
this.onError("write error", err.stack);
105105
} else {
106-
this.writable = true;
107106
this.emit("drain");
107+
this.writable = true;
108+
this.emit("ready");
108109
}
109110
};
110111

lib/transports/webtransport.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@ export class WebTransport extends Transport {
5656
debug("error while writing: %s", e.message);
5757
}
5858

59-
this.writable = true;
6059
this.emit("drain");
60+
this.writable = true;
61+
this.emit("ready");
6162
}
6263

6364
doClose(fn) {

lib/userver.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export class uServer extends BaseServer {
8080
const transport = ws.getUserData().transport;
8181
transport.socket = ws;
8282
transport.writable = true;
83-
transport.emit("drain");
83+
transport.emit("ready");
8484
},
8585
message: (ws, message, isBinary) => {
8686
ws.getUserData().transport.onData(

test/server.js

+23
Original file line numberDiff line numberDiff line change
@@ -2701,6 +2701,29 @@ describe("server", () => {
27012701
});
27022702
});
27032703

2704+
it("should execute when message sent during polling upgrade window", (done) => {
2705+
const engine = listen((port) => {
2706+
const socket = new ClientSocket(`ws://localhost:${port}`, {
2707+
transports: ["polling", "websocket"],
2708+
});
2709+
2710+
const partialDone = createPartialDone(() => {
2711+
engine.httpServer?.close();
2712+
socket.close();
2713+
done();
2714+
}, 2);
2715+
2716+
engine.on("connection", (conn) => {
2717+
conn.on("upgrading", () => {
2718+
conn.send("a", partialDone);
2719+
});
2720+
});
2721+
socket.on("open", () => {
2722+
socket.on("message", partialDone);
2723+
});
2724+
});
2725+
});
2726+
27042727
it("should execute when message sent (websocket)", (done) => {
27052728
const engine = listen({ allowUpgrades: false }, (port) => {
27062729
const socket = new ClientSocket(`ws://localhost:${port}`, {

0 commit comments

Comments
 (0)