Skip to content

Commit b11763b

Browse files
feat: add HTTP long-polling implementation based on fetch()
Usage: ```js import { Socket, transports, Fetch } from "engine.io-client"; transports.polling = Fetch; const socket = new Socket("https://example.com"); ``` Note: tree-shaking unused transports is not currently supported and will be added later. Related: - socketio/socket.io#4980 - #716
1 parent 218c344 commit b11763b

File tree

13 files changed

+499
-333
lines changed

13 files changed

+499
-333
lines changed

.github/workflows/ci.yml

+4
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,7 @@ jobs:
3030

3131
- name: Run tests
3232
run: npm test
33+
34+
- name: Run tests with fetch()
35+
run: npm run test:node-fetch
36+
if: ${{ matrix.node-version == '20' }} # fetch() was added in Node.js v18.0.0 (without experimental flag)

lib/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@ export { transports } from "./transports/index.js";
88
export { installTimerFunctions } from "./util.js";
99
export { parse } from "./contrib/parseuri.js";
1010
export { nextTick } from "./transports/websocket-constructor.js";
11+
12+
export { Fetch } from "./transports/polling-fetch.js";
13+
export { XHR } from "./transports/polling-xhr.js";

lib/transports/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { Polling } from "./polling.js";
1+
import { XHR } from "./polling-xhr.js";
22
import { WS } from "./websocket.js";
33
import { WT } from "./webtransport.js";
44

55
export const transports = {
66
websocket: WS,
77
webtransport: WT,
8-
polling: Polling,
8+
polling: XHR,
99
};

lib/transports/polling-fetch.ts

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { Polling } from "./polling.js";
2+
import { CookieJar, createCookieJar } from "./xmlhttprequest.js";
3+
4+
/**
5+
* HTTP long-polling based on `fetch()`
6+
*
7+
* @see https://developer.mozilla.org/en-US/docs/Web/API/fetch
8+
*/
9+
export class Fetch extends Polling {
10+
private readonly cookieJar?: CookieJar;
11+
12+
constructor(opts) {
13+
super(opts);
14+
15+
if (this.opts.withCredentials) {
16+
this.cookieJar = createCookieJar();
17+
}
18+
}
19+
20+
override doPoll() {
21+
this._fetch()
22+
.then((res) => {
23+
if (!res.ok) {
24+
return this.onError("fetch read error", res.status, res);
25+
}
26+
27+
res.text().then((data) => this.onData(data));
28+
})
29+
.catch((err) => {
30+
this.onError("fetch read error", err);
31+
});
32+
}
33+
34+
override doWrite(data: string, callback: () => void) {
35+
this._fetch(data)
36+
.then((res) => {
37+
if (!res.ok) {
38+
return this.onError("fetch write error", res.status, res);
39+
}
40+
41+
callback();
42+
})
43+
.catch((err) => {
44+
this.onError("fetch write error", err);
45+
});
46+
}
47+
48+
private _fetch(data?: string) {
49+
const isPost = data !== undefined;
50+
const headers = new Headers(this.opts.extraHeaders);
51+
52+
if (isPost) {
53+
headers.set("content-type", "text/plain;charset=UTF-8");
54+
}
55+
56+
this.cookieJar?.appendCookies(headers);
57+
58+
return fetch(this.uri(), {
59+
method: isPost ? "POST" : "GET",
60+
body: isPost ? data : null,
61+
headers,
62+
credentials: this.opts.withCredentials ? "include" : "omit",
63+
}).then((res) => {
64+
if (this.cookieJar) {
65+
// @ts-ignore getSetCookie() was added in Node.js v19.7.0
66+
this.cookieJar.parseCookies(res.headers.getSetCookie());
67+
}
68+
69+
return res;
70+
});
71+
}
72+
}

0 commit comments

Comments
 (0)