Skip to content

Commit 7b719ea

Browse files
fenosMurderlon
andauthored
@tus/server: handle request cancellation gracefully (#774)
This PR fixes 2 regressions: - Handle gracefully the request cancellation by aborting the context, which will allow the locks to be released immediately and available to be acquired by subsequent requests. Currently, this has been broken since the context was never cancelled when the request was aborted - Force closing the connection when the request was aborted, to prevent the client from needing to send the whole body before realising that the upload was cancelled (potentially from another request acquiring the lock) --------- Co-authored-by: Merlijn Vos <[email protected]>
1 parent 75fc616 commit 7b719ea

File tree

2 files changed

+27
-2
lines changed

2 files changed

+27
-2
lines changed

.changeset/modern-birds-appear.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@tus/server": patch
3+
---
4+
5+
Handle request cancellation gracefully on Node.js runtime

packages/server/src/server.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,17 @@ export class Server extends EventEmitter {
134134
const context = this.createContext()
135135
const headers = new Headers()
136136

137+
// Special case on the Node.js runtime,
138+
// We handle gracefully request errors such as disconnects or timeouts.
139+
// This is important to avoid memory leaks and ensure that the server can
140+
// handle subsequent requests without issues.
141+
if ('node' in req && req.node) {
142+
const nodeReq = (req.node as {req: http.IncomingMessage}).req
143+
nodeReq.once('error', () => {
144+
context.abort()
145+
})
146+
}
147+
137148
const onError = async (error: {
138149
status_code?: number
139150
body?: string
@@ -212,7 +223,16 @@ export class Server extends EventEmitter {
212223
// Invoke the handler for the method requested
213224
const handler = this.handlers[req.method as keyof Handlers]
214225
if (handler) {
215-
return handler.send(req, context, headers).catch(onError)
226+
const resp = await handler.send(req, context, headers).catch(onError)
227+
228+
if (context.signal.aborted) {
229+
// If the request was aborted, we should not send any response body.
230+
// The server should just close the connection.
231+
resp.headers.set('Connection', 'close')
232+
return resp
233+
}
234+
235+
return resp
216236
}
217237

218238
return this.write(context, headers, 404, 'Not found\n')
@@ -253,7 +273,7 @@ export class Server extends EventEmitter {
253273
return new Response(body, {status, headers})
254274
}
255275

256-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
276+
// biome-ignore lint/suspicious/noExplicitAny: it's fine
257277
listen(...args: any[]): http.Server {
258278
return http.createServer(this.handle.bind(this)).listen(...args)
259279
}

0 commit comments

Comments
 (0)