diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..f1076c3
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,22 @@
+// For format details, see https://aka.ms/devcontainer.json. For config options, see the
+// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
+{
+ "name": "Node.js & TypeScript",
+ // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
+ "image": "mcr.microsoft.com/devcontainers/typescript-node:0-18",
+
+ // Features to add to the dev container. More info: https://containers.dev/features.
+ // "features": {},
+
+ // Use 'forwardPorts' to make a list of ports inside the container available locally.
+ "forwardPorts": [9000],
+
+ // Use 'postCreateCommand' to run commands after the container is created.
+ "postCreateCommand": "npm clean-install"
+
+ // Configure tool-specific properties.
+ // "customizations": {},
+
+ // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
+ // "remoteUser": "root"
+}
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..1853c1a
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,6 @@
+.git
+.nyc_output
+.parcel-cache
+coverage
+dist
+node_modules
\ No newline at end of file
diff --git a/.eslintrc.json b/.eslintrc.json
index d52bcf8..a106a42 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,37 +1,16 @@
{
- "parser": "@typescript-eslint/parser",
- "extends": [
- "eslint:recommended",
- "plugin:@typescript-eslint/eslint-recommended",
- "plugin:@typescript-eslint/recommended"
- ],
- "env": {
- "node": true,
- "es6": true,
- "mocha": true
- },
- "parserOptions": {
- "ecmaVersion": 2018,
- "sourceType": "module"
- },
- "rules": {
- "no-var": "error",
- "no-console": "off",
- "@typescript-eslint/camelcase": "off",
- "@typescript-eslint/interface-name-prefix": "off",
- "@typescript-eslint/member-delimiter-style": [
- "error",
- {
- "multiline": {
- "delimiter": "semi",
- "requireLast": true
- },
- "singleline": {
- "delimiter": "semi",
- "requireLast": true
- }
- }
- ],
- "@typescript-eslint/explicit-function-return-type": "off"
- }
-}
\ No newline at end of file
+ "parser": "@typescript-eslint/parser",
+ "extends": [
+ "eslint:recommended",
+ "plugin:@typescript-eslint/eslint-recommended",
+ "plugin:@typescript-eslint/recommended"
+ ],
+ "env": {
+ "node": true,
+ "es6": true
+ },
+ "parserOptions": {
+ "ecmaVersion": 2018,
+ "sourceType": "module"
+ }
+}
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index ec4bb38..3ba13e0 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1 +1 @@
-blank_issues_enabled: false
\ No newline at end of file
+blank_issues_enabled: false
diff --git a/.github/ISSUE_TEMPLATE/peer-template.md b/.github/ISSUE_TEMPLATE/peer-template.md
index a380cba..c78ccc9 100644
--- a/.github/ISSUE_TEMPLATE/peer-template.md
+++ b/.github/ISSUE_TEMPLATE/peer-template.md
@@ -1,28 +1,30 @@
---
name: peer template
about: Create a report to help us improve
-title: ''
-labels: ''
-assignees: ''
-
+title: ""
+labels: ""
+assignees: ""
---
### I'm having an issue:
- - Give an expressive description of what is went wrong
- - Version of `peer` you're experiencing this issue
- - Nodejs version?
- - Platform name and its version (Win, Mac, Linux)?
- - Nice to have: a repository with code to reproduce the issue
- - If you're getting an error or exception, please provide its full stack-trace as plain-text or screenshot
+
+- Give an expressive description of what is went wrong
+- Version of `peer` you're experiencing this issue
+- Nodejs version?
+- Platform name and its version (Win, Mac, Linux)?
+- Nice to have: a repository with code to reproduce the issue
+- If you're getting an error or exception, please provide its full stack-trace as plain-text or screenshot
### I have a suggestion:
- - Describe your feature / request
- - How you're going to use it? Give a usage example(s)
+
+- Describe your feature / request
+- How you're going to use it? Give a usage example(s)
### Documentation is missing something or incorrect (have typos, etc.):
- - Give an expressive description what you have changed/added and why
- - Make sure you're using correct markdown markup
- - Make sure all code blocks starts with triple ``` (*backtick*) and have a syntax tag, for more read [this docs](https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting)
- - Post addition/changes in issue, we will manage it
+
+- Give an expressive description what you have changed/added and why
+- Make sure you're using correct markdown markup
+- Make sure all code blocks starts with triple ``` (_backtick_) and have a syntax tag, for more read [this docs](https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting)
+- Post addition/changes in issue, we will manage it
## Thank you, and do not forget to get rid of this default message
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
new file mode 100644
index 0000000..f2a1558
--- /dev/null
+++ b/.github/workflows/docker.yml
@@ -0,0 +1,28 @@
+name: Docker build & publish
+
+on:
+ push:
+ branches: ["master"]
+ pull_request:
+ branches: ["master"]
+
+jobs:
+ docker:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+ - name: Login to Docker Hub
+ uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: Build
+ if: ${{ github.event_name == 'pull_request' }}
+ uses: docker/build-push-action@v4
+ - name: Build & publish
+ if: ${{ github.event_name == 'push' }}
+ uses: docker/build-push-action@v4
+ with:
+ push: true
+ tags: peerjs/peerjs-server-test:nightly
diff --git a/.github/workflows/fly.yml b/.github/workflows/fly.yml
new file mode 100644
index 0000000..d75fc5b
--- /dev/null
+++ b/.github/workflows/fly.yml
@@ -0,0 +1,18 @@
+name: Fly Deploy
+
+on:
+ push:
+ branches:
+ - master
+
+env:
+ FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
+
+jobs:
+ deploy:
+ name: Deploy app
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: superfly/flyctl-actions/setup-flyctl@master
+ - run: flyctl deploy --remote-only
diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml
new file mode 100644
index 0000000..ac977df
--- /dev/null
+++ b/.github/workflows/node.js.yml
@@ -0,0 +1,35 @@
+# 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
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
+
+name: Node.js CI
+
+on:
+ push:
+ branches: ["master"]
+ pull_request:
+ branches: ["master"]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ node-version: [14.x, 16.x, 18.x]
+ # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v3
+ with:
+ node-version: ${{ matrix.node-version }}
+ cache: "npm"
+ - run: npm ci
+ - run: npm run build
+ - run: npm run lint
+ - run: npm run coverage
+ - name: Publish code coverage to CodeClimate
+ uses: paambaati/codeclimate-action@v3.2.0
+ env:
+ CC_TEST_REPORTER_ID: ${{secrets.CC_TEST_REPORTER_ID}}
diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml
new file mode 100644
index 0000000..82692e0
--- /dev/null
+++ b/.github/workflows/prettier.yml
@@ -0,0 +1,23 @@
+# From https://til.simonwillison.net/github-actions/prettier-github-actions
+name: Check JavaScript for conformance with Prettier
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ prettier:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out repo
+ uses: actions/checkout@v3
+ - uses: actions/cache@v3
+ name: Configure npm caching
+ with:
+ path: ~/.npm
+ key: ${{ runner.os }}-npm-${{ hashFiles('**/workflows/prettier.yml') }}
+ restore-keys: |
+ ${{ runner.os }}-npm-
+ - name: Run prettier
+ run: |-
+ npx prettier --check .
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 80dd5ca..4575655 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -10,18 +10,20 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Node.js
- uses: actions/setup-node@v2
+ uses: actions/setup-node@v3
with:
node-version: "lts/*"
- name: Install dependencies
run: npm ci
+ - name: Build
+ run: npm run build
- name: Import GPG key
id: import_gpg
- uses: crazy-max/ghaction-import-gpg@v4
+ uses: crazy-max/ghaction-import-gpg@v5
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.GPG_PASSPHRASE }}
@@ -33,4 +35,6 @@ jobs:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
GIT_COMMITTER_NAME: ${{ steps.import_gpg.outputs.name }}
GIT_COMMITTER_EMAIL: ${{ steps.import_gpg.outputs.email }}
+ DOCKER_REGISTRY_USER: ${{ secrets.DOCKERHUB_USERNAME }}
+ DOCKER_REGISTRY_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
run: npx semantic-release
diff --git a/.gitignore b/.gitignore
index 5e5bc23..0b7e0b7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
-lib-cov
+.nyc_output
+coverage
*.seed
*.log
*.csv
@@ -7,6 +8,7 @@ lib-cov
*.pid
*.gz
+.parcel-cache
dist
pids
logs
@@ -17,4 +19,4 @@ npm-debug.log
.idea
.cache
-.vscode
\ No newline at end of file
+.vscode
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..c36c824
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,5 @@
+dist
+coverage
+
+# semantic-release
+CHANGELOG.md
diff --git a/.prettierrc.toml b/.prettierrc.toml
new file mode 100644
index 0000000..437054d
--- /dev/null
+++ b/.prettierrc.toml
@@ -0,0 +1,3 @@
+trailingComma = "all"
+semi = true
+useTabs = true
\ No newline at end of file
diff --git a/.releaserc.json b/.releaserc.json
index 749f4b1..15cd279 100644
--- a/.releaserc.json
+++ b/.releaserc.json
@@ -1,11 +1,31 @@
{
- "branches": ["stable", { "name": "rc", "prerelease": true }],
+ "branches": [
+ "stable",
+ {
+ "name": "rc",
+ "prerelease": true
+ }
+ ],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",
"@semantic-release/git",
- "@semantic-release/github"
+ "@semantic-release/github",
+ [
+ "@codedependant/semantic-release-docker",
+ {
+ "dockerTags": [
+ "{{#if prerelease.[0]}}{{prerelease.[0]}}{{else}}latest{{/if}}",
+ "{{major}}-{{#if prerelease.[0]}}{{prerelease.[0]}}{{else}}latest{{/if}}",
+ "{{major}}.{{minor}}-{{#if prerelease.[0]}}{{prerelease.[0]}}{{else}}latest{{/if}}",
+ "{{version}}"
+ ],
+ "dockerImage": "peerjs-server",
+ "dockerFile": "Dockerfile",
+ "dockerProject": "peerjs"
+ }
+ ]
]
}
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index d71c354..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-language: node_js
-node_js:
- - node
- - lts/*
- - 14
- - 12
- - 10
diff --git a/.whitesource b/.whitesource
new file mode 100644
index 0000000..9c7ae90
--- /dev/null
+++ b/.whitesource
@@ -0,0 +1,14 @@
+{
+ "scanSettings": {
+ "baseBranches": []
+ },
+ "checkRunSettings": {
+ "vulnerableCheckRunConclusionLevel": "failure",
+ "displayMode": "diff",
+ "useMendCheckNames": true
+ },
+ "issueSettings": {
+ "minSeverityLevel": "LOW",
+ "issueType": "DEPENDENCY"
+ }
+}
\ No newline at end of file
diff --git a/changelog.md b/CHANGELOG.md
similarity index 100%
rename from changelog.md
rename to CHANGELOG.md
diff --git a/Dockerfile b/Dockerfile
index eb35e8f..10185ce 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,10 +1,18 @@
-FROM node:alpine
+FROM docker.io/library/node:18.14.2 as build
RUN mkdir /peer-server
WORKDIR /peer-server
-COPY bin ./bin
-COPY dist ./dist
-COPY package.json .
-RUN npm install --production
-EXPOSE 9000
-ENTRYPOINT ["node", "bin/peerjs"]
-CMD [ "--port", "9000", "--path", "/myapp" ]
+COPY package.json package-lock.json ./
+RUN npm clean-install
+COPY . ./
+RUN npm run build
+RUN npm run test
+
+FROM docker.io/library/node:18.14.2-alpine as production
+RUN mkdir /peer-server
+WORKDIR /peer-server
+COPY package.json package-lock.json ./
+RUN npm clean-install --omit=dev
+COPY --from=build /peer-server/dist/bin/peerjs.js ./
+ENV PORT 9000
+EXPOSE ${PORT}
+ENTRYPOINT ["node", "peerjs.js"]
diff --git a/PRIVACY.md b/PRIVACY.md
index 6c4afb1..87ae40a 100644
--- a/PRIVACY.md
+++ b/PRIVACY.md
@@ -3,8 +3,8 @@
**We do not collect or store any information.**
While you are connected to a PeerJS server, your IP address, randomly-generated
-client ID, and signalling data are kept in the server's memory. With default
+client ID, and signalling data are kept in the server's memory. With default
settings, the server will remove this information from memory 60 seconds after
-you stop communicating with the service. (See the
+you stop communicating with the service. (See the
[`alive_timeout`](https://github.com/peers/peerjs-server#config--cli-options)
setting.)
diff --git a/README.md b/README.md
index 0aed177..bec3191 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,8 @@
[](https://www.npmjs.com/package/peer)
[](https://www.npmjs.com/package/peer)
[](https://hub.docker.com/r/peerjs/peerjs-server)
-# PeerServer: A server for PeerJS #
+
+# PeerServer: A server for PeerJS
PeerServer helps establishing connections between PeerJS clients. Data is not proxied through the server.
@@ -23,20 +24,23 @@ Run your own server on Gitpod!
If you don't want to develop anything, just enter few commands below.
1. Install the package globally:
- ```sh
- $ npm install peer -g
- ```
+ ```sh
+ $ npm install peer -g
+ ```
2. Run the server:
- ```sh
- $ peerjs --port 9000 --key peerjs --path /myapp
- Started PeerServer on ::, port: 9000, path: /myapp (v. 0.3.2)
- ```
+ ```sh
+ $ peerjs --port 9000 --key peerjs --path /myapp
+
+ Started PeerServer on ::, port: 9000, path: /myapp (v. 0.3.2)
+ ```
+
3. Check it: http://127.0.0.1:9000/myapp It should returns JSON with name, description and website fields.
#### Docker
Also, you can use Docker image to run a new container:
+
```sh
$ docker run -p 9000:9000 -d peerjs/peerjs-server
```
@@ -48,24 +52,28 @@ $ kubectl run peerjs-server --image=peerjs/peerjs-server --port 9000 --expose --
```
### Create a custom server:
+
If you have your own server, you can attach PeerServer.
1. Install the package:
- ```bash
- # $ cd your-project-path
-
- # with npm
- $ npm install peer
-
- # with yarn
- $ yarn add peer
- ```
-2. Use PeerServer object to create a new server:
- ```javascript
- const { PeerServer } = require('peer');
- const peerServer = PeerServer({ port: 9000, path: '/myapp' });
- ```
+ ```bash
+ # $ cd your-project-path
+
+ # with npm
+ $ npm install peer
+
+ # with yarn
+ $ yarn add peer
+ ```
+
+2. Use PeerServer object to create a new server:
+
+ ```javascript
+ const { PeerServer } = require("peer");
+
+ const peerServer = PeerServer({ port: 9000, path: "/myapp" });
+ ```
3. Check it: http://127.0.0.1:9000/myapp It should returns JSON with name, description and website fields.
@@ -73,64 +81,66 @@ If you have your own server, you can attach PeerServer.
```html
```
## 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.
```javascript
-const fs = require('fs');
-const { PeerServer } = require('peer');
+const fs = require("fs");
+const { PeerServer } = require("peer");
const peerServer = PeerServer({
- port: 9000,
- ssl: {
- key: fs.readFileSync('/path/to/your/ssl/key/here.key'),
- cert: fs.readFileSync('/path/to/your/ssl/certificate/here.crt')
- }
+ port: 9000,
+ ssl: {
+ key: fs.readFileSync("/path/to/your/ssl/key/here.key"),
+ cert: fs.readFileSync("/path/to/your/ssl/certificate/here.crt"),
+ },
});
```
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:
```javascript
-const fs = require('fs');
-const { PeerServer } = require('peer');
+const fs = require("fs");
+const { PeerServer } = require("peer");
const peerServer = PeerServer({
- port: 9000,
- ssl: {
- SNICallback: (servername, cb) => {
- // your code here ....
- }
- }
+ port: 9000,
+ ssl: {
+ SNICallback: (servername, cb) => {
+ // your code here ....
+ },
+ },
});
```
-
## Running PeerServer behind a reverse proxy
Make sure to set the `proxied` option, otherwise IP based limiting will fail.
@@ -139,29 +149,31 @@ The option is passed verbatim to the
if it is truthy.
```javascript
-const { PeerServer } = require('peer');
+const { PeerServer } = require("peer");
const peerServer = PeerServer({
- port: 9000,
- path: '/myapp',
- proxied: true
+ port: 9000,
+ path: "/myapp",
+ proxied: true,
});
```
## Custom client ID generation
+
By default, PeerServer uses `uuid/v4` npm package to generate random client IDs.
You can set `generateClientId` option in config to specify a custom function to generate client IDs.
```javascript
-const { PeerServer } = require('peer');
+const { PeerServer } = require("peer");
-const customGenerationFunction = () => (Math.random().toString(36) + '0000000000000000000').substr(2, 16);
+const customGenerationFunction = () =>
+ (Math.random().toString(36) + "0000000000000000000").substr(2, 16);
const peerServer = PeerServer({
- port: 9000,
- path: '/myapp',
- generateClientId: customGenerationFunction
+ port: 9000,
+ path: "/myapp",
+ generateClientId: customGenerationFunction,
});
```
@@ -170,34 +182,34 @@ Open http://127.0.0.1:9000/myapp/peerjs/id to see a new random id.
## Combining with existing express app
```javascript
-const express = require('express');
-const { ExpressPeerServer } = require('peer');
+const express = require("express");
+const { ExpressPeerServer } = require("peer");
const app = express();
-app.get('/', (req, res, next) => res.send('Hello world!'));
+app.get("/", (req, res, next) => res.send("Hello world!"));
// =======
const server = app.listen(9000);
const peerServer = ExpressPeerServer(server, {
- path: '/myapp'
+ path: "/myapp",
});
-app.use('/peerjs', peerServer);
+app.use("/peerjs", peerServer);
// == OR ==
-const http = require('http');
+const http = require("http");
const server = http.createServer(app);
const peerServer = ExpressPeerServer(server, {
- debug: true,
- path: '/myapp'
+ debug: true,
+ path: "/myapp",
});
-app.use('/peerjs', peerServer);
+app.use("/peerjs", peerServer);
server.listen(9000);
@@ -236,18 +248,20 @@ $ npm test
We have 'ready to use' images on docker hub:
https://hub.docker.com/r/peerjs/peerjs-server
+To run the latest image:
-To run the latest image:
```sh
$ docker run -p 9000:9000 -d peerjs/peerjs-server
```
You can build a new image simply by calling:
+
```sh
$ docker build -t myimage https://github.com/peers/peerjs-server.git
```
-To run the image execute this:
+To run the image execute this:
+
```sh
$ docker run -p 9000:9000 -d myimage
```
@@ -289,29 +303,29 @@ resources:
3. Create `server.js` (which node will run by default for the `start` script):
```js
-const express = require('express');
-const { ExpressPeerServer } = require('peer');
+const express = require("express");
+const { ExpressPeerServer } = require("peer");
const app = express();
-app.enable('trust proxy');
+app.enable("trust proxy");
const PORT = process.env.PORT || 9000;
const server = app.listen(PORT, () => {
- console.log(`App listening on port ${PORT}`);
- console.log('Press Ctrl+C to quit.');
+ console.log(`App listening on port ${PORT}`);
+ console.log("Press Ctrl+C to quit.");
});
const peerServer = ExpressPeerServer(server, {
- path: '/'
+ path: "/",
});
-app.use('/', peerServer);
+app.use("/", peerServer);
module.exports = app;
```
4. Deploy to an existing GAE project (assuming you are already logged in via
-`gcloud`), replacing `YOUR-PROJECT-ID-HERE` with your particular project ID:
+ `gcloud`), replacing `YOUR-PROJECT-ID-HERE` with your particular project ID:
```sh
gcloud app deploy --project=YOUR-PROJECT-ID-HERE --promote --quiet app.yaml
diff --git a/__test__/messageHandler/handlers/heartbeat/index.spec.ts b/__test__/messageHandler/handlers/heartbeat/index.spec.ts
new file mode 100644
index 0000000..cddfca8
--- /dev/null
+++ b/__test__/messageHandler/handlers/heartbeat/index.spec.ts
@@ -0,0 +1,17 @@
+import { describe, expect, it } from "@jest/globals";
+
+import { Client } from "../../../../src/models/client";
+import { HeartbeatHandler } from "../../../../src/messageHandler/handlers";
+
+describe("Heartbeat handler", () => {
+ it("should update last ping time", () => {
+ const client = new Client({ id: "id", token: "" });
+ client.setLastPing(0);
+
+ const nowTime = new Date().getTime();
+
+ HeartbeatHandler(client);
+ expect(client.getLastPing()).toBeGreaterThanOrEqual(nowTime - 2);
+ expect(nowTime).toBeGreaterThanOrEqual(client.getLastPing() - 2);
+ });
+});
diff --git a/__test__/messageHandler/handlers/transmission/index.spec.ts b/__test__/messageHandler/handlers/transmission/index.spec.ts
new file mode 100644
index 0000000..b26276f
--- /dev/null
+++ b/__test__/messageHandler/handlers/transmission/index.spec.ts
@@ -0,0 +1,117 @@
+import { describe, expect, it } from "@jest/globals";
+
+import { Client } from "../../../../src/models/client";
+import { TransmissionHandler } from "../../../../src/messageHandler/handlers";
+import { Realm } from "../../../../src/models/realm";
+import { MessageType } from "../../../../src/enums";
+import type WebSocket from "ws";
+
+const createFakeSocket = (): WebSocket => {
+ /* eslint-disable @typescript-eslint/no-empty-function */
+ const sock = {
+ send: (): void => {},
+ close: (): void => {},
+ on: (): void => {},
+ };
+ /* eslint-enable @typescript-eslint/no-empty-function */
+
+ return sock as unknown as WebSocket;
+};
+
+describe("Transmission handler", () => {
+ it("should save message in queue when destination client not connected", () => {
+ const realm = new Realm();
+ const handleTransmission = TransmissionHandler({ realm });
+
+ const clientFrom = new Client({ id: "id1", token: "" });
+ const idTo = "id2";
+ realm.setClient(clientFrom, clientFrom.getId());
+
+ handleTransmission(clientFrom, {
+ type: MessageType.OFFER,
+ src: clientFrom.getId(),
+ dst: idTo,
+ });
+
+ expect(realm.getMessageQueueById(idTo)?.getMessages().length).toBe(1);
+ });
+
+ it("should not save LEAVE and EXPIRE messages in queue when destination client not connected", () => {
+ const realm = new Realm();
+ const handleTransmission = TransmissionHandler({ realm });
+
+ const clientFrom = new Client({ id: "id1", token: "" });
+ const idTo = "id2";
+ realm.setClient(clientFrom, clientFrom.getId());
+
+ handleTransmission(clientFrom, {
+ type: MessageType.LEAVE,
+ src: clientFrom.getId(),
+ dst: idTo,
+ });
+ handleTransmission(clientFrom, {
+ type: MessageType.EXPIRE,
+ src: clientFrom.getId(),
+ dst: idTo,
+ });
+
+ expect(realm.getMessageQueueById(idTo)).toBeUndefined();
+ });
+
+ it("should send message to destination client when destination client connected", () => {
+ const realm = new Realm();
+ const handleTransmission = TransmissionHandler({ realm });
+
+ const clientFrom = new Client({ id: "id1", token: "" });
+ const clientTo = new Client({ id: "id2", token: "" });
+ const socketTo = createFakeSocket();
+ clientTo.setSocket(socketTo);
+ realm.setClient(clientTo, clientTo.getId());
+
+ let sent = false;
+ socketTo.send = (): void => {
+ sent = true;
+ };
+
+ handleTransmission(clientFrom, {
+ type: MessageType.OFFER,
+ src: clientFrom.getId(),
+ dst: clientTo.getId(),
+ });
+
+ expect(sent).toBe(true);
+ });
+
+ it("should send LEAVE message to source client when sending to destination client failed", () => {
+ const realm = new Realm();
+ const handleTransmission = TransmissionHandler({ realm });
+
+ const clientFrom = new Client({ id: "id1", token: "" });
+ const clientTo = new Client({ id: "id2", token: "" });
+ const socketFrom = createFakeSocket();
+ const socketTo = createFakeSocket();
+ clientFrom.setSocket(socketFrom);
+ clientTo.setSocket(socketTo);
+ realm.setClient(clientFrom, clientFrom.getId());
+ realm.setClient(clientTo, clientTo.getId());
+
+ let sent = false;
+ socketFrom.send = (data: string): void => {
+ if (JSON.parse(data)?.type === MessageType.LEAVE) {
+ sent = true;
+ }
+ };
+
+ socketTo.send = (): void => {
+ throw Error();
+ };
+
+ handleTransmission(clientFrom, {
+ type: MessageType.OFFER,
+ src: clientFrom.getId(),
+ dst: clientTo.getId(),
+ });
+
+ expect(sent).toBe(true);
+ });
+});
diff --git a/__test__/messageHandler/handlersRegistry.spec.ts b/__test__/messageHandler/handlersRegistry.spec.ts
new file mode 100644
index 0000000..74e36a6
--- /dev/null
+++ b/__test__/messageHandler/handlersRegistry.spec.ts
@@ -0,0 +1,28 @@
+import { describe, expect, it } from "@jest/globals";
+
+import { HandlersRegistry } from "../../src/messageHandler/handlersRegistry";
+import type { Handler } from "../../src/messageHandler/handler";
+import { MessageType } from "../../src/enums";
+
+describe("HandlersRegistry", () => {
+ it("should execute handler for message type", () => {
+ const handlersRegistry = new HandlersRegistry();
+
+ let handled = false;
+
+ const handler: Handler = (): boolean => {
+ handled = true;
+ return true;
+ };
+
+ handlersRegistry.registerHandler(MessageType.OPEN, handler);
+
+ handlersRegistry.handle(undefined, {
+ type: MessageType.OPEN,
+ src: "src",
+ dst: "dst",
+ });
+
+ expect(handled).toBe(true);
+ });
+});
diff --git a/__test__/models/messageQueue.spec.ts b/__test__/models/messageQueue.spec.ts
new file mode 100644
index 0000000..28ce922
--- /dev/null
+++ b/__test__/models/messageQueue.spec.ts
@@ -0,0 +1,63 @@
+import { describe, expect, it } from "@jest/globals";
+
+import { MessageQueue } from "../../src/models/messageQueue";
+import { MessageType } from "../../src/enums";
+import type { IMessage } from "../../src/models/message";
+import { wait } from "../utils";
+
+describe("MessageQueue", () => {
+ const createTestMessage = (): IMessage => {
+ return {
+ type: MessageType.OPEN,
+ src: "src",
+ dst: "dst",
+ };
+ };
+
+ describe("#addMessage", () => {
+ it("should add message to queue", () => {
+ const queue = new MessageQueue();
+ queue.addMessage(createTestMessage());
+ expect(queue.getMessages().length).toBe(1);
+ });
+ });
+
+ describe("#readMessage", () => {
+ it("should return undefined for empty queue", () => {
+ const queue = new MessageQueue();
+ expect(queue.readMessage()).toBeUndefined();
+ });
+
+ it("should return message if any exists in queue", () => {
+ const queue = new MessageQueue();
+ const message = createTestMessage();
+ queue.addMessage(message);
+
+ expect(queue.readMessage()).toEqual(message);
+ expect(queue.readMessage()).toBeUndefined();
+ });
+ });
+
+ describe("#getLastReadAt", () => {
+ it("should not be changed if no messages when read", () => {
+ const queue = new MessageQueue();
+ const lastReadAt = queue.getLastReadAt();
+ queue.readMessage();
+ expect(queue.getLastReadAt()).toBe(lastReadAt);
+ });
+
+ it("should be changed when read message", async () => {
+ const queue = new MessageQueue();
+ const lastReadAt = queue.getLastReadAt();
+ queue.addMessage(createTestMessage());
+
+ await wait(10);
+
+ expect(queue.getLastReadAt()).toBe(lastReadAt);
+
+ queue.readMessage();
+
+ expect(queue.getLastReadAt()).toBeGreaterThanOrEqual(lastReadAt + 10 - 2);
+ });
+ });
+});
diff --git a/__test__/models/realm.spec.ts b/__test__/models/realm.spec.ts
new file mode 100644
index 0000000..207f939
--- /dev/null
+++ b/__test__/models/realm.spec.ts
@@ -0,0 +1,51 @@
+import { describe, expect, it } from "@jest/globals";
+
+import { Realm } from "../../src/models/realm";
+import { Client } from "../../src/models/client";
+
+describe("Realm", () => {
+ describe("#generateClientId", () => {
+ it("should generate a 36-character UUID, or return function value", () => {
+ const realm = new Realm();
+ expect(realm.generateClientId().length).toBe(36);
+ expect(realm.generateClientId(() => "abcd")).toBe("abcd");
+ });
+ });
+
+ describe("#setClient", () => {
+ it("should add client to realm", () => {
+ const realm = new Realm();
+ const client = new Client({ id: "id", token: "" });
+
+ realm.setClient(client, "id");
+ expect(realm.getClientsIds()).toEqual(["id"]);
+ });
+ });
+
+ describe("#removeClientById", () => {
+ it("should remove client from realm", () => {
+ const realm = new Realm();
+ const client = new Client({ id: "id", token: "" });
+
+ realm.setClient(client, "id");
+ realm.removeClientById("id");
+
+ expect(realm.getClientById("id")).toBeUndefined();
+ });
+ });
+
+ describe("#getClientsIds", () => {
+ it("should reflects on add/remove childs", () => {
+ const realm = new Realm();
+ const client = new Client({ id: "id", token: "" });
+
+ realm.setClient(client, "id");
+ expect(realm.getClientsIds()).toEqual(["id"]);
+
+ expect(realm.getClientById("id")).toBe(client);
+
+ realm.removeClientById("id");
+ expect(realm.getClientsIds()).toEqual([]);
+ });
+ });
+});
diff --git a/__test__/peerjs.spec.ts b/__test__/peerjs.spec.ts
new file mode 100644
index 0000000..9b97e62
--- /dev/null
+++ b/__test__/peerjs.spec.ts
@@ -0,0 +1,91 @@
+import { describe, expect, it } from "@jest/globals";
+
+import http from "http";
+import expectedJson from "../app.json";
+import fetch from "node-fetch";
+import * as crypto from "crypto";
+import { startServer } from "./utils";
+
+const PORT = "9000";
+
+async function makeRequest() {
+ return new Promise