Skip to content

Commit 01ca986

Browse files
committed
fix: handle request cancellation gracefully
1 parent 75fc616 commit 01ca986

File tree

1 file changed

+32
-1
lines changed

1 file changed

+32
-1
lines changed

packages/server/src/server.ts

Lines changed: 32 additions & 1 deletion
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,15 @@ 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+
return this.handleAbortedRequest(context, resp)
232+
}
233+
234+
return resp
216235
}
217236

218237
return this.write(context, headers, 404, 'Not found\n')
@@ -266,6 +285,18 @@ export class Server extends EventEmitter {
266285
return this.datastore.deleteExpired()
267286
}
268287

288+
protected handleAbortedRequest(context: CancellationContext, resp: Response) {
289+
const isAborted = context.signal.aborted
290+
if (isAborted) {
291+
// If the request was aborted, we should not send any response body.
292+
// The server should just close the connection.
293+
resp.headers.set('Connection', 'close')
294+
return resp
295+
}
296+
297+
return resp
298+
}
299+
269300
protected createContext() {
270301
// Initialize two AbortControllers:
271302
// 1. `requestAbortController` for instant request termination, particularly useful for stopping clients to upload when errors occur.

0 commit comments

Comments
 (0)