Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure that setErrorHandler functions run for errors thrown before the websocket is upgraded #216

Merged
merged 1 commit into from
Aug 4, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,11 @@ fastify.listen(3000, err => {

### Custom error handler:

You can optionally provide a custom errorHandler that will be used to handle any cleaning up:
You can optionally provide a custom `errorHandler` that will be used to handle any cleaning up of established websocket connections. The `errorHandler` will be called if any errors are thrown by your websocket route handler after the connection has been established. Note that neither Fastify's `onError` hook or functions registered with `fastify.setErrorHandler` will be called for errors thrown during a websocket request handler.

```js
'use strict'
Neither the `errorHandler` passed to this plugin or fastify's `onError` hook will be called for errors encountered during message processing for your connection. If you want to handle unexpected errors within your `message` event handlers, you'll need to use your own `try { } catch {}` statements and decide what to send back over the websocket.

```js
const fastify = require('fastify')()

fastify.register(require('@fastify/websocket'), {
Expand Down Expand Up @@ -231,6 +231,7 @@ fastify.listen(3000, err => {
})
```

Note: Fastify's `onError` and error handlers registered by `setErrorHandler` will still be called for errors encountered *before* the websocket connection is established. This means errors thrown by `onRequest` hooks, `preValidation` handlers, and hooks registered by plugins will use the normal error handling mechanisms in Fastify. Once the websocket is established and your websocket route handler is called, `fastify-websocket`'s `errorHandler` takes over.
## Options

`@fastify/websocket` accept these options for [`ws`](https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback) :
Expand Down
13 changes: 0 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,6 @@ function fastifyWebsocket (fastify, opts, next) {
done()
})

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()
handleUpgrade(request.raw, connection => {
// Handle the error
errorHandler.call(this, error, connection, request, reply)
})
}
done()
})

fastify.addHook('onResponse', (request, reply, done) => {
if (request.ws) {
request.raw[kWs].destroy()
Expand Down
48 changes: 37 additions & 11 deletions test/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,14 @@ test('Should run onError hook before handler is executed (error thrown in onRequ
fastify.addHook('onError', async (request, reply) => t.ok('called', 'onError'))

fastify.get('/echo', { websocket: true }, (conn, request) => {
t.teardown(conn.destroy.bind(conn))
t.fail()
})
})

fastify.listen({ port: 0 }, function (err) {
t.error(err)
const ws = new WebSocket('ws://localhost:' + (fastify.server.address()).port + '/echo')
const client = WebSocket.createWebSocketStream(ws, { encoding: 'utf8' })
t.teardown(client.destroy.bind(client))
ws.on('close', code => t.equal(code, 1006))
ws.on('unexpected-response', (_request, response) => t.equal(response.statusCode, 500))
})
})

Expand Down Expand Up @@ -130,9 +128,7 @@ test('Should run onError hook before handler is executed (error thrown in preVal
fastify.listen({ port: 0 }, function (err) {
t.error(err)
const ws = new WebSocket('ws://localhost:' + (fastify.server.address()).port + '/echo')
const client = WebSocket.createWebSocketStream(ws, { encoding: 'utf8' })
t.teardown(client.destroy.bind(client))
ws.on('close', code => t.equal(code, 1006))
ws.on('unexpected-response', (_request, response) => t.equal(response.statusCode, 500))
})
})

Expand All @@ -152,7 +148,7 @@ test('onError hooks can send a reply and prevent hijacking', t => {

fastify.addHook('onError', async (request, reply) => {
t.ok('called', 'onError')
await reply.code(404).send('there was an error')
await reply.code(501).send('there was an error')
})

fastify.get('/echo', { websocket: true }, (conn, request) => {
Expand All @@ -163,9 +159,39 @@ test('onError hooks can send a reply and prevent hijacking', t => {
fastify.listen({ port: 0 }, function (err) {
t.error(err)
const ws = new WebSocket('ws://localhost:' + (fastify.server.address()).port + '/echo')
const client = WebSocket.createWebSocketStream(ws, { encoding: 'utf8' })
t.teardown(client.destroy.bind(client))
ws.on('close', code => t.equal(code, 1006))
ws.on('unexpected-response', (_request, response) => t.equal(response.statusCode, 501))
})
})

test('setErrorHandler functions can send a reply and prevent hijacking', t => {
t.plan(4)
const fastify = Fastify()

t.teardown(() => fastify.close())

fastify.register(fastifyWebsocket)

fastify.register(async function (fastify) {
fastify.addHook('preValidation', async (request, reply) => {
await Promise.resolve()
throw new Error('Fail')
})

fastify.setErrorHandler(async (error, request, reply) => {
t.ok('called', 'onError')
t.ok(error)
await reply.code(501).send('there was an error')
})

fastify.get('/echo', { websocket: true }, (conn, request) => {
t.fail()
})
})

fastify.listen({ port: 0 }, function (err) {
t.error(err)
const ws = new WebSocket('ws://localhost:' + (fastify.server.address()).port + '/echo')
ws.on('unexpected-response', (_request, response) => t.equal(response.statusCode, 501))
})
})

Expand Down