Skip to content

Commit 85af1ce

Browse files
authored
Fix node crashing when used without stream (#498)
* fix for null return * update version * fix body already consumed for redirect * update version * Create strange-pugs-sort.md
1 parent c91c83b commit 85af1ce

File tree

4 files changed

+35
-8
lines changed

4 files changed

+35
-8
lines changed

.changeset/strange-pugs-sort.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"open-next": patch
3+
---
4+
5+
Fix node crashing when used without stream

packages/open-next/src/core/requestHandler.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
} from "http/index.js";
88
import { InternalEvent, InternalResult } from "types/open-next";
99
import { DetachedPromiseRunner } from "utils/promise";
10-
import { fromReadableStream } from "utils/stream";
1110

1211
import { debug, error, warn } from "../adapters/logger";
1312
import { convertRes, createServerResponse, proxyRequest } from "./routing/util";
@@ -73,12 +72,12 @@ export async function openNextHandler(
7372
);
7473
res.statusCode = preprocessResult.statusCode;
7574
res.flushHeaders();
76-
const body = await fromReadableStream(
77-
preprocessResult.body,
78-
preprocessResult.isBase64Encoded,
79-
);
80-
res.write(body);
75+
const [bodyToConsume, bodyToReturn] = preprocessResult.body.tee();
76+
for await (const chunk of bodyToConsume) {
77+
res.write(chunk);
78+
}
8179
res.end();
80+
preprocessResult.body = bodyToReturn;
8281
}
8382
return preprocessResult;
8483
} else {

packages/open-next/src/core/routing/util.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ export function convertRes(res: OpenNextNodeResponse): InternalResult {
8181
? headers["content-type"][0]
8282
: headers["content-type"],
8383
);
84-
const body = Readable.toWeb(res);
84+
// We cannot convert the OpenNextNodeResponse to a ReadableStream directly
85+
// You can look in the `aws-lambda.ts` file for some context
86+
const body = Readable.toWeb(Readable.from(res.getBody()));
8587
return {
8688
type: "core",
8789
statusCode,

packages/open-next/src/wrappers/aws-lambda.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Writable } from "node:stream";
2+
13
import type {
24
APIGatewayProxyEvent,
35
APIGatewayProxyEventV2,
@@ -6,6 +8,7 @@ import type {
68
CloudFrontRequestEvent,
79
CloudFrontRequestResult,
810
} from "aws-lambda";
11+
import { StreamCreator } from "http/openNextResponse";
912
import type { WrapperHandler } from "types/open-next";
1013

1114
import { WarmerEvent, WarmerResponse } from "../adapters/warmer-function";
@@ -40,7 +43,25 @@ const handler: WrapperHandler =
4043

4144
const internalEvent = await converter.convertFrom(event);
4245

43-
const response = await handler(internalEvent);
46+
//TODO: create a simple reproduction and open an issue in the node repo
47+
//This is a workaround, there is an issue in node that causes node to crash silently if the OpenNextNodeResponse stream is not consumed
48+
//This does not happen everytime, it's probably caused by suspended component in ssr (either via <Suspense> or loading.tsx)
49+
//Everyone that wish to create their own wrapper without a StreamCreator should implement this workaround
50+
//This is not necessary if the underlying handler does not use OpenNextNodeResponse (At the moment, OpenNextNodeResponse is used by the node runtime servers and the image server)
51+
const fakeStream: StreamCreator = {
52+
writeHeaders: () => {
53+
return new Writable({
54+
write: (_chunk, _encoding, callback) => {
55+
callback();
56+
},
57+
});
58+
},
59+
onFinish: () => {
60+
// Do nothing
61+
},
62+
};
63+
64+
const response = await handler(internalEvent, fakeStream);
4465

4566
return converter.convertTo(response, event);
4667
};

0 commit comments

Comments
 (0)