Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/eleven-moose-heal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@opennextjs/aws": patch
---

fix(dev-overrides): Add automatic response cleanup via onClose callback

This changes will make `request.signal.onabort` work in route handlers for `node` and `express-dev` wrappers.
8 changes: 8 additions & 0 deletions packages/open-next/src/http/openNextResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ export class OpenNextNodeResponse extends Transform implements ServerResponse {
) {
this.statusCode = statusCode;
}

// We want to destroy this response when the original response is closed. (i.e when the client disconnects)
// This is to support `request.signal.onabort` in route handlers
if (streamCreator?.onClose) {
streamCreator.onClose(() => {
this.destroy();
});
}
}

// Necessary for next 12
Expand Down
12 changes: 12 additions & 0 deletions packages/open-next/src/overrides/wrappers/express-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ const wrapper: WrapperHandler = async (handler, converter) => {
req.headers["x-forwarded-proto"] = req.protocol;
}
const internalEvent = await converter.convertFrom(req);

let onCloseCallback: (() => void) | undefined;
const streamCreator: StreamCreator = {
writeHeaders: (prelude) => {
res.setHeader("Set-Cookie", prelude.cookies);
Expand All @@ -49,7 +51,17 @@ const wrapper: WrapperHandler = async (handler, converter) => {
return res;
},
onFinish: () => {},
onClose: (callback) => {
onCloseCallback = callback;
},
};

res.on("close", () => {
if (onCloseCallback) {
onCloseCallback();
}
});

await handler(internalEvent, { streamCreator });
});

Expand Down
16 changes: 15 additions & 1 deletion packages/open-next/src/overrides/wrappers/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,35 @@ import { debug, error } from "../../adapters/logger";
const wrapper: WrapperHandler = async (handler, converter) => {
const server = createServer(async (req, res) => {
const internalEvent = await converter.convertFrom(req);

let onCloseCallback: (() => void) | undefined;
const streamCreator: StreamCreator = {
writeHeaders: (prelude) => {
res.setHeader("Set-Cookie", prelude.cookies);
res.writeHead(prelude.statusCode, prelude.headers);
res.flushHeaders();
return res;
},
onClose: (callback) => {
onCloseCallback = callback;
},
};

res.on("close", () => {
if (onCloseCallback) {
onCloseCallback();
}
});

if (internalEvent.rawPath === "/__health") {
res.writeHead(200, {
"Content-Type": "text/plain",
});
res.end("OK");
} else {
await handler(internalEvent, { streamCreator });
await handler(internalEvent, {
streamCreator,
});
}
});

Expand Down
2 changes: 2 additions & 0 deletions packages/open-next/src/types/open-next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export interface StreamCreator {
// Just to fix an issue with aws lambda streaming with empty body
onWrite?: () => void;
onFinish?: (length: number) => void;
// This is called when the wrappers response is closed, i.e. when the client disconnects
onClose?: (callback: () => void) => void;
}

export type WaitUntil = (promise: Promise<void>) => void;
Expand Down
Loading