Socket
Socket
Sign inDemoInstall

@fastify/websocket

Package Overview
Dependencies
3
Maintainers
20
Versions
20
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 8.3.1 to 9.0.0

test/inject.test.js

60

index.js
'use strict'
const { ServerResponse } = require('node:http')
const { PassThrough } = require('node:stream')
const { randomBytes } = require('node:crypto')
const fp = require('fastify-plugin')
const WebSocket = require('ws')
const Duplexify = require('duplexify')

@@ -50,2 +53,56 @@ const kWs = Symbol('ws-socket')

async function injectWS (path = '/', upgradeContext = {}) {
const server2Client = new PassThrough()
const client2Server = new PassThrough()
const serverStream = new Duplexify(server2Client, client2Server)
const clientStream = new Duplexify(client2Server, server2Client)
const ws = new WebSocket(null, undefined, { isServer: false })
const head = Buffer.from([])
let resolve, reject
const promise = new Promise((_resolve, _reject) => { resolve = _resolve; reject = _reject })
ws.on('open', () => {
clientStream.removeListener('data', onData)
resolve(ws)
})
const onData = (chunk) => {
if (chunk.toString().includes('HTTP/1.1 101 Switching Protocols')) {
ws._isServer = false
ws.setSocket(clientStream, head, { maxPayload: 0 })
} else {
clientStream.removeListener('data', onData)
const statusCode = Number(chunk.toString().match(/HTTP\/1.1 (\d+)/)[1])
reject(new Error('Unexpected server response: ' + statusCode))
}
}
clientStream.on('data', onData)
const req = {
...upgradeContext,
method: 'GET',
headers: {
...upgradeContext.headers,
connection: 'upgrade',
upgrade: 'websocket',
'sec-websocket-version': 13,
'sec-websocket-key': randomBytes(16).toString('base64')
},
httpVersion: '1.1',
url: path,
[kWs]: serverStream,
[kWsHead]: head
}
websocketListenServer.emit('upgrade', req, req[kWs], req[kWsHead])
return promise
}
fastify.decorate('injectWS', injectWS)
function onUpgrade (rawRequest, socket, head) {

@@ -168,2 +225,3 @@ // Save a reference to the socket and then dispatch the request through the normal fastify router so that it will invoke hooks and then eventually a route handler that might upgrade the socket.

}
fastify.server.removeListener('upgrade', onUpgrade)

@@ -186,3 +244,3 @@

// Reference: https://github.com/websockets/ws/blob/master/lib/stream.js#L35
conn.on('error', _ => {})
conn.on('error', _ => { })
request.log.error(error)

@@ -189,0 +247,0 @@ conn.destroy(error)

7

package.json
{
"name": "@fastify/websocket",
"version": "8.3.1",
"version": "9.0.0",
"description": "basic websocket support for fastify",

@@ -30,3 +30,3 @@ "main": "index.js",

"@fastify/pre-commit": "^2.0.2",
"@fastify/type-provider-typebox": "^3.2.0",
"@fastify/type-provider-typebox": "^4.0.0",
"@types/node": "^20.1.0",

@@ -39,5 +39,6 @@ "@types/ws": "^8.2.2",

"tap": "^16.0.0",
"tsd": "^0.29.0"
"tsd": "^0.30.1"
},
"dependencies": {
"duplexify": "^4.1.2",
"fastify-plugin": "^4.0.0",

@@ -44,0 +45,0 @@ "ws": "^8.0.0"

@@ -259,2 +259,74 @@ # @fastify/websocket

### Testing
Testing the ws handler can be quite tricky, luckily `fastify-websocket` decorates fastify instance with `injectWS`.
It allows to test easily a websocket endpoint.
The signature of injectWS is the following: `([path], [upgradeContext])`.
#### App.js
```js
'use strict'
const Fastify = require('fastify')
const FastifyWebSocket = require('@fastify/websocket')
const App = Fastify()
App.register(FastifyWebSocket);
App.register(async function(fastify) {
fastify.addHook('preValidation', async (request, reply) => {
if (request.headers['api-key'] !== 'some-random-key') {
return reply.code(401).send()
}
})
fastify.get('/', { websocket: true }, (connection) => {
connection.socket.on('message', message => {
connection.socket.send('hi from server')
})
})
})
module.exports = App
```
#### App.test.js
```js
'use strict'
const { test } = require('tap')
const Fastify = require('fastify')
const App = require('./app.js')
test('connect to /', async (t) => {
t.plan(1)
const fastify = Fastify()
fastify.register(App)
t.teardown(fastify.close.bind(fastify))
const ws = await fastify.injectWS('/', {headers: { "api-key" : "some-random-key" }})
let resolve;
const promise = new Promise(r => { resolve = r })
ws.on('message', (data) => {
resolve(data.toString());
})
ws.send('hi from client')
t.assert(await promise, 'hi from server')
// Remember to close the ws at the end
ws.terminate()
})
```
#### Things to know
- Websocket need to be closed manually at the end of each test.
- `fastify.ready()` needs to be awaited to ensure that fastify has been decorated.
- You need to register the event listener before sending the message if you need to process server response.
## Options

@@ -261,0 +333,0 @@

@@ -30,5 +30,8 @@ /// <reference types="node" />

type InjectWSFn<RawRequest> =
((path?: string, upgradeContext?: Partial<RawRequest>) => Promise<WebSocket>)
interface FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider> {
get: RouteShorthandMethod<RawServer, RawRequest, RawReply, TypeProvider, Logger>,
websocketServer: WebSocket.Server,
injectWS: InjectWSFn<RawRequest>
}

@@ -71,3 +74,2 @@

interface WebSocketServerOptions extends Omit<WebSocket.ServerOptions, "path"> { }
export type WebsocketHandler<

@@ -86,7 +88,5 @@ RawServer extends RawServerBase = RawServerDefault,

) => void | Promise<any>;
export interface SocketStream extends Duplex {
socket: WebSocket;
}
export interface WebsocketPluginOptions {

@@ -98,3 +98,2 @@ errorHandler?: (this: FastifyInstance, error: Error, connection: SocketStream, request: FastifyRequest, reply: FastifyReply) => void;

}
export interface RouteOptions<

@@ -101,0 +100,0 @@ RawServer extends RawServerBase = RawServerDefault,

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc