├── .eslintrc ├── .github ├── dependabot.yml └── workflows │ └── node.js.yml ├── .gitignore ├── CHANGELOG.md ├── Containerfile ├── LICENCE ├── README.md ├── examples ├── .eslintrc └── simple.js ├── index.js ├── package-lock.json ├── package.json └── test ├── .eslintrc ├── cert.pem ├── key.pem └── test.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "es6": true 5 | }, 6 | "rules": { 7 | "quotes": [2, "single"], 8 | "no-param-reassign": ["error", { "props": false }] 9 | }, 10 | "extends": "airbnb" 11 | } 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | open-pull-requests-limit: 10 8 | assignees: 9 | - kudos 10 | -------------------------------------------------------------------------------- /.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://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [14.x, 16.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: 'npm' 29 | - run: npm ci 30 | - run: npm run lint 31 | - run: npm run test 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | .DS_Store 3 | .AppleDouble -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | Note: koa-websocket follows semver. 4 | 5 | ## v6.0.0 6 | 7 | * Upgrade ws to 7.0.1, which drops support for Node 6. 8 | * Upgrade dev deps to fix a vuln with js-yaml. 9 | 10 | ## v5.0.1 11 | 12 | * Remove accidental addition of npm from dependencies 13 | 14 | ## v5.0.0 15 | 16 | * Upgraded all dependencies, including for ws to fix some security issues. 17 | 18 | ## v4.1.0 19 | 20 | * Added support for passing https server options, enabling wss support - @stripedpajamas 21 | 22 | ## v4.0.0 23 | 24 | * Switch Koa v2 support to master, and moved v1 to the legacy branch. 25 | * Added ability to set websocket options. 26 | * Upgraded to websocket 2.x. 27 | 28 | ## v3.0.1 29 | 30 | Because NPM wouldn't let me retroactively tag 3.0.0 as `next` instead of latest. 31 | 32 | ## v3.0.0 33 | 34 | This is a pre-release for compatibility with Koa 2. Doing `npm install koa-websocket` will continue to give you 2.x until Koa 2 is stable. 35 | 36 | ### Improvements 37 | 38 | * Koa 2 comptiblity. 39 | 40 | ## v2.0.0 41 | 42 | ### Improvements 43 | 44 | * Bump ws major for DoS fix. 45 | 46 | ## v1.1.0 47 | 48 | ### Improvements 49 | 50 | * Bump ws for iojs 3. 51 | 52 | ## v1.0.0 53 | 54 | ### Improvements 55 | 56 | * Expose koa context object instead of socket as `this`, attach active ws socket to context object on `websocket`. 57 | 58 | ### Bugs 59 | 60 | * Fix ws urls containing query parameters. 61 | -------------------------------------------------------------------------------- /Containerfile: -------------------------------------------------------------------------------- 1 | FROM node:16 2 | 3 | WORKDIR /app 4 | 5 | CMD ["npm", "run", "test"] 6 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2018 Jonathan Cremin 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 NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # koa-websocket 2 | 3 | [![CI Status](https://github.com/kudos/koa-websocket/actions/workflows/node.js.yml/badge.svg)](https://github.com/kudos/koa-websocket/actions) 4 | 5 | > Koa v2 is now the default. For Koa v1 support install with koa-websocket@2 and see the `legacy` branch. 6 | 7 | Supports `ws://` and `wss://` 8 | 9 | ## Installation 10 | 11 | `npm install koa-websocket` 12 | 13 | ## Usage 14 | 15 | See examples directory for a simple implementation. 16 | 17 | ### Let's Encrypt 18 | 19 | Example with Let's Encrypt ([the Greenlock package](https://git.daplie.com/Daplie/greenlock-koa)): 20 | 21 | ```js 22 | const Koa = require('koa'); 23 | const greenlock = require('greenlock-express'); 24 | const websockify = require('koa-websocket'); 25 | 26 | const le = greenlock.create({ 27 | // all your sweet Let's Encrypt options here 28 | }); 29 | 30 | // the magic happens right here 31 | const app = websockify(new Koa(), wsOptions, le.httpsOptions); 32 | 33 | app.ws.use((ctx) => { 34 | // the websocket is added to the context as `ctx.websocket`. 35 | ctx.websocket.on('message', function(message) { 36 | // do something 37 | }); 38 | }); 39 | 40 | app.listen(3000); 41 | ``` 42 | 43 | ## API 44 | #### websockify(KoaApp, [WebSocketOptions], [httpsOptions]) 45 | The WebSocket options object just get passed right through to the `new WebSocketServer(options)` call. 46 | 47 | The optional HTTPS options object gets passed right into `https.createServer(options)`. If the HTTPS options are 48 | passed in, koa-websocket will use the built-in Node HTTPS server to provide support for the `wss://` protocol. 49 | 50 | ## License 51 | MIT 52 | -------------------------------------------------------------------------------- /examples/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "import/no-extraneous-dependencies": 0, 4 | "import/no-unresolved": 0, 5 | "no-console": 0, 6 | "arrow-body-style": 0, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /examples/simple.js: -------------------------------------------------------------------------------- 1 | const Koa = require('koa'); 2 | const Router = require('@koa/router'); 3 | const websockify = require('..'); 4 | 5 | const app = websockify(new Koa()); 6 | 7 | const wsRouter = Router(); 8 | const router = Router(); 9 | 10 | // Regular middleware 11 | wsRouter.use((ctx, next) => { 12 | // return `next` to pass the context (ctx) on to the next ws middleware 13 | return next(ctx); 14 | }); 15 | 16 | // Using routes 17 | wsRouter.get('/', async (ctx, next) => { 18 | // `ctx` is the regular koa context created from the `ws` onConnection `socket.upgradeReq` object. 19 | // the websocket is added to the context on `ctx.websocket`. 20 | ctx.websocket.send('Hello World'); 21 | ctx.websocket.on('message', (message) => { 22 | // do something with the message from client 23 | if (message.toString() === 'ping') { 24 | ctx.websocket.send('pong'); 25 | } 26 | }); 27 | return next; 28 | }); 29 | 30 | // A normal http route to set up a basic ws test 31 | router.get('/', async (ctx, next) => { 32 | ctx.body = `Check the network inspector!`; 36 | return next; 37 | }); 38 | 39 | // Attach both routers 40 | // Note it's app.ws.use for our ws router 41 | app.ws.use(wsRouter.routes()).use(wsRouter.allowedMethods()); 42 | app.use(router.routes()).use(router.allowedMethods()); 43 | 44 | app.listen(3000); 45 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const url = require('url'); 2 | const https = require('https'); 3 | const compose = require('koa-compose'); 4 | const co = require('co'); 5 | const ws = require('ws'); 6 | 7 | const WebSocketServer = ws.Server; 8 | const debug = require('debug')('koa:websockets'); 9 | 10 | function KoaWebSocketServer(app) { 11 | this.app = app; 12 | this.middleware = []; 13 | } 14 | 15 | KoaWebSocketServer.prototype.listen = function listen(options) { 16 | this.server = new WebSocketServer(options); 17 | this.server.on('connection', this.onConnection.bind(this)); 18 | }; 19 | 20 | KoaWebSocketServer.prototype.onConnection = function onConnection(socket, req) { 21 | debug('Connection received'); 22 | socket.on('error', (err) => { 23 | debug('Error occurred:', err); 24 | }); 25 | const fn = co.wrap(compose(this.middleware)); 26 | 27 | const context = this.app.createContext(req); 28 | context.websocket = socket; 29 | context.path = url.parse(req.url).pathname; 30 | 31 | fn(context).catch((err) => { 32 | debug(err); 33 | }); 34 | }; 35 | 36 | KoaWebSocketServer.prototype.use = function use(fn) { 37 | this.middleware.push(fn); 38 | return this; 39 | }; 40 | 41 | module.exports = function middleware(app, wsOptions, httpsOptions) { 42 | const oldListen = app.listen; 43 | app.listen = function listen(...args) { 44 | debug('Attaching server...'); 45 | if (typeof httpsOptions === 'object') { 46 | const httpsServer = https.createServer(httpsOptions, app.callback()); 47 | app.server = httpsServer.listen(...args); 48 | } else { 49 | app.server = oldListen.apply(app, args); 50 | } 51 | const options = { server: app.server }; 52 | if (wsOptions) { 53 | Object.keys(wsOptions).forEach((key) => { 54 | if (Object.prototype.hasOwnProperty.call(wsOptions, key)) { 55 | options[key] = wsOptions[key]; 56 | } 57 | }); 58 | } 59 | app.ws.listen(options); 60 | return app.server; 61 | }; 62 | app.ws = new KoaWebSocketServer(app); 63 | return app; 64 | }; 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa-websocket", 3 | "version": "7.0.0", 4 | "description": "Light wrapper around Koa providing a websocket middleware handler that is koa-router compatible.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha $NODE_FLAGS test/test.js", 8 | "lint": "eslint ." 9 | }, 10 | "keywords": [ 11 | "koa", 12 | "websockets", 13 | "ws", 14 | "sockets", 15 | "routes" 16 | ], 17 | "author": { 18 | "name": "Jonathan Cremin", 19 | "email": "jonathan@crem.in", 20 | "url": "https://crem.in", 21 | "twitter": "https://twitter.com/kudoz" 22 | }, 23 | "repository": "git://github.com/kudos/koa-websocket", 24 | "bugs": "https://github.com/kudos/koa-websocket/issues", 25 | "homepage": "https://github.com/kudos/koa-websocket", 26 | "license": "MIT", 27 | "dependencies": { 28 | "co": "^4.6.0", 29 | "debug": "^4.3.4", 30 | "koa-compose": "^4.1.0", 31 | "ws": "^8.5.0" 32 | }, 33 | "devDependencies": { 34 | "eslint": "^8.13.0", 35 | "eslint-config-airbnb": "^19.0.4", 36 | "eslint-plugin-import": "^2.26.0", 37 | "eslint-plugin-jsx-a11y": "^6.5.1", 38 | "eslint-plugin-react": "^7.29.4", 39 | "koa": "^2.13.4", 40 | "@koa/router": "^10.1.1", 41 | "koa-route": "^3.2.0", 42 | "mocha": "^9.2.2" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEpDCCAowCCQDmCPfGdjaoRjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls 3 | b2NhbGhvc3QwHhcNMTcxMTA1MjIxNjExWhcNNDUwMzIzMjIxNjExWjAUMRIwEAYD 4 | VQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDH 5 | aoAG7gq9iW0LARjU+2gWlFojbHNDwhphOiXdFiAN5Y5GWerfVvwbHLLgIWcW1Qqf 6 | eLAWQsgO0g41FYXCzMni26qThh+xVZLYN0gE7Ei/u/JMANV0cGAmTxOKe5IumQxB 7 | czVEYtwsrYq7MxLxTtHl4DY4yq7wj2BwgyX2zND3Z2qEB2gi90T8TCQ3FcKjXh0Z 8 | bHGVJhswIarZNVYeZO+qLu+S+Cx48esEVmVA58aqmx1Sn0+CyY2vGzj6cMnUoDGR 9 | yHP4TwhjsWy4OzVTxMC8I8n2qSVWkEa0V0LseQLSYrugogXv/9Nr1RyoO6BQBUWu 10 | KHv3yt+p8eHzN8ZgyoBdtTMVeIvwG+KhhKbatwtzaw37D9VFkO+Ba5hH3ERBKfIw 11 | OW+FKgemtrGlyqKN2WjytrwiJ/DqiIs1m2n1d4kifrP5YB8u0k4FADRKzWFZhH2/ 12 | OBWUqOJf1/kN3joJTq7/eXyzCUL7sBxVAREFTnK1uIiviJtz/324VU1haBdqFcqq 13 | 3SOaHWatw9j5KPkIEOZTLa+s17IFli0IQmx77Si6xAnnDrpeJJuL4cKYcZZJXZIb 14 | /LAteL2YN8vjkHYPYyLZJMVLzDpMP/FH16OtzNZKA1+JEkP8ZVR1nMLESuYp5mTj 15 | 8lcn+VE6n3K1nv1thsEmepEGaeB5YbG4fxRe7r/inwIDAQABMA0GCSqGSIb3DQEB 16 | CwUAA4ICAQCfFNl2+PaWtHAJyaLLr9BrJ5l8ThPoEn90eNT1CqdCwZHyrvfq8EKF 17 | IfRh4BEwrFzL1iwrsEULnRekY/OrP5a2xFlr+yyQyjsrp6f6I3P1HZuHUVwUT0Mq 18 | MBzM5OzjtmqFw779lfWwP/nfiECFM37Ww/61e0dvK7JLwFTp/JRDx1TRJ1cu2XEA 19 | yTwkpfGh+wZH/EVuWYlHJ01DI6wffQoC/OSls3VNLg3AOWTsn9pRHnMns3qk75Uo 20 | cizxukJh8BZ9JayyxulVq6TLS2ZN8BN+LEnt6FO4aNRcJMrLE/qRvqFwuQEWl5GH 21 | eT3ZPFP/Ke78veR8ho0ugmelomXc8amuG8Eznd3mu8y2GwRkX3JsoF1gGkn14s4y 22 | G35kExzSLWF4gESNow4sDyAJUA1WUsOcWam41g2/zlCaeQwYk9AOTngSD+geL1fp 23 | A7l/oXScLgG3u+nnyNPS4Rg93RJTkhLS8xwiH7O0NtBoBh1kujinmOmg1DpNQm1Z 24 | bL8mTawQYpBn14/LddwVVBQ+JiDxzx9UkdpZG3RojSAFgu29LMV8jhV3n6aHxLot 25 | D1fTfMB+9Ne4MwxXIpdTgrgp33Km5fCAc4BqrtGaTAqhSb4xdZLL0rmwSXHFrZbH 26 | JaQlrhjR+aCWengYHSH3tVFBKzOc11BFPrPcCh7Enrz3tXoeI9+h3g== 27 | -----END CERTIFICATE----- 28 | -------------------------------------------------------------------------------- /test/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | 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 51 | /P3VmpFPGShIjwe+rBxztErM5GihgNA= 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const fs = require('fs'); 3 | const WebSocket = require('ws'); 4 | const Koa = require('koa'); 5 | const route = require('koa-route'); 6 | 7 | const koaws = require('..'); 8 | 9 | describe('should route ws messages seperately', () => { 10 | const app = koaws(new Koa()); 11 | 12 | app.ws.use((ctx, next) => { 13 | ctx.websocket.on('message', (message) => { 14 | if (message.toString() === '123') { 15 | ctx.websocket.send(message); 16 | } 17 | }); 18 | return next(); 19 | }); 20 | 21 | app.ws.use(route.all('/abc', (ctx) => { 22 | ctx.websocket.on('message', (message) => { 23 | ctx.websocket.send(message); 24 | }); 25 | })); 26 | 27 | app.ws.use(route.all('/def', (ctx) => { 28 | ctx.websocket.on('message', (message) => { 29 | ctx.websocket.send(message); 30 | }); 31 | })); 32 | 33 | let server = null; 34 | 35 | before((done) => { 36 | server = app.listen(done); 37 | }); 38 | 39 | after((done) => { 40 | server.close(done); 41 | }); 42 | 43 | it('sends 123 message to any route', (done) => { 44 | const ws = new WebSocket(`ws://localhost:${server.address().port}/not-a-route`); 45 | ws.on('open', () => { 46 | ws.send('123'); 47 | }); 48 | ws.on('message', (data) => { 49 | assert(data.toString() === '123'); 50 | done(); 51 | ws.close(); 52 | }); 53 | }); 54 | 55 | it('sends abc message to abc route', (done) => { 56 | const ws = new WebSocket(`ws://localhost:${server.address().port}/abc`); 57 | ws.on('open', () => { 58 | ws.send('abc'); 59 | }); 60 | ws.on('message', (data) => { 61 | assert(data.toString() === 'abc'); 62 | done(); 63 | ws.close(); 64 | }); 65 | }); 66 | 67 | it('sends def message to def route', (done) => { 68 | const ws = new WebSocket(`ws://localhost:${server.address().port}/def`); 69 | ws.on('open', () => { 70 | ws.send('def'); 71 | }); 72 | ws.on('message', (data) => { 73 | assert(data.toString() === 'def'); 74 | done(); 75 | ws.close(); 76 | }); 77 | }); 78 | 79 | it('handles urls with query parameters', (done) => { 80 | const ws = new WebSocket(`ws://localhost:${server.address().port}/abc?foo=bar`); 81 | ws.on('open', () => { 82 | ws.send('abc'); 83 | }); 84 | ws.on('message', (data) => { 85 | assert(data.toString() === 'abc'); 86 | done(); 87 | ws.close(); 88 | }); 89 | }); 90 | }); 91 | 92 | describe('should support custom ws server options', () => { 93 | const app = koaws(new Koa(), { 94 | handleProtocols(protocols) { 95 | if (protocols.has('bad_protocol')) { return false; } 96 | return protocols.pop(); 97 | }, 98 | }); 99 | 100 | let server = null; 101 | 102 | before((done) => { 103 | server = app.listen(done); 104 | }); 105 | 106 | after((done) => { 107 | server.close(done); 108 | }); 109 | 110 | it('reject bad protocol use wsOptions', (done) => { 111 | const ws = new WebSocket(`ws://localhost:${server.address().port}/abc`, ['bad_protocol']); 112 | ws.on('error', () => { 113 | assert(true); 114 | done(); 115 | ws.close(); 116 | }); 117 | }); 118 | }); 119 | 120 | describe('should support custom http server options', () => { 121 | // The cert is self-signed. 122 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; 123 | 124 | const secureApp = koaws(new Koa(), {}, { 125 | key: fs.readFileSync('./test/key.pem'), 126 | cert: fs.readFileSync('./test/cert.pem'), 127 | }); 128 | 129 | let server = null; 130 | 131 | before((done) => { 132 | server = secureApp.listen(done); 133 | }); 134 | 135 | after((done) => { 136 | server.close(done); 137 | }); 138 | 139 | it('supports wss protocol', (done) => { 140 | const ws = new WebSocket(`wss://localhost:${server.address().port}/abc`); 141 | ws.on('open', () => { 142 | assert(true); 143 | done(); 144 | ws.close(); 145 | }); 146 | }); 147 | }); 148 | --------------------------------------------------------------------------------