Files
socket.io/test/node.js
Damien Arrachequesne f4d898ee96 feat: allow to provide a list of transport implementations
This commit adds the ability to provide a list of transport
implementations to use when connecting to an Engine.IO server.

This can be used to use HTTP long-polling based on `fetch()`, instead
of the default implementation based on the `XMLHttpRequest` object.

```
import { Socket, Fetch, WebSocket } from "engine.io-client";

const socket = new Socket({
  transports: [Fetch, WebSocket]
});
```

This is useful in some environments that do not provide a
`XMLHttpRequest` object, like Chrome extension background scripts.

> XMLHttpRequest() can't be called from a service worker, extension or
otherwise. Replace calls from your background script to
XMLHttpRequest() with calls to global fetch().

Source: https://developer.chrome.com/docs/extensions/develop/migrate/to-service-workers#replace-xmlhttprequest

Related:

- https://github.com/socketio/engine.io-client/issues/716
- https://github.com/socketio/socket.io/issues/4980

This is also useful when running the client with Deno or Bun, as it
allows to use the built-in `fetch()` method and `WebSocket` object,
instead of using the `xmlhttprequest-ssl` and `ws` Node.js packages.

Related: https://github.com/socketio/socket.io-deno/issues/12

This feature also comes with the ability to exclude the code related to
unused transports (a.k.a. "tree-shaking"):

```js
import { SocketWithoutUpgrade, WebSocket } from "engine.io-client";

const socket = new SocketWithoutUpgrade({
  transports: [WebSocket]
});
```

In that case, the code related to HTTP long-polling and WebTransport
will be excluded from the final bundle.

Related: https://github.com/socketio/socket.io/discussions/4393
2024-05-31 16:56:25 +02:00

139 lines
3.6 KiB
JavaScript

const path = require("path");
const { exec } = require("child_process");
const { Socket } = require("../");
const { repeat } = require("./util");
const expect = require("expect.js");
const { parse } = require("../build/cjs/globals.node.js");
describe("node.js", () => {
describe("autoRef option", () => {
const fixture = (filename) =>
process.execPath + " " + path.join(__dirname, "fixtures", filename);
it("should stop once the timer is triggered", (done) => {
exec(fixture("unref.js"), done);
});
it("should stop once the timer is triggered (polling)", (done) => {
exec(fixture("unref-polling-only.js"), done);
});
it("should stop once the timer is triggered (websocket)", (done) => {
exec(fixture("unref-websocket-only.js"), done);
});
it("should not stop with autoUnref set to false", (done) => {
let isComplete = false;
const process = exec(fixture("no-unref.js"), () => {
if (!isComplete) {
done(new Error("should not happen"));
}
});
setTimeout(() => {
isComplete = true;
process.kill();
done();
}, 1000);
});
});
it("should merge binary packets according to maxPayload value", (done) => {
const socket = new Socket({ transports: ["polling"] });
socket.on("open", () => {
socket.send(Buffer.allocUnsafe(72));
socket.send(Buffer.allocUnsafe(20));
socket.send(repeat("a", 20));
socket.send(Buffer.allocUnsafe(20));
socket.send(Buffer.allocUnsafe(72));
let count = 0;
socket.on("message", () => {
count++;
if (count === 5) {
socket.close();
done();
}
});
});
});
it("should send cookies with withCredentials: true", (done) => {
const socket = new Socket("http://localhost:3000", {
transports: ["polling"],
withCredentials: true,
});
socket.on("open", () => {
setTimeout(() => {
socket.send("sendHeaders");
}, 10);
});
socket.on("message", (data) => {
if (data === "hi") {
return;
}
const headers = JSON.parse(data);
expect(headers.cookie).to.eql("1=1; 2=2");
socket.close();
done();
});
});
it("should not send cookies with withCredentials: false", (done) => {
const socket = new Socket("http://localhost:3000", {
transports: ["polling"],
withCredentials: false,
});
socket.on("open", () => {
socket.send("sendHeaders");
});
socket.on("message", (data) => {
if (data === "hi") {
return;
}
const headers = JSON.parse(data);
expect(headers.cookie).to.eql(undefined);
socket.close();
done();
});
});
});
describe("cookie parsing", () => {
it("should parse a simple set-cookie header", () => {
const cookieStr = "foo=bar";
expect(parse(cookieStr)).to.eql({
name: "foo",
value: "bar",
});
});
it("should parse a complex set-cookie header", () => {
const cookieStr =
"foo=bar; Max-Age=1000; Domain=.example.com; Path=/; Expires=Tue, 01 Jul 2025 10:01:11 GMT; HttpOnly; Secure; SameSite=strict";
expect(parse(cookieStr)).to.eql({
name: "foo",
value: "bar",
expires: new Date("Tue Jul 01 2025 06:01:11 GMT-0400 (EDT)"),
});
});
it("should parse a weird but valid cookie", () => {
const cookieStr =
"foo=bar=bar&foo=foo&John=Doe&Doe=John; Domain=.example.com; Path=/; HttpOnly; Secure";
expect(parse(cookieStr)).to.eql({
name: "foo",
value: "bar=bar&foo=foo&John=Doe&Doe=John",
});
});
});