Skip to content

Commit a634bb7

Browse files
darkgl0wEomm
authored andcommitted
Allow wss plugin to be customized (#22)
1 parent 2932bdb commit a634bb7

File tree

3 files changed

+185
-27
lines changed

3 files changed

+185
-27
lines changed

README.md

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,75 @@
11
# fastify-websocket
2-
3-
[![Greenkeeper badge](https://badges.greenkeeper.io/fastify/fastify-websocket.svg)](https://greenkeeper.io/)
2+
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/) [![Build Status](https://travis-ci.org/fastify/fastify-websocket.svg?branch=master)](https://travis-ci.org/fastify/fastify-websocket) [![Greenkeeper badge](https://badges.greenkeeper.io/fastify/fastify-websocket.svg)](https://greenkeeper.io/)
43

54
WebSocket support for [Fastify](https://github.com/fastify/fastify).
65
Built upon [websocket-stream](http://npm.im/websocket-stream).
76

8-
## Example
7+
## Install
8+
9+
```
10+
npm install fastify-websocket --save
11+
```
12+
13+
## Usage
14+
15+
All you need to do is to add it to your project with `register` and you are done!
16+
17+
### Example
918

1019
```js
1120
'use strict'
1221

1322
const fastify = require('fastify')()
1423

15-
fastify.register(require('fastify-websocket'), { handle })
24+
const wssOtions = {
25+
maxPayload: 1048576, // we set the maximum allowed messages size to 1 MiB (1024 bytes * 1024 bytes)
26+
path: '/fastify', // we accept only connections matching this path e.g.: ws://localhost:3000/fastify
27+
verifyClient: function (info, next) {
28+
if (info.req.headers['x-fastify-header'] !== 'fastify is awesome !') {
29+
return next(false) // the connection is not allowed
30+
}
31+
32+
next(true) // the connection is allowed
33+
}
34+
}
35+
36+
fastify.register(require('fastify-websocket'), {
37+
handle,
38+
options: wssOptions
39+
})
1640

1741
function handle (conn) {
18-
conn.pipe(conn) // creates a echo server
42+
conn.pipe(conn) // creates an echo server
1943
}
2044

21-
fastify.listen(0)
45+
fastify.listen(3000, (err) => {
46+
if (err) {
47+
fastify.log.error(err);
48+
process.exit(1);
49+
}
50+
})
2251
```
2352

53+
## Options :
54+
`fastify-websocket` accept the same options as [`websocket-stream`](https://github.com/maxogden/websocket-stream#options) and as [`ws`](https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback) :
55+
56+
- `objectMode` - Send each chunk on its own, and do not try to pack them in a single websocket frame.
57+
- `host` - The hostname where to bind the server.
58+
- `port` - The port where to bind the server.
59+
- `backlog` - The maximum length of the queue of pending connections.
60+
- `server` - A pre-created Node.js HTTP/S server.
61+
- `verifyClient` - A function which can be used to validate incoming connections.
62+
- `handleProtocols` - A function which can be used to handle the WebSocket subprotocols.
63+
- `path` - Accept only connections matching this path.
64+
- `noServer` - Enable no server mode.
65+
- `clientTracking` - Specifies whether or not to track clients.
66+
- `perMessageDeflate` - Enable/disable permessage-deflate.
67+
- `maxPayload` - The maximum allowed message size in bytes.
68+
69+
For more informations you can check [`ws` options documentation](https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback) and [`websocket-stream` options documentation](https://github.com/maxogden/websocket-stream#options).
70+
71+
_**NB:** By default if you do not provide a `server` option `fastify-websocket` will bind your websocket server instance to the scoped `fastify` instance._
72+
2473
## TODO
2574

2675
* [ ] Support Hooks?

index.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,28 @@
33
const fp = require('fastify-plugin')
44
const websocket = require('websocket-stream')
55

6-
module.exports = fp(function (fastify, opts, next) {
7-
var handle = opts.handle
6+
function fastifyWebsocket (fastify, opts, next) {
7+
const handle = opts.handle
8+
const options = Object.assign({ server: fastify.server }, opts.options)
89

910
if (typeof handle !== 'function') {
1011
return next(new Error('invalid handle function'))
1112
}
1213

13-
var wss = websocket.createServer({
14-
server: fastify.server
15-
}, handle)
14+
const wss = websocket.createServer(options, handle)
1615

1716
fastify.decorate('websocketServer', wss)
1817

18+
fastify.addHook('onClose', close)
19+
1920
next()
20-
}, {
21+
}
22+
23+
function close (fastify, done) {
24+
fastify.websocketServer.close(done)
25+
}
26+
27+
module.exports = fp(fastifyWebsocket, {
2128
fastify: '>=0.39.0',
2229
name: 'fastify-websocket'
2330
})

test.js

Lines changed: 117 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,35 @@
11
'use strict'
22

3+
const http = require('http')
34
const test = require('tap').test
4-
const fastify = require('fastify')
5+
const Fastify = require('fastify')
56
const fastifyWebsocket = require('.')
67
const websocket = require('websocket-stream')
78

8-
test('expose a websocket', (t) => {
9+
test('Should expose a websocket', (t) => {
910
t.plan(3)
1011

11-
const server = fastify()
12-
server.register(fastifyWebsocket, { handle })
12+
const fastify = Fastify()
13+
t.tearDown(() => fastify.close())
1314

14-
function handle (conn) {
15-
conn.setEncoding('utf8')
16-
conn.write('hello client')
17-
t.tearDown(conn.destroy.bind(conn))
15+
fastify.register(fastifyWebsocket, { handle })
1816

19-
conn.once('data', (chunk) => {
17+
function handle (connection) {
18+
connection.setEncoding('utf8')
19+
connection.write('hello client')
20+
t.tearDown(() => connection.destroy())
21+
22+
connection.once('data', (chunk) => {
2023
t.equal(chunk, 'hello server')
21-
conn.end()
24+
connection.end()
2225
})
2326
}
2427

25-
t.tearDown(server.close.bind(server))
26-
27-
server.listen(0, (err) => {
28+
fastify.listen(0, (err) => {
2829
t.error(err)
2930

30-
const client = websocket('ws://localhost:' + server.server.address().port)
31-
t.tearDown(client.destroy.bind(client))
31+
const client = websocket('ws://localhost:' + fastify.server.address().port)
32+
t.tearDown(() => client.destroy())
3233

3334
client.setEncoding('utf8')
3435
client.write('hello server')
@@ -39,3 +40,104 @@ test('expose a websocket', (t) => {
3940
})
4041
})
4142
})
43+
44+
test('Should be able to pass custom options to websocket-stream', (t) => {
45+
t.plan(3)
46+
47+
const fastify = Fastify()
48+
t.tearDown(() => fastify.close())
49+
50+
const options = {
51+
verifyClient: function (info) {
52+
t.equal(info.req.headers['x-custom-header'], 'fastify is awesome !')
53+
54+
return true
55+
}
56+
}
57+
58+
fastify.register(fastifyWebsocket, { handle, options })
59+
60+
// this is all that's needed to create an echo server
61+
function handle (connection) {
62+
connection.pipe(connection)
63+
t.tearDown(() => connection.destroy())
64+
}
65+
66+
fastify.listen(0, (err) => {
67+
t.error(err)
68+
69+
const clientOptions = { headers: { 'x-custom-header': 'fastify is awesome !' } }
70+
const client = websocket('ws://localhost:' + fastify.server.address().port, clientOptions)
71+
t.tearDown(() => client.destroy())
72+
73+
client.setEncoding('utf8')
74+
client.write('hello')
75+
76+
client.once('data', (chunk) => {
77+
t.equal(chunk, 'hello')
78+
client.end()
79+
})
80+
})
81+
})
82+
83+
test('Should be able to pass a custom server option to websocket-stream', (t) => {
84+
t.plan(2)
85+
86+
// We create an external server
87+
const externalServerPort = 3000
88+
const externalServer = http
89+
.createServer()
90+
.on('connection', (socket) => {
91+
socket.unref()
92+
})
93+
.listen(externalServerPort, 'localhost')
94+
95+
const fastify = Fastify()
96+
t.tearDown(() => {
97+
externalServer.close()
98+
fastify.close()
99+
})
100+
101+
const options = {
102+
server: externalServer
103+
}
104+
105+
fastify.register(fastifyWebsocket, { handle, options })
106+
107+
// this is all that's needed to create an echo server
108+
function handle (connection) {
109+
connection.pipe(connection)
110+
t.tearDown(() => connection.destroy())
111+
}
112+
113+
fastify.listen(0, (err) => {
114+
t.error(err)
115+
116+
const client = websocket('ws://localhost:' + externalServerPort)
117+
t.tearDown(() => client.destroy())
118+
119+
client.setEncoding('utf8')
120+
client.write('hello')
121+
122+
client.once('data', (chunk) => {
123+
t.equal(chunk, 'hello')
124+
client.end()
125+
})
126+
})
127+
})
128+
129+
test('Should throw on an invalid handle parameter', (t) => {
130+
t.plan(2)
131+
132+
const fastify = Fastify()
133+
t.tearDown(() => fastify.close())
134+
135+
const handle = 'handle must be a function'
136+
137+
fastify.register(fastifyWebsocket, { handle })
138+
139+
fastify.listen(0, (err) => {
140+
t.ok(err)
141+
t.equal(err.message, 'invalid handle function')
142+
})
143+
})

0 commit comments

Comments
 (0)