Skip to content

Commit

Permalink
feat: add HTTP long-polling implementation based on fetch()
Browse files Browse the repository at this point in the history
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
  • Loading branch information
darrachequesne committed Apr 23, 2024
1 parent 218c344 commit b11763b
Show file tree
Hide file tree
Showing 13 changed files with 499 additions and 333 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ jobs:

- name: Run tests
run: npm test

- name: Run tests with fetch()
run: npm run test:node-fetch
if: ${{ matrix.node-version == '20' }} # fetch() was added in Node.js v18.0.0 (without experimental flag)
3 changes: 3 additions & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ export { transports } from "./transports/index.js";
export { installTimerFunctions } from "./util.js";
export { parse } from "./contrib/parseuri.js";
export { nextTick } from "./transports/websocket-constructor.js";

export { Fetch } from "./transports/polling-fetch.js";
export { XHR } from "./transports/polling-xhr.js";
4 changes: 2 additions & 2 deletions lib/transports/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Polling } from "./polling.js";
import { XHR } from "./polling-xhr.js";
import { WS } from "./websocket.js";
import { WT } from "./webtransport.js";

export const transports = {
websocket: WS,
webtransport: WT,
polling: Polling,
polling: XHR,
};
72 changes: 72 additions & 0 deletions lib/transports/polling-fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Polling } from "./polling.js";
import { CookieJar, createCookieJar } from "./xmlhttprequest.js";

/**
* HTTP long-polling based on `fetch()`
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/fetch
*/
export class Fetch extends Polling {
private readonly cookieJar?: CookieJar;

constructor(opts) {
super(opts);

if (this.opts.withCredentials) {
this.cookieJar = createCookieJar();
}
}

override doPoll() {
this._fetch()
.then((res) => {
if (!res.ok) {
return this.onError("fetch read error", res.status, res);
}

res.text().then((data) => this.onData(data));
})
.catch((err) => {
this.onError("fetch read error", err);
});
}

override doWrite(data: string, callback: () => void) {
this._fetch(data)
.then((res) => {
if (!res.ok) {
return this.onError("fetch write error", res.status, res);
}

callback();
})
.catch((err) => {
this.onError("fetch write error", err);
});
}

private _fetch(data?: string) {
const isPost = data !== undefined;
const headers = new Headers(this.opts.extraHeaders);

if (isPost) {
headers.set("content-type", "text/plain;charset=UTF-8");
}

this.cookieJar?.appendCookies(headers);

return fetch(this.uri(), {
method: isPost ? "POST" : "GET",
body: isPost ? data : null,
headers,
credentials: this.opts.withCredentials ? "include" : "omit",
}).then((res) => {
if (this.cookieJar) {
// @ts-ignore getSetCookie() was added in Node.js v19.7.0
this.cookieJar.parseCookies(res.headers.getSetCookie());
}

return res;
});
}
}
Loading

0 comments on commit b11763b

Please sign in to comment.