Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ jobs:
strategy:
matrix:
node-version:
- 10
- 12
- 14
- 16
- 18
os:
- macos-latest
- ubuntu-latest
Expand Down
80 changes: 39 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@ After registering this plugin, you can choose on which routes the WS server will

const fastify = require('fastify')()
fastify.register(require('@fastify/websocket'))

fastify.get('/', { websocket: true }, (connection /* SocketStream */, req /* FastifyRequest */) => {
connection.socket.on('message', message => {
// message.toString() === 'hi from client'
connection.socket.send('hi from server')
fastify.register(async function (fastify) {
fastify.get('/', { websocket: true }, (connection /* SocketStream */, req /* FastifyRequest */) => {
connection.socket.on('message', message => {
// message.toString() === 'hi from client'
connection.socket.send('hi from server')
})
})
})

Expand All @@ -62,18 +63,19 @@ fastify.register(require('@fastify/websocket'), {
options: { maxPayload: 1048576 }
})


fastify.get('/*', { websocket: true }, (connection /* SocketStream */, req /* FastifyRequest */) => {
connection.socket.on('message', message => {
// message.toString() === 'hi from client'
connection.socket.send('hi from wildcard route')
fastify.register(async function (fastify) {
fastify.get('/*', { websocket: true }, (connection /* SocketStream */, req /* FastifyRequest */) => {
connection.socket.on('message', message => {
// message.toString() === 'hi from client'
connection.socket.send('hi from wildcard route')
})
})
})

fastify.get('/', { websocket: true }, (connection /* SocketStream */, req /* FastifyRequest */) => {
connection.socket.on('message', message => {
// message.toString() === 'hi from client'
connection.socket.send('hi from server')
fastify.get('/', { websocket: true }, (connection /* SocketStream */, req /* FastifyRequest */) => {
connection.socket.on('message', message => {
// message.toString() === 'hi from client'
connection.socket.send('hi from server')
})
})
})

Expand Down Expand Up @@ -127,11 +129,10 @@ This plugin uses the same router as the `fastify` instance, this has a few impli
- When using `@fastify/websocket`, it needs to be registered before all routes in order to be able to intercept websocket connections to existing routes and close the connection on non-websocket routes.

```js
'use strict'

const fastify = require('fastify')()
import Fastify from 'fastify'

fastify.register(require('@fastify/websocket'))
const fastify = Fastify()
await fastify.register(require('@fastify/websocket'))

fastify.get('/', { websocket: true }, function wsHandler (connection, req) {
// bound to fastify server
Expand All @@ -143,12 +144,7 @@ fastify.get('/', { websocket: true }, function wsHandler (connection, req) {
})
})

fastify.listen(3000, err => {
if (err) {
fastify.log.error(err)
process.exit(1)
}
})
await fastify.listen({ port: 3000 })
```

If you need to handle both HTTP requests and incoming socket connections on the same route, you can still do it using the [full declaration syntax](https://www.fastify.io/docs/latest/Routes/#full-declaration), adding a `wsHandler` property.
Expand All @@ -167,22 +163,24 @@ fastify.register(require('@fastify/websocket'), {
options: { maxPayload: 1048576 }
})

fastify.route({
method: 'GET',
url: '/hello',
handler: (req, reply) => {
// this will handle http requests
reply.send({ hello: 'world' })
},
wsHandler: (conn, req) => {
// this will handle websockets connections
conn.setEncoding('utf8')
conn.write('hello client')

conn.once('data', chunk => {
conn.end()
})
}
fastify.register(async function () {
fastify.route({
method: 'GET',
url: '/hello',
handler: (req, reply) => {
// this will handle http requests
reply.send({ hello: 'world' })
},
wsHandler: (conn, req) => {
// this will handle websockets connections
conn.setEncoding('utf8')
conn.write('hello client')

conn.once('data', chunk => {
conn.end()
})
}
})
})

fastify.listen(3000, err => {
Expand Down
6 changes: 3 additions & 3 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse, Server } from 'http';
import { FastifyRequest, FastifyPluginCallback, RawServerBase, RawServerDefault, RawRequestDefaultExpression, RawReplyDefaultExpression, RequestGenericInterface, ContextConfigDefault, FastifyInstance } from 'fastify';
import { FastifyRequest, FastifyPluginCallback, RawServerBase, RawServerDefault, RawRequestDefaultExpression, RawReplyDefaultExpression, RequestGenericInterface, ContextConfigDefault, FastifyInstance} from 'fastify';
import * as fastify from 'fastify';
import * as WebSocket from 'ws';
import { Duplex, DuplexOptions } from 'stream';
Expand All @@ -18,8 +18,8 @@ declare module 'fastify' {
websocket?: boolean;
}

interface FastifyInstance<RawServer, RawRequest, RawReply> {
get: RouteShorthandMethod<RawServer, RawRequest, RawReply>
interface FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider> {
get: RouteShorthandMethod<RawServer, RawRequest, RawReply, TypeProvider>,
websocketServer: WebSocket.Server,
}

Expand Down
16 changes: 13 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ function fastifyWebsocket (fastify, opts, next) {
})

fastify.addHook('onError', (request, reply, error, done) => {
/* istanbul ignore next */
if (request.raw[kWs]) {
// Hijack reply to prevent fastify from sending the error after onError hooks are done running
reply.hijack()
Expand All @@ -92,21 +93,30 @@ function fastifyWebsocket (fastify, opts, next) {
done()
})

fastify.addHook('onResponse', (request, reply, error, done) => {
if (request.ws) {
request.raw[kWs].destroy()
}
done()
})

fastify.addHook('onRoute', routeOptions => {
let isWebsocketRoute = false
let wsHandler = routeOptions.wsHandler
let handler = routeOptions.handler

if (routeOptions.websocket || routeOptions.wsHandler) {
if (routeOptions.method !== 'GET') {
if (routeOptions.method === 'HEAD') {
return
} else if (routeOptions.method !== 'GET') {
throw new Error('websocket handler can only be declared in GET method')
}

isWebsocketRoute = true

if (routeOptions.websocket) {
wsHandler = routeOptions.handler
handler = function (request, reply) {
handler = function (_, reply) {
reply.code(404).send()
}
}
Expand Down Expand Up @@ -171,7 +181,7 @@ function fastifyWebsocket (fastify, opts, next) {
connection.socket.close()
}

function defaultErrorHandler (error, conn, request, reply) {
function defaultErrorHandler (error, conn, request) {
// Before destroying the connection, we attach an error listener.
// Since we already handled the error, adding this listener prevents the ws
// library from emitting the error and causing an uncaughtException
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"homepage": "https://github.com/fastify/fastify-websocket#readme",
"devDependencies": {
"@types/ws": "^8.2.2",
"fastify": "^3.25.3",
"fastify": "^4.0.0-rc.2",
"pre-commit": "^1.2.2",
"snazzy": "^9.0.0",
"split2": "^4.1.0",
Expand Down
Loading