Skip to content

Commit 46b8af7

Browse files
committed
fix(server): add explicit resource cleanup in the wrapped response streams
The original `.on("error")` prevented Koa from doing its magic to automatically destroy the source stream when the client closed the connection. This should properly fix it. In Koa v4 (at some point) we may be able to use the Node.js provided stream.pipeline, depends a bit on how the changes shape up. Without having to override if we send a response or if Koa does it.
1 parent cc05500 commit 46b8af7

1 file changed

Lines changed: 38 additions & 2 deletions

File tree

  • packages/server/src/middleware

packages/server/src/middleware/log.js

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,45 @@ export function logMiddleware(app, options) {
164164
logInfoAndEndTrace(ctx, startTime, responseLength);
165165
return;
166166
} else if (ctx.body && ctx.body.readable) {
167-
const body = ctx.body;
167+
const body = ctx.body; // The original s3Stream
168168
counter = new StreamLength();
169-
ctx.body = body.pipe(counter).on("error", ctx.onerror);
169+
170+
// A cleanup function to remove all listeners we're about to add.
171+
const cleanup = () => {
172+
body.removeListener("error", onError);
173+
counter.removeListener("error", onError);
174+
counter.removeListener("finish", onFinish);
175+
counter.removeListener("close", onClose);
176+
};
177+
178+
const onError = (err) => {
179+
// If either stream has an error, destroy the other.
180+
if (!body.destroyed) body.destroy();
181+
if (!counter.destroyed) counter.destroy(err); // Pass error to propagate
182+
cleanup();
183+
};
184+
185+
const onFinish = () => {
186+
// Cleanup listeners to prevent listener leaks.
187+
cleanup();
188+
};
189+
190+
const onClose = () => {
191+
// This handles the client abort. The counter was closed prematurely.
192+
// We must destroy the source and clean up.
193+
if (!body.destroyed) {
194+
body.destroy();
195+
}
196+
cleanup();
197+
};
198+
199+
// Attach all the listeners
200+
body.on("error", onError);
201+
counter.on("error", onError);
202+
counter.on("finish", onFinish);
203+
counter.on("close", onClose);
204+
205+
ctx.body = body.pipe(counter);
170206
await bodyCloseOrFinish(ctx);
171207
}
172208

0 commit comments

Comments
 (0)