forked from fastify/fastify
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into (fastify#4439)-Access-handler-name-add-prope…
…rties-to-req-route-options
- Loading branch information
Showing
64 changed files
with
1,765 additions
and
534 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -136,6 +136,7 @@ dist | |
.DS_Store | ||
|
||
# lock files | ||
bun.lockb | ||
package-lock.json | ||
pnpm-lock.yaml | ||
yarn.lock | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
<h1 align="center">Fastify</h1> | ||
|
||
# Detecting When Clients Abort | ||
|
||
## Introduction | ||
|
||
Fastify provides request events to trigger at certain points in a request's | ||
lifecycle. However, there isn't a built-in mechanism to | ||
detect unintentional client disconnection scenarios such as when the client's | ||
internet connection is interrupted. This guide covers methods to detect if | ||
and when a client intentionally aborts a request. | ||
|
||
Keep in mind, Fastify's `clientErrorHandler` is not designed to detect when a | ||
client aborts a request. This works in the same way as the standard Node HTTP | ||
module, which triggers the `clientError` event when there is a bad request or | ||
exceedingly large header data. When a client aborts a request, there is no | ||
error on the socket and the `clientErrorHandler` will not be triggered. | ||
|
||
## Solution | ||
|
||
### Overview | ||
|
||
The proposed solution is a possible way of detecting when a client | ||
intentionally aborts a request, such as when a browser is closed or the HTTP | ||
request is aborted from your client application. If there is an error in your | ||
application code that results in the server crashing, you may require | ||
additional logic to avoid a false abort detection. | ||
|
||
The goal here is to detect when a client intentionally aborts a connection | ||
so your application logic can proceed accordingly. This can be useful for | ||
logging purposes or halting business logic. | ||
|
||
### Hands-on | ||
|
||
Say we have the following base server set up: | ||
|
||
```js | ||
import Fastify from 'fastify'; | ||
|
||
const sleep = async (time) => { | ||
return await new Promise(resolve => setTimeout(resolve, time || 1000)); | ||
} | ||
|
||
const app = Fastify({ | ||
logger: { | ||
transport: { | ||
target: 'pino-pretty', | ||
options: { | ||
translateTime: 'HH:MM:ss Z', | ||
ignore: 'pid,hostname', | ||
}, | ||
}, | ||
}, | ||
}) | ||
|
||
app.addHook('onRequest', async (request, reply) => { | ||
request.raw.on('close', () => { | ||
if (request.raw.aborted) { | ||
app.log.info('request closed') | ||
} | ||
}) | ||
}) | ||
|
||
app.get('/', async (request, reply) => { | ||
await sleep(3000) | ||
reply.code(200).send({ ok: true }) | ||
}) | ||
|
||
const start = async () => { | ||
try { | ||
await app.listen({ port: 3000 }) | ||
} catch (err) { | ||
app.log.error(err) | ||
process.exit(1) | ||
} | ||
} | ||
|
||
start() | ||
``` | ||
|
||
Our code is setting up a Fastify server which includes the following | ||
functionality: | ||
|
||
- Accepting requests at http://localhost:3000, with a 3 second delayed response | ||
of `{ ok: true }`. | ||
- An onRequest hook that triggers when every request is received. | ||
- Logic that triggers in the hook when the request is closed. | ||
- Logging that occurs when the closed request property `aborted` is true. | ||
|
||
In the request close event, you should examine the diff between a successful | ||
request and one aborted by the client to determine the best property for your | ||
use case. You can find details about request properties in the | ||
[NodeJS documentation](https://nodejs.org/api/http.html). | ||
|
||
You can also perform this logic outside of a hook, directly in a specific route. | ||
|
||
```js | ||
app.get('/', async (request, reply) => { | ||
request.raw.on('close', () => { | ||
if (request.raw.aborted) { | ||
app.log.info('request closed') | ||
} | ||
}) | ||
await sleep(3000) | ||
reply.code(200).send({ ok: true }) | ||
}) | ||
``` | ||
|
||
At any point in your business logic, you can check if the request has been | ||
aborted and perform alternative actions. | ||
|
||
```js | ||
app.get('/', async (request, reply) => { | ||
await sleep(3000) | ||
if (request.raw.aborted) { | ||
// do something here | ||
} | ||
await sleep(3000) | ||
reply.code(200).send({ ok: true }) | ||
}) | ||
``` | ||
|
||
A benefit to adding this in your application code is that you can log Fastify | ||
details such as the reqId, which may be unavailable in lower-level code that | ||
only has access to the raw request information. | ||
|
||
### Testing | ||
|
||
To test this functionality you can use an app like Postman and cancel your | ||
request within 3 seconds. Alternatively, you can use Node to send an HTTP | ||
request with logic to abort the request before 3 seconds. Example: | ||
|
||
```js | ||
const controller = new AbortController(); | ||
const signal = controller.signal; | ||
|
||
(async () => { | ||
try { | ||
const response = await fetch('http://localhost:3000', { signal }); | ||
const body = await response.text(); | ||
console.log(body); | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
})(); | ||
|
||
setTimeout(() => { | ||
controller.abort() | ||
}, 1000); | ||
``` | ||
|
||
With either approach, you should see the Fastify log appear at the moment the | ||
request is aborted. | ||
|
||
## Conclusion | ||
|
||
Specifics of the implementation will vary from one problem to another, but the | ||
main goal of this guide was to show a very specific use case of an issue that | ||
could be solved within Fastify's ecosystem. | ||
|
||
You can listen to the request close event and determine if the request was | ||
aborted or if it was successfully delivered. You can implement this solution | ||
in an onRequest hook or directly in an individual route. | ||
|
||
This approach will not trigger in the event of internet disruption, and such | ||
detection would require additional business logic. If you have flawed backend | ||
application logic that results in a server crash, then you could trigger a | ||
false detection. The `clientErrorHandler`, either by default or with custom | ||
logic, is not intended to handle this scenario and will not trigger when the | ||
client aborts a request. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.