├── .devcontainer
└── devcontainer.json
├── .dockerignore
├── .eslintignore
├── .eslintrc.json
├── .github
└── workflows
│ ├── node.js.yml
│ ├── prettier.yml
│ └── release.yml
├── .gitignore
├── .gitpod.yml
├── .prettierignore
├── .prettierrc.toml
├── .releaserc.json
├── CHANGELOG.md
├── Dockerfile
├── LICENSE
├── PRIVACY.md
├── README.md
├── __test__
├── messageHandler
│ ├── handlers
│ │ ├── heartbeat
│ │ │ └── index.spec.ts
│ │ └── transmission
│ │ │ └── index.spec.ts
│ └── handlersRegistry.spec.ts
├── models
│ ├── messageQueue.spec.ts
│ └── realm.spec.ts
├── peerjs.spec.ts
├── services
│ ├── checkBrokenConnections
│ │ └── index.spec.ts
│ ├── messagesExpire
│ │ └── index.spec.ts
│ └── webSocketServer
│ │ └── index.spec.ts
└── utils.ts
├── app.json
├── bin
└── peerjs.ts
├── jest.config.js
├── package-lock.json
├── package.json
├── renovate.json
├── src
├── api
│ ├── README.md
│ ├── index.ts
│ └── v1
│ │ └── public
│ │ └── index.ts
├── config
│ └── index.ts
├── enums.ts
├── index.ts
├── instance.ts
├── messageHandler
│ ├── handler.ts
│ ├── handlers
│ │ ├── heartbeat
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ └── transmission
│ │ │ └── index.ts
│ ├── handlersRegistry.ts
│ └── index.ts
├── models
│ ├── client.ts
│ ├── message.ts
│ ├── messageQueue.ts
│ └── realm.ts
└── services
│ ├── checkBrokenConnections
│ └── index.ts
│ ├── messagesExpire
│ └── index.ts
│ └── webSocketServer
│ └── index.ts
└── tsconfig.json
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the
2 | // README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
3 | {
4 | "name": "Node.js & TypeScript",
5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
6 | "image": "mcr.microsoft.com/devcontainers/typescript-node:0-18",
7 |
8 | // Features to add to the dev container. More info: https://containers.dev/features.
9 | // "features": {},
10 |
11 | // Use 'forwardPorts' to make a list of ports inside the container available locally.
12 | "forwardPorts": [9000],
13 |
14 | // Use 'postCreateCommand' to run commands after the container is created.
15 | "postCreateCommand": "npm clean-install"
16 |
17 | // Configure tool-specific properties.
18 | // "customizations": {},
19 |
20 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
21 | // "remoteUser": "root"
22 | }
23 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | .git
2 | .nyc_output
3 | .parcel-cache
4 | coverage
5 | dist
6 | node_modules
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist/
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "extends": [
4 | "eslint:recommended",
5 | "plugin:@typescript-eslint/recommended-type-checked",
6 | "plugin:@typescript-eslint/strict-type-checked",
7 | "plugin:@typescript-eslint/stylistic-type-checked"
8 | ],
9 | "ignorePatterns": ["coverage", "jest.config.js", "dist", "__test__"],
10 | "env": {
11 | "node": true,
12 | "es6": true
13 | },
14 | "parserOptions": {
15 | "project": true
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | pull_request:
9 | branches: ["master"]
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 |
15 | strategy:
16 | matrix:
17 | node-version: [16.x, 18.x, 20.x]
18 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
19 |
20 | steps:
21 | - uses: actions/checkout@v4
22 | - name: Use Node.js ${{ matrix.node-version }}
23 | uses: actions/setup-node@v4
24 | with:
25 | node-version: ${{ matrix.node-version }}
26 | cache: "npm"
27 | - run: npm ci
28 | - run: npm run build
29 | - run: npm run lint
30 | - run: npm run coverage
31 | - name: Publish code coverage to CodeClimate
32 | uses: paambaati/codeclimate-action@v5.0.0
33 | env:
34 | CC_TEST_REPORTER_ID: ${{secrets.CC_TEST_REPORTER_ID}}
35 |
--------------------------------------------------------------------------------
/.github/workflows/prettier.yml:
--------------------------------------------------------------------------------
1 | # From https://til.simonwillison.net/github-actions/prettier-github-actions
2 | name: Check JavaScript for conformance with Prettier
3 |
4 | on:
5 | push:
6 | pull_request:
7 |
8 | jobs:
9 | prettier:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Check out repo
13 | uses: actions/checkout@v4
14 | - uses: actions/setup-node@v4
15 | with:
16 | node-version: 16
17 | cache: "npm"
18 | - run: npm ci
19 | - name: Run prettier
20 | run: |-
21 | npm run format:check
22 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 | on:
3 | push:
4 | branches:
5 | - rc
6 | - stable
7 | jobs:
8 | release:
9 | name: Release
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v4
14 | with:
15 | fetch-depth: 0
16 | - name: Set up Docker Buildx
17 | uses: docker/setup-buildx-action@v3
18 | - name: Setup Node.js
19 | uses: actions/setup-node@v4
20 | with:
21 | node-version: "lts/*"
22 | - name: Install dependencies
23 | run: npm ci
24 | - name: Build
25 | run: npm run build
26 | - name: Import GPG key
27 | id: import_gpg
28 | uses: crazy-max/ghaction-import-gpg@v5
29 | with:
30 | gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
31 | passphrase: ${{ secrets.GPG_PASSPHRASE }}
32 | git_user_signingkey: true
33 | git_commit_gpgsign: true
34 | - name: Release
35 | env:
36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
37 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
38 | GIT_COMMITTER_NAME: ${{ steps.import_gpg.outputs.name }}
39 | GIT_COMMITTER_EMAIL: ${{ steps.import_gpg.outputs.email }}
40 | DOCKER_REGISTRY_USER: ${{ secrets.DOCKERHUB_USERNAME }}
41 | DOCKER_REGISTRY_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
42 | run: npx semantic-release
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .nyc_output
2 | coverage
3 | *.seed
4 | *.log
5 | *.csv
6 | *.dat
7 | *.out
8 | *.pid
9 | *.gz
10 |
11 | .parcel-cache
12 | dist
13 | pids
14 | logs
15 | results
16 |
17 | node_modules
18 | npm-debug.log
19 |
20 | .idea
21 | .cache
22 | .vscode
23 |
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | tasks:
2 | - init: npm i
3 | command: npm start
4 |
5 | ports:
6 | - port: 9000
7 | onOpen: open-preview
8 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | dist
2 | coverage
3 |
4 | # semantic-release
5 | CHANGELOG.md
6 |
--------------------------------------------------------------------------------
/.prettierrc.toml:
--------------------------------------------------------------------------------
1 | trailingComma = "all"
2 | semi = true
3 | useTabs = true
--------------------------------------------------------------------------------
/.releaserc.json:
--------------------------------------------------------------------------------
1 | {
2 | "branches": [
3 | "stable",
4 | {
5 | "name": "rc",
6 | "prerelease": true
7 | }
8 | ],
9 | "plugins": [
10 | "@semantic-release/commit-analyzer",
11 | "@semantic-release/release-notes-generator",
12 | "@semantic-release/changelog",
13 | "@semantic-release/npm",
14 | "@semantic-release/git",
15 | "@semantic-release/github",
16 | [
17 | "@codedependant/semantic-release-docker",
18 | {
19 | "dockerTags": [
20 | "{{#if prerelease.[0]}}{{prerelease.[0]}}{{else}}latest{{/if}}",
21 | "{{major}}-{{#if prerelease.[0]}}{{prerelease.[0]}}{{else}}latest{{/if}}",
22 | "{{major}}.{{minor}}-{{#if prerelease.[0]}}{{prerelease.[0]}}{{else}}latest{{/if}}",
23 | "{{version}}"
24 | ],
25 | "dockerImage": "peerjs-server",
26 | "dockerPlatform": ["linux/amd64", "linux/arm64"],
27 | "dockerFile": "Dockerfile",
28 | "dockerProject": "peerjs"
29 | }
30 | ]
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [1.0.2](https://github.com/peers/peerjs-server/compare/v1.0.1...v1.0.2) (2023-12-05)
2 |
3 |
4 | ### Bug Fixes
5 |
6 | * **deps:** update dependency @types/express to v4.17.18 ([f4bcc16](https://github.com/peers/peerjs-server/commit/f4bcc1651598f349626b67099c7a39b01b417f6c))
7 | * **deps:** update dependency @types/express to v4.17.19 ([ebec5b0](https://github.com/peers/peerjs-server/commit/ebec5b07aa29e951058d64a5c98efa238ce0069a))
8 | * **deps:** update dependency @types/express to v4.17.20 ([a6c01fd](https://github.com/peers/peerjs-server/commit/a6c01fd47bfd89fa74a716650a44643612a659aa))
9 | * **deps:** update dependency @types/express to v4.17.21 ([80df87f](https://github.com/peers/peerjs-server/commit/80df87f3da63624e6c7107453f3789e76d688798))
10 | * reduce unnecessary timeouts ([638af56](https://github.com/peers/peerjs-server/commit/638af56f679881194f60b29ae7f7bb7b5756662a)), closes [#431](https://github.com/peers/peerjs-server/issues/431)
11 |
12 | ## [1.0.1](https://github.com/peers/peerjs-server/compare/v1.0.0...v1.0.1) (2023-08-25)
13 |
14 |
15 | ### Bug Fixes
16 |
17 | * **deps:** update dependency node-fetch to v3.3.1 ([2275ce3](https://github.com/peers/peerjs-server/commit/2275ce35eb3c44e0a22fd4a4e0d2dca66ebd1219))
18 | * **deps:** update dependency node-fetch to v3.3.2 ([05a1833](https://github.com/peers/peerjs-server/commit/05a1833363b0592ee7941dd295c3ca9ac64d9d04))
19 | * **deps:** update dependency yargs to v17.7.2 ([23b4e47](https://github.com/peers/peerjs-server/commit/23b4e47fb32a773d48027b0cdd0c10be7fad27cb))
20 | * remove confusing version number ([f6314df](https://github.com/peers/peerjs-server/commit/f6314df40c37add66deac5dd8487ad54e4814237))
21 |
22 |
23 | ### Reverts
24 |
25 | * Revert "build: deploy to fly.io" ([c3de627](https://github.com/peers/peerjs-server/commit/c3de627fad9ddd22230793fb3f6757d402a052a5))
26 | * Revert "chore: Configure Mend Bolt for GitHub (#349)" ([fd00aef](https://github.com/peers/peerjs-server/commit/fd00aef9809dd60bb7e36b2fdc58e47a1e4b036c)), closes [#349](https://github.com/peers/peerjs-server/issues/349)
27 |
28 | # [1.0.0](https://github.com/peers/peerjs-server/compare/v0.6.1...v1.0.0) (2023-03-07)
29 |
30 |
31 | ### Bug Fixes
32 |
33 | * **deps:** update dependency ws to v8 ([1ecc94b](https://github.com/peers/peerjs-server/commit/1ecc94b887d23ac59b3622a2fefc9fdab24f170f))
34 | * import from ESM only environments ([476299e](https://github.com/peers/peerjs-server/commit/476299ed08f73e41d175d61b4281736bf8df1ea6))
35 | * more accurate types ([68f973a](https://github.com/peers/peerjs-server/commit/68f973afb44a1f71c9fd9a644602312d8ceda5cf)), closes [#182](https://github.com/peers/peerjs-server/issues/182)
36 | * **npm audit:** Updates all dependencies that cause `npm audit` to issue a warning ([1aaafbc](https://github.com/peers/peerjs-server/commit/1aaafbc4504224f36287fd721f6edbc27a5b9eaa)), closes [#287](https://github.com/peers/peerjs-server/issues/287)
37 | * the server could crash if a client sends invalid frames ([29394de](https://github.com/peers/peerjs-server/commit/29394dea5e1303cdf07337d39c2c93249fdd41db))
38 |
39 |
40 | ### Features
41 |
42 | * drop Node {10,11,12,13} support ([b70ed79](https://github.com/peers/peerjs-server/commit/b70ed79d9a239593d128ea2914eea0c2107b03b2))
43 | * ESM support ([2b73b5c](https://github.com/peers/peerjs-server/commit/2b73b5c97de4a366d6635719891b65d5f9878628))
44 | * remove deprecated XHR fallback ([d900145](https://github.com/peers/peerjs-server/commit/d90014590160faf1d489a18ea489c28c43cd4690))
45 | * set the PEERSERVER_PATH with an environment variable ([084fb8a](https://github.com/peers/peerjs-server/commit/084fb8a4bddfcb153a4cb861ba700c8352cd4b35)), closes [#213](https://github.com/peers/peerjs-server/issues/213)
46 | * set the PORT with an environment variable ([68a3398](https://github.com/peers/peerjs-server/commit/68a3398f54b0f45bfe8c501c627f531980823ec1)), closes [#213](https://github.com/peers/peerjs-server/issues/213)
47 | * specify cors options via cli or js ([05f12cd](https://github.com/peers/peerjs-server/commit/05f12cdc562b1a5eb9481f5116da7c001105793a)), closes [#196](https://github.com/peers/peerjs-server/issues/196) [#221](https://github.com/peers/peerjs-server/issues/221)
48 |
49 |
50 | ### Performance Improvements
51 |
52 | * use the builtin UUID generator for Peer ids instead of the `uuid` module ([5d882dd](https://github.com/peers/peerjs-server/commit/5d882dd0c6af9bed8602e0507fdf5c1d284be075))
53 |
54 |
55 | ### BREAKING CHANGES
56 |
57 | * Requires PeerJS >= 1.0
58 | * Node >= 14 required
59 |
60 | 14 is the oldest currently supported version. See https://github.com/nodejs/release#release-schedule
61 |
62 | # PeerServer Changelog
63 |
64 | ### vNEXT
65 |
66 |
67 | ### 0.6.1
68 |
69 | * New: PeerJS Server in Docker capture ^C signal and terminate gracefully. #205
70 | * Fix: SSL options in default config. #230
71 |
72 | ### 0.6.0
73 |
74 | * New: `host` option (`--host`, `-H`). #197 Thanks @millette
75 | * Fix: Allows SNICallback instead of hardcoded key/cert. #225 Thanks @brunobg
76 | * Change: Upgrade TypeScript version to 4.1.2.
77 |
78 | ### 0.5.3
79 |
80 | * PeerServer uses yargs instead of an outdated minimist. #190 Thanks @hobindar
81 |
82 | ### 0.5.2
83 |
84 | * Fix: WebSocket server doesn't work on Windows #170 Thanks @lqdchrm
85 |
86 | ### 0.5.1
87 |
88 | * Fix: WebSocket server doesn't work when use non "/" mount path with ExpressPeerServer #132
89 |
90 | ### 0.5.0
91 |
92 | * Fix: http api not working - #163 Thanks riscoss63
93 |
94 | * Change: use "/" instead of "/myapp" as a default value for config's `path` option
95 |
96 | * New: typescript declaration file
97 |
98 | * Update deps:
99 | ```diff
100 | - "cors": "2.8.4",
101 | + "cors": "^2.8.5",
102 | - "uuid": "3.3.3",
103 | + "uuid": "^3.4.0",
104 | - "ws": "7.1.2",
105 | + "ws": "^7.2.3"
106 | ```
107 |
108 | ### 0.4.0
109 |
110 | * New: Allow passing in custom client ID generation function - #157 Thanks @ajmar
111 |
112 | ### 0.3.2
113 |
114 | * Fixed: fix main field in package.json
115 |
116 | ### 0.3.1
117 |
118 | * Fixed: no expire message in some cases
119 |
120 | ### 0.3.0
121 |
122 | * Convert project to TypeScript 3.7.3.
123 | * Use UUID when generate client id - #152
124 | * Refactoring (add ESLint, split code into small unit) Thanks to @d07RiV @zhou-yg
125 | * Update deps.
126 |
127 | ### 0.2.6
128 |
129 | * Ensure 16 character IDs.
130 |
131 | ### 0.2.5
132 |
133 | * Takes a `path` option, which the peer server will append PeerJS routes to.
134 | * Add support for configurable server IP address.
135 |
136 | ### 0.2.1
137 |
138 | * Added test suite.
139 | * Locked node dependency for restify.
140 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM --platform=$BUILDPLATFORM docker.io/library/node:18.20.8 as build
2 | ARG TARGETPLATFORM
3 | ARG BUILDPLATFORM
4 | RUN mkdir /peer-server
5 | WORKDIR /peer-server
6 | COPY package.json package-lock.json ./
7 | RUN npm clean-install
8 | COPY . ./
9 | RUN npm run build
10 | RUN npm run test
11 |
12 | FROM docker.io/library/node:18.20.8-alpine as production
13 | RUN mkdir /peer-server
14 | WORKDIR /peer-server
15 | COPY package.json package-lock.json ./
16 | RUN npm clean-install --omit=dev
17 | COPY --from=build /peer-server/dist/bin/peerjs.js ./
18 | ENV PORT 9000
19 | EXPOSE ${PORT}
20 | ENTRYPOINT ["node", "peerjs.js"]
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Michelle Bu and Eric Zhang, http://peerjs.com
2 |
3 | (The MIT License)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | "Software"), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/PRIVACY.md:
--------------------------------------------------------------------------------
1 | # Privacy Policy
2 |
3 | **We do not collect or store any information.**
4 |
5 | While you are connected to a PeerJS server, your IP address, randomly-generated
6 | client ID, and signalling data are kept in the server's memory. With default
7 | settings, the server will remove this information from memory 60 seconds after
8 | you stop communicating with the service. (See the
9 | [`alive_timeout`](https://github.com/peers/peerjs-server#config--cli-options)
10 | setting.)
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/peers/peerjs-server)
2 | 
3 | 
4 | [](https://www.npmjs.com/package/peer)
5 | [](https://www.npmjs.com/package/peer)
6 | [](https://hub.docker.com/r/peerjs/peerjs-server)
7 |
8 | # PeerServer: A server for PeerJS
9 |
10 | PeerServer helps establishing connections between PeerJS clients. Data is not proxied through the server.
11 |
12 | Run your own server on Gitpod!
13 |
14 | [](https://gitpod.io/#https://github.com/peers/peerjs-server)
15 |
16 | ### [https://peerjs.com](https://peerjs.com)
17 |
18 | ## Usage
19 |
20 | ### Run server
21 |
22 | #### Natively
23 |
24 | If you don't want to develop anything, just enter few commands below.
25 |
26 | 1. Install the package globally:
27 | ```sh
28 | $ npm install peer -g
29 | ```
30 | 2. Run the server:
31 |
32 | ```sh
33 | $ peerjs --port 9000 --key peerjs --path /myapp
34 |
35 | Started PeerServer on ::, port: 9000, path: /myapp (v. 0.3.2)
36 | ```
37 |
38 | 3. Check it: http://127.0.0.1:9000/myapp It should returns JSON with name, description and website fields.
39 |
40 | #### Docker
41 |
42 | Also, you can use Docker image to run a new container:
43 |
44 | ```sh
45 | $ docker run -p 9000:9000 -d peerjs/peerjs-server
46 | ```
47 |
48 | ##### Kubernetes
49 |
50 | ```sh
51 | $ kubectl run peerjs-server --image=peerjs/peerjs-server --port 9000 --expose -- --port 9000 --path /myapp
52 | ```
53 |
54 | ### Create a custom server:
55 |
56 | If you have your own server, you can attach PeerServer.
57 |
58 | 1. Install the package:
59 |
60 | ```bash
61 | # $ cd your-project-path
62 |
63 | # with npm
64 | $ npm install peer
65 |
66 | # with yarn
67 | $ yarn add peer
68 | ```
69 |
70 | 2. Use PeerServer object to create a new server:
71 |
72 | ```javascript
73 | const { PeerServer } = require("peer");
74 |
75 | const peerServer = PeerServer({ port: 9000, path: "/myapp" });
76 | ```
77 |
78 | 3. Check it: http://127.0.0.1:9000/myapp It should returns JSON with name, description and website fields.
79 |
80 | ### Connecting to the server from client PeerJS:
81 |
82 | ```html
83 |
90 | ```
91 |
92 | ## Config / CLI options
93 |
94 | You can provide config object to `PeerServer` function or specify options for `peerjs` CLI.
95 |
96 | | CLI option | JS option | Description | Required | Default |
97 | | ------------------------ | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | :--------: |
98 | | `--port, -p` | `port` | Port to listen (number) | **Yes** | |
99 | | `--key, -k` | `key` | Connection key (string). Client must provide it to call API methods | No | `"peerjs"` |
100 | | `--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 | `"/"` |
101 | | `--proxied` | `proxied` | Set `true` if PeerServer stays behind a reverse proxy (boolean) | No | `false` |
102 | | `--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` |
103 | | `--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` |
104 | | `--concurrent_limit, -c` | `concurrent_limit` | Maximum number of clients' connections to WebSocket server (number) | No | `5000` |
105 | | `--sslkey` | `sslkey` | Path to SSL key (string) | No | |
106 | | `--sslcert` | `sslcert` | Path to SSL certificate (string) | No | |
107 | | `--allow_discovery` | `allow_discovery` | Allow to use GET `/peers` http API method to get an array of ids of all connected clients (boolean) | No | |
108 | | `--cors` | `corsOptions` | The CORS origins that can access this server |
109 | | | `generateClientId` | A function which generate random client IDs when calling `/id` API method (`() => string`) | No | `uuid/v4` |
110 |
111 | ## Using HTTPS
112 |
113 | Simply pass in PEM-encoded certificate and key.
114 |
115 | ```javascript
116 | const fs = require("fs");
117 | const { PeerServer } = require("peer");
118 |
119 | const peerServer = PeerServer({
120 | port: 9000,
121 | ssl: {
122 | key: fs.readFileSync("/path/to/your/ssl/key/here.key"),
123 | cert: fs.readFileSync("/path/to/your/ssl/certificate/here.crt"),
124 | },
125 | });
126 | ```
127 |
128 | You can also pass any other [SSL options accepted by https.createServer](https://nodejs.org/api/https.html#https_https_createserver_options_requestlistenerfrom), such as `SNICallback:
129 |
130 | ```javascript
131 | const fs = require("fs");
132 | const { PeerServer } = require("peer");
133 |
134 | const peerServer = PeerServer({
135 | port: 9000,
136 | ssl: {
137 | SNICallback: (servername, cb) => {
138 | // your code here ....
139 | },
140 | },
141 | });
142 | ```
143 |
144 | ## Running PeerServer behind a reverse proxy
145 |
146 | Make sure to set the `proxied` option, otherwise IP based limiting will fail.
147 | The option is passed verbatim to the
148 | [expressjs `trust proxy` setting](http://expressjs.com/4x/api.html#app-settings)
149 | if it is truthy.
150 |
151 | ```javascript
152 | const { PeerServer } = require("peer");
153 |
154 | const peerServer = PeerServer({
155 | port: 9000,
156 | path: "/myapp",
157 | proxied: true,
158 | });
159 | ```
160 |
161 | ## Custom client ID generation
162 |
163 | By default, PeerServer uses `uuid/v4` npm package to generate random client IDs.
164 |
165 | You can set `generateClientId` option in config to specify a custom function to generate client IDs.
166 |
167 | ```javascript
168 | const { PeerServer } = require("peer");
169 |
170 | const customGenerationFunction = () =>
171 | (Math.random().toString(36) + "0000000000000000000").substr(2, 16);
172 |
173 | const peerServer = PeerServer({
174 | port: 9000,
175 | path: "/myapp",
176 | generateClientId: customGenerationFunction,
177 | });
178 | ```
179 |
180 | Open http://127.0.0.1:9000/myapp/peerjs/id to see a new random id.
181 |
182 | ## Combining with existing express app
183 |
184 | ```javascript
185 | const express = require("express");
186 | const { ExpressPeerServer } = require("peer");
187 |
188 | const app = express();
189 |
190 | app.get("/", (req, res, next) => res.send("Hello world!"));
191 |
192 | // =======
193 |
194 | const server = app.listen(9000);
195 |
196 | const peerServer = ExpressPeerServer(server, {
197 | path: "/myapp",
198 | });
199 |
200 | app.use("/peerjs", peerServer);
201 |
202 | // == OR ==
203 |
204 | const http = require("http");
205 |
206 | const server = http.createServer(app);
207 | const peerServer = ExpressPeerServer(server, {
208 | debug: true,
209 | path: "/myapp",
210 | });
211 |
212 | app.use("/peerjs", peerServer);
213 |
214 | server.listen(9000);
215 |
216 | // ========
217 | ```
218 |
219 | Open the browser and check http://127.0.0.1:9000/peerjs/myapp
220 |
221 | ## Events
222 |
223 | The `'connection'` event is emitted when a peer connects to the server.
224 |
225 | ```javascript
226 | peerServer.on('connection', (client) => { ... });
227 | ```
228 |
229 | The `'disconnect'` event is emitted when a peer disconnects from the server or
230 | when the peer can no longer be reached.
231 |
232 | ```javascript
233 | peerServer.on('disconnect', (client) => { ... });
234 | ```
235 |
236 | ## HTTP API
237 |
238 | Read [/src/api/README.md](src/api/README.md)
239 |
240 | ## Running tests
241 |
242 | ```sh
243 | $ npm test
244 | ```
245 |
246 | ## Docker
247 |
248 | We have 'ready to use' images on docker hub:
249 | https://hub.docker.com/r/peerjs/peerjs-server
250 |
251 | To run the latest image:
252 |
253 | ```sh
254 | $ docker run -p 9000:9000 -d peerjs/peerjs-server
255 | ```
256 |
257 | You can build a new image simply by calling:
258 |
259 | ```sh
260 | $ docker build -t myimage https://github.com/peers/peerjs-server.git
261 | ```
262 |
263 | To run the image execute this:
264 |
265 | ```sh
266 | $ docker run -p 9000:9000 -d myimage
267 | ```
268 |
269 | This will start a peerjs server on port 9000 exposed on port 9000 with key `peerjs` on path `/myapp`.
270 |
271 | Open your browser with http://localhost:9000/myapp It should returns JSON with name, description and website fields. http://localhost:9000/myapp/peerjs/id - should returns a random string (random client id)
272 |
273 | ## Running in Google App Engine
274 |
275 | Google App Engine will create an HTTPS certificate for you automatically,
276 | making this by far the easiest way to deploy PeerJS in the Google Cloud
277 | Platform.
278 |
279 | 1. Create a `package.json` file for GAE to read:
280 |
281 | ```sh
282 | echo "{}" > package.json
283 | npm install express@latest peer@latest
284 | ```
285 |
286 | 2. Create an `app.yaml` file to configure the GAE application.
287 |
288 | ```yaml
289 | runtime: nodejs
290 |
291 | # Flex environment required for WebSocket support, which is required for PeerJS.
292 | env: flex
293 |
294 | # Limit resources to one instance, one CPU, very little memory or disk.
295 | manual_scaling:
296 | instances: 1
297 | resources:
298 | cpu: 1
299 | memory_gb: 0.5
300 | disk_size_gb: 0.5
301 | ```
302 |
303 | 3. Create `server.js` (which node will run by default for the `start` script):
304 |
305 | ```js
306 | const express = require("express");
307 | const { ExpressPeerServer } = require("peer");
308 | const app = express();
309 |
310 | app.enable("trust proxy");
311 |
312 | const PORT = process.env.PORT || 9000;
313 | const server = app.listen(PORT, () => {
314 | console.log(`App listening on port ${PORT}`);
315 | console.log("Press Ctrl+C to quit.");
316 | });
317 |
318 | const peerServer = ExpressPeerServer(server, {
319 | path: "/",
320 | });
321 |
322 | app.use("/", peerServer);
323 |
324 | module.exports = app;
325 | ```
326 |
327 | 4. Deploy to an existing GAE project (assuming you are already logged in via
328 | `gcloud`), replacing `YOUR-PROJECT-ID-HERE` with your particular project ID:
329 |
330 | ```sh
331 | gcloud app deploy --project=YOUR-PROJECT-ID-HERE --promote --quiet app.yaml
332 | ```
333 |
334 | ## Privacy
335 |
336 | See [PRIVACY.md](https://github.com/peers/peerjs-server/blob/master/PRIVACY.md)
337 |
338 | ## Problems?
339 |
340 | Discuss PeerJS on our Discord community:
341 | https://discord.gg/Ud2PvAtK37
342 |
343 | Please post any bugs as a Github issue.
344 |
--------------------------------------------------------------------------------
/__test__/messageHandler/handlers/heartbeat/index.spec.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from "@jest/globals";
2 |
3 | import { Client } from "../../../../src/models/client.ts";
4 | import { HeartbeatHandler } from "../../../../src/messageHandler/handlers/index.ts";
5 |
6 | describe("Heartbeat handler", () => {
7 | it("should update last ping time", () => {
8 | const client = new Client({ id: "id", token: "" });
9 | client.setLastPing(0);
10 |
11 | const nowTime = new Date().getTime();
12 |
13 | HeartbeatHandler(client);
14 | expect(client.getLastPing()).toBeGreaterThanOrEqual(nowTime - 2);
15 | expect(nowTime).toBeGreaterThanOrEqual(client.getLastPing() - 2);
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/__test__/messageHandler/handlers/transmission/index.spec.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from "@jest/globals";
2 |
3 | import { Client } from "../../../../src/models/client.ts";
4 | import { TransmissionHandler } from "../../../../src/messageHandler/handlers/index.ts";
5 | import { Realm } from "../../../../src/models/realm.ts";
6 | import { MessageType } from "../../../../src/enums.ts";
7 | import type WebSocket from "ws";
8 |
9 | const createFakeSocket = (): WebSocket => {
10 | /* eslint-disable @typescript-eslint/no-empty-function */
11 | const sock = {
12 | send: (): void => {},
13 | close: (): void => {},
14 | on: (): void => {},
15 | };
16 | /* eslint-enable @typescript-eslint/no-empty-function */
17 |
18 | return sock as unknown as WebSocket;
19 | };
20 |
21 | describe("Transmission handler", () => {
22 | it("should save message in queue when destination client not connected", () => {
23 | const realm = new Realm();
24 | const handleTransmission = TransmissionHandler({ realm });
25 |
26 | const clientFrom = new Client({ id: "id1", token: "" });
27 | const idTo = "id2";
28 | realm.setClient(clientFrom, clientFrom.getId());
29 |
30 | handleTransmission(clientFrom, {
31 | type: MessageType.OFFER,
32 | src: clientFrom.getId(),
33 | dst: idTo,
34 | });
35 |
36 | expect(realm.getMessageQueueById(idTo)?.getMessages().length).toBe(1);
37 | });
38 |
39 | it("should not save LEAVE and EXPIRE messages in queue when destination client not connected", () => {
40 | const realm = new Realm();
41 | const handleTransmission = TransmissionHandler({ realm });
42 |
43 | const clientFrom = new Client({ id: "id1", token: "" });
44 | const idTo = "id2";
45 | realm.setClient(clientFrom, clientFrom.getId());
46 |
47 | handleTransmission(clientFrom, {
48 | type: MessageType.LEAVE,
49 | src: clientFrom.getId(),
50 | dst: idTo,
51 | });
52 | handleTransmission(clientFrom, {
53 | type: MessageType.EXPIRE,
54 | src: clientFrom.getId(),
55 | dst: idTo,
56 | });
57 |
58 | expect(realm.getMessageQueueById(idTo)).toBeUndefined();
59 | });
60 |
61 | it("should send message to destination client when destination client connected", () => {
62 | const realm = new Realm();
63 | const handleTransmission = TransmissionHandler({ realm });
64 |
65 | const clientFrom = new Client({ id: "id1", token: "" });
66 | const clientTo = new Client({ id: "id2", token: "" });
67 | const socketTo = createFakeSocket();
68 | clientTo.setSocket(socketTo);
69 | realm.setClient(clientTo, clientTo.getId());
70 |
71 | let sent = false;
72 | socketTo.send = (): void => {
73 | sent = true;
74 | };
75 |
76 | handleTransmission(clientFrom, {
77 | type: MessageType.OFFER,
78 | src: clientFrom.getId(),
79 | dst: clientTo.getId(),
80 | });
81 |
82 | expect(sent).toBe(true);
83 | });
84 |
85 | it("should send LEAVE message to source client when sending to destination client failed", () => {
86 | const realm = new Realm();
87 | const handleTransmission = TransmissionHandler({ realm });
88 |
89 | const clientFrom = new Client({ id: "id1", token: "" });
90 | const clientTo = new Client({ id: "id2", token: "" });
91 | const socketFrom = createFakeSocket();
92 | const socketTo = createFakeSocket();
93 | clientFrom.setSocket(socketFrom);
94 | clientTo.setSocket(socketTo);
95 | realm.setClient(clientFrom, clientFrom.getId());
96 | realm.setClient(clientTo, clientTo.getId());
97 |
98 | let sent = false;
99 | socketFrom.send = (data: string): void => {
100 | if (JSON.parse(data)?.type === MessageType.LEAVE) {
101 | sent = true;
102 | }
103 | };
104 |
105 | socketTo.send = (): void => {
106 | throw Error();
107 | };
108 |
109 | handleTransmission(clientFrom, {
110 | type: MessageType.OFFER,
111 | src: clientFrom.getId(),
112 | dst: clientTo.getId(),
113 | });
114 |
115 | expect(sent).toBe(true);
116 | });
117 | });
118 |
--------------------------------------------------------------------------------
/__test__/messageHandler/handlersRegistry.spec.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from "@jest/globals";
2 |
3 | import { HandlersRegistry } from "../../src/messageHandler/handlersRegistry.ts";
4 | import type { Handler } from "../../src/messageHandler/handler.ts";
5 | import { MessageType } from "../../src/enums.ts";
6 |
7 | describe("HandlersRegistry", () => {
8 | it("should execute handler for message type", () => {
9 | const handlersRegistry = new HandlersRegistry();
10 |
11 | let handled = false;
12 |
13 | const handler: Handler = (): boolean => {
14 | handled = true;
15 | return true;
16 | };
17 |
18 | handlersRegistry.registerHandler(MessageType.OPEN, handler);
19 |
20 | handlersRegistry.handle(undefined, {
21 | type: MessageType.OPEN,
22 | src: "src",
23 | dst: "dst",
24 | });
25 |
26 | expect(handled).toBe(true);
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/__test__/models/messageQueue.spec.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from "@jest/globals";
2 |
3 | import { MessageQueue } from "../../src/models/messageQueue.ts";
4 | import { MessageType } from "../../src/enums.ts";
5 | import type { IMessage } from "../../src/index.js";
6 | import { wait } from "../utils.ts";
7 |
8 | describe("MessageQueue", () => {
9 | const createTestMessage = (): IMessage => {
10 | return {
11 | type: MessageType.OPEN,
12 | src: "src",
13 | dst: "dst",
14 | };
15 | };
16 |
17 | describe("#addMessage", () => {
18 | it("should add message to queue", () => {
19 | const queue = new MessageQueue();
20 | queue.addMessage(createTestMessage());
21 | expect(queue.getMessages().length).toBe(1);
22 | });
23 | });
24 |
25 | describe("#readMessage", () => {
26 | it("should return undefined for empty queue", () => {
27 | const queue = new MessageQueue();
28 | expect(queue.readMessage()).toBeUndefined();
29 | });
30 |
31 | it("should return message if any exists in queue", () => {
32 | const queue = new MessageQueue();
33 | const message = createTestMessage();
34 | queue.addMessage(message);
35 |
36 | expect(queue.readMessage()).toEqual(message);
37 | expect(queue.readMessage()).toBeUndefined();
38 | });
39 | });
40 |
41 | describe("#getLastReadAt", () => {
42 | it("should not be changed if no messages when read", () => {
43 | const queue = new MessageQueue();
44 | const lastReadAt = queue.getLastReadAt();
45 | queue.readMessage();
46 | expect(queue.getLastReadAt()).toBe(lastReadAt);
47 | });
48 |
49 | it("should be changed when read message", async () => {
50 | const queue = new MessageQueue();
51 | const lastReadAt = queue.getLastReadAt();
52 | queue.addMessage(createTestMessage());
53 |
54 | await wait(10);
55 |
56 | expect(queue.getLastReadAt()).toBe(lastReadAt);
57 |
58 | queue.readMessage();
59 |
60 | expect(queue.getLastReadAt()).toBeGreaterThanOrEqual(lastReadAt + 10 - 2);
61 | });
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/__test__/models/realm.spec.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from "@jest/globals";
2 |
3 | import { Realm } from "../../src/models/realm.ts";
4 | import { Client } from "../../src/models/client.ts";
5 |
6 | describe("Realm", () => {
7 | describe("#generateClientId", () => {
8 | it("should generate a 36-character UUID, or return function value", () => {
9 | const realm = new Realm();
10 | expect(realm.generateClientId().length).toBe(36);
11 | expect(realm.generateClientId(() => "abcd")).toBe("abcd");
12 | });
13 | });
14 |
15 | describe("#setClient", () => {
16 | it("should add client to realm", () => {
17 | const realm = new Realm();
18 | const client = new Client({ id: "id", token: "" });
19 |
20 | realm.setClient(client, "id");
21 | expect(realm.getClientsIds()).toEqual(["id"]);
22 | });
23 | });
24 |
25 | describe("#removeClientById", () => {
26 | it("should remove client from realm", () => {
27 | const realm = new Realm();
28 | const client = new Client({ id: "id", token: "" });
29 |
30 | realm.setClient(client, "id");
31 | realm.removeClientById("id");
32 |
33 | expect(realm.getClientById("id")).toBeUndefined();
34 | });
35 | });
36 |
37 | describe("#getClientsIds", () => {
38 | it("should reflects on add/remove childs", () => {
39 | const realm = new Realm();
40 | const client = new Client({ id: "id", token: "" });
41 |
42 | realm.setClient(client, "id");
43 | expect(realm.getClientsIds()).toEqual(["id"]);
44 |
45 | expect(realm.getClientById("id")).toBe(client);
46 |
47 | realm.removeClientById("id");
48 | expect(realm.getClientsIds()).toEqual([]);
49 | });
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/__test__/peerjs.spec.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from "@jest/globals";
2 |
3 | import http from "http";
4 | import expectedJson from "../app.json";
5 | import fetch from "node-fetch";
6 | import * as crypto from "crypto";
7 | import { startServer } from "./utils.ts";
8 |
9 | const PORT = "9000";
10 |
11 | async function makeRequest() {
12 | return new Promise