Skip to content

Commit

Permalink
feat: specify cors options via cli or js
Browse files Browse the repository at this point in the history
Closes #196, #221
  • Loading branch information
jonasgloning committed Feb 14, 2023
1 parent ac44657 commit 05f12cd
Show file tree
Hide file tree
Showing 10 changed files with 281 additions and 84 deletions.
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,20 +84,20 @@ If you have your own server, you can attach PeerServer.
## Config / CLI options
You can provide config object to `PeerServer` function or specify options for `peerjs` CLI.
| CLI option | JS option | Description | Required | Default |
| -------- | ------- | ------------- | :------: | :---------: |
| `--port, -p` | `port` | Port to listen (number) | **Yes** | |
| `--key, -k` | `key` | Connection key (string). Client must provide it to call API methods | No | `"peerjs"` |
| `--path` | `path` | Path (string). The server responds for requests to the root URL + path. **E.g.** Set the `path` to `/myapp` and run server on 9000 port via `peerjs --port 9000 --path /myapp` Then open http://127.0.0.1:9000/myapp - you should see a JSON reponse. | No | `"/"` |
| `--proxied` | `proxied` | Set `true` if PeerServer stays behind a reverse proxy (boolean) | No | `false` |
| `--expire_timeout, -t` | `expire_timeout` | The amount of time after which a message sent will expire, the sender will then receive a `EXPIRE` message (milliseconds). | No | `5000` |
| `--alive_timeout` | `alive_timeout` | Timeout for broken connection (milliseconds). If the server doesn't receive any data from client (includes `pong` messages), the client's connection will be destroyed. | No | `60000` |
| `--concurrent_limit, -c` | `concurrent_limit` | Maximum number of clients' connections to WebSocket server (number) | No | `5000` |
| `--sslkey` | `sslkey` | Path to SSL key (string) | No | |
| `--sslcert` | `sslcert` | Path to SSL certificate (string) | No | |
| `--allow_discovery` | `allow_discovery` | Allow to use GET `/peers` http API method to get an array of ids of all connected clients (boolean) | No | |
| | `generateClientId` | A function which generate random client IDs when calling `/id` API method (`() => string`) | No | `uuid/v4` |
| CLI option | JS option | Description | Required | Default |
|--------------------------|--------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:|:----------:|
| `--port, -p` | `port` | Port to listen (number) | **Yes** | |
| `--key, -k` | `key` | Connection key (string). Client must provide it to call API methods | No | `"peerjs"` |
| `--path` | `path` | Path (string). The server responds for requests to the root URL + path. **E.g.** Set the `path` to `/myapp` and run server on 9000 port via `peerjs --port 9000 --path /myapp` Then open http://127.0.0.1:9000/myapp - you should see a JSON reponse. | No | `"/"` |
| `--proxied` | `proxied` | Set `true` if PeerServer stays behind a reverse proxy (boolean) | No | `false` |
| `--expire_timeout, -t` | `expire_timeout` | The amount of time after which a message sent will expire, the sender will then receive a `EXPIRE` message (milliseconds). | No | `5000` |
| `--alive_timeout` | `alive_timeout` | Timeout for broken connection (milliseconds). If the server doesn't receive any data from client (includes `pong` messages), the client's connection will be destroyed. | No | `60000` |
| `--concurrent_limit, -c` | `concurrent_limit` | Maximum number of clients' connections to WebSocket server (number) | No | `5000` |
| `--sslkey` | `sslkey` | Path to SSL key (string) | No | |
| `--sslcert` | `sslcert` | Path to SSL certificate (string) | No | |
| `--allow_discovery` | `allow_discovery` | Allow to use GET `/peers` http API method to get an array of ids of all connected clients (boolean) | No | |
| `--cors` | `corsOptions` | The CORS origins that can access this server |
| | `generateClientId` | A function which generate random client IDs when calling `/id` API method (`() => string`) | No | `uuid/v4` |
## Using HTTPS
Simply pass in PEM-encoded certificate and key.
Expand Down
110 changes: 72 additions & 38 deletions __test__/peerjs.spec.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,91 @@
import { describe, expect, it } from "@jest/globals";
import {describe, expect, it} from "@jest/globals";

import http from 'http';
import expectedJson from '../app.json';
import { spawn } from 'child_process';
import path from 'path';
import fetch from "node-fetch";
import * as crypto from "crypto";
import {startServer} from "./utils";

const PORT = '9000';

async function makeRequest() {
return new Promise<object>((resolve, reject) => {
http.get(`http://localhost:${PORT}/`, resp => {
let data = '';
return new Promise<object>((resolve, reject) => {
http.get(`http://localhost:${PORT}/`, resp => {
let data = '';

resp.on('data', chunk => {
data += chunk;
});
resp.on('data', chunk => {
data += chunk;
});

resp.on('end', () => {
resolve(JSON.parse(data));
});
resp.on('end', () => {
resolve(JSON.parse(data));
});

}).on("error", err => {
console.log("Error: " + err.message);
reject(err);
}).on("error", err => {
console.log("Error: " + err.message);
reject(err);
});
});
});
}

describe('Check bin/peerjs', () => {
it('should return content of app.json file', async () => {
expect.assertions(1);
let resolver: () => void;
let rejecter: (err: unknown) => void;
const promise = new Promise<void>((resolve, reject) => {
resolver = resolve;
rejecter = reject;
it('should return content of app.json file', async () => {
expect.assertions(1);

const ls = await startServer()
try {
const resp = await makeRequest();
expect(resp).toEqual(expectedJson);
} finally {
ls.kill();
}

});

const ls = spawn('node', [path.join(__dirname, '../', 'dist/bin/peerjs.js'), '--port', PORT]);
ls.stdout.on('data', async (data: string) => {
if (!data.includes('Started')) return;

try {
const resp = await makeRequest();
expect(resp).toEqual(expectedJson);
resolver();
} catch (error) {
rejecter(error);
} finally {
ls.kill('SIGKILL');
}
it('should reflect the origin header in CORS by default', async () => {
expect.assertions(1);

const ls = await startServer()
const origin = crypto.randomUUID();
try {
const res = await fetch(`http://localhost:${PORT}/peerjs/id`, {
headers: {
Origin: origin
}
})
expect(res.headers.get("access-control-allow-origin")).toBe(origin)
} finally {
ls.kill()
}
});
it('should respect the CORS parameters', async () => {
expect.assertions(3);

return promise;
});
const origin1 = crypto.randomUUID();
const origin2 = crypto.randomUUID();
const origin3 = crypto.randomUUID();
const ls = await startServer(["--cors", origin1, "--cors", origin2])
try {
const res1 = await fetch(`http://localhost:${PORT}/peerjs/id`, {
headers: {
Origin: origin1
}
})
expect(res1.headers.get("access-control-allow-origin")).toBe(origin1)
const res2 = await fetch(`http://localhost:${PORT}/peerjs/id`, {
headers: {
Origin: origin2
}
})
expect(res2.headers.get("access-control-allow-origin")).toBe(origin2)
const res3 = await fetch(`http://localhost:${PORT}/peerjs/id`, {
headers: {
Origin: origin3
}
})
expect(res3.headers.get("access-control-allow-origin")).toBe(null)
} finally {
ls.kill()
}
});
});
14 changes: 14 additions & 0 deletions __test__/utils.ts
Original file line number Diff line number Diff line change
@@ -1 +1,15 @@
import {ChildProcessWithoutNullStreams, spawn} from "child_process";
import path from "path";

export const wait = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms));

export const startServer = (params: string[] = []) => {
return new Promise<ChildProcessWithoutNullStreams>((resolve, reject)=> {
const ls = spawn('node', [path.join(__dirname, '../', 'dist/bin/peerjs.js'), '--port', "9000", ...params]);
ls.stdout.once("data", ()=> resolve(ls))
ls.stderr.once("data", ()=>{
ls.kill()
reject()
})
})
}
23 changes: 17 additions & 6 deletions bin/peerjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import yargs from "yargs";
import { hideBin } from 'yargs/helpers'
import { PeerServer} from "../src";
import type { AddressInfo } from "node:net";
import type {CorsOptions} from "cors";

const y = yargs(hideBin(process.argv));

Expand Down Expand Up @@ -73,18 +74,28 @@ const opts = y
demandOption: false,
describe: "allow discovery of peers",
},
proxied: {
type: "boolean",
demandOption: false,
describe: "Set true if PeerServer stays behind a reverse proxy",
default: false,
},
proxied: {
type: "boolean",
demandOption: false,
describe: "Set true if PeerServer stays behind a reverse proxy",
default: false,
},
cors: {
type: "string",
array: true,
describe: "Set the list of CORS origins",
},
})
.boolean("allow_discovery").parseSync();

if(!opts.port){
opts.port= parseInt(process.env["PORT"] as string)
}
if(opts.cors){
opts["corsOptions"] = {
origin: opts.cors
} satisfies CorsOptions;
}
process.on("uncaughtException", function (e) {
console.error("Error: " + e);
});
Expand Down
12 changes: 8 additions & 4 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
/** @type {import('jest').Config} */
const config = {
transform: {
"^.+\\.(t|j)sx?$": "@swc/jest",
},
collectCoverageFrom: ["./src/**"]
testEnvironment: "node",
transform: {
"^.+\\.(t|j)sx?$": "@swc/jest",
},
transformIgnorePatterns: [
// "node_modules"
],
collectCoverageFrom: ["./src/**"]
};

export default config;
Loading

0 comments on commit 05f12cd

Please sign in to comment.