From 85af1ce3319f81aa243536cf909cae59683efd75 Mon Sep 17 00:00:00 2001 From: conico974 Date: Mon, 26 Aug 2024 19:13:08 +0200 Subject: [PATCH] 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 --- .changeset/strange-pugs-sort.md | 5 ++++ packages/open-next/src/core/requestHandler.ts | 11 ++++----- packages/open-next/src/core/routing/util.ts | 4 +++- packages/open-next/src/wrappers/aws-lambda.ts | 23 ++++++++++++++++++- 4 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 .changeset/strange-pugs-sort.md diff --git a/.changeset/strange-pugs-sort.md b/.changeset/strange-pugs-sort.md new file mode 100644 index 00000000..a9dc1c85 --- /dev/null +++ b/.changeset/strange-pugs-sort.md @@ -0,0 +1,5 @@ +--- +"open-next": patch +--- + +Fix node crashing when used without stream diff --git a/packages/open-next/src/core/requestHandler.ts b/packages/open-next/src/core/requestHandler.ts index 0e83cc0e..5a5342e6 100644 --- a/packages/open-next/src/core/requestHandler.ts +++ b/packages/open-next/src/core/requestHandler.ts @@ -7,7 +7,6 @@ import { } from "http/index.js"; import { InternalEvent, InternalResult } from "types/open-next"; import { DetachedPromiseRunner } from "utils/promise"; -import { fromReadableStream } from "utils/stream"; import { debug, error, warn } from "../adapters/logger"; import { convertRes, createServerResponse, proxyRequest } from "./routing/util"; @@ -73,12 +72,12 @@ export async function openNextHandler( ); res.statusCode = preprocessResult.statusCode; res.flushHeaders(); - const body = await fromReadableStream( - preprocessResult.body, - preprocessResult.isBase64Encoded, - ); - res.write(body); + const [bodyToConsume, bodyToReturn] = preprocessResult.body.tee(); + for await (const chunk of bodyToConsume) { + res.write(chunk); + } res.end(); + preprocessResult.body = bodyToReturn; } return preprocessResult; } else { diff --git a/packages/open-next/src/core/routing/util.ts b/packages/open-next/src/core/routing/util.ts index bae3d413..c7b25dd4 100644 --- a/packages/open-next/src/core/routing/util.ts +++ b/packages/open-next/src/core/routing/util.ts @@ -81,7 +81,9 @@ export function convertRes(res: OpenNextNodeResponse): InternalResult { ? headers["content-type"][0] : headers["content-type"], ); - const body = Readable.toWeb(res); + // We cannot convert the OpenNextNodeResponse to a ReadableStream directly + // You can look in the `aws-lambda.ts` file for some context + const body = Readable.toWeb(Readable.from(res.getBody())); return { type: "core", statusCode, diff --git a/packages/open-next/src/wrappers/aws-lambda.ts b/packages/open-next/src/wrappers/aws-lambda.ts index 3477e9ff..e4fa8afc 100644 --- a/packages/open-next/src/wrappers/aws-lambda.ts +++ b/packages/open-next/src/wrappers/aws-lambda.ts @@ -1,3 +1,5 @@ +import { Writable } from "node:stream"; + import type { APIGatewayProxyEvent, APIGatewayProxyEventV2, @@ -6,6 +8,7 @@ import type { CloudFrontRequestEvent, CloudFrontRequestResult, } from "aws-lambda"; +import { StreamCreator } from "http/openNextResponse"; import type { WrapperHandler } from "types/open-next"; import { WarmerEvent, WarmerResponse } from "../adapters/warmer-function"; @@ -40,7 +43,25 @@ const handler: WrapperHandler = const internalEvent = await converter.convertFrom(event); - const response = await handler(internalEvent); + //TODO: create a simple reproduction and open an issue in the node repo + //This is a workaround, there is an issue in node that causes node to crash silently if the OpenNextNodeResponse stream is not consumed + //This does not happen everytime, it's probably caused by suspended component in ssr (either via or loading.tsx) + //Everyone that wish to create their own wrapper without a StreamCreator should implement this workaround + //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) + const fakeStream: StreamCreator = { + writeHeaders: () => { + return new Writable({ + write: (_chunk, _encoding, callback) => { + callback(); + }, + }); + }, + onFinish: () => { + // Do nothing + }, + }; + + const response = await handler(internalEvent, fakeStream); return converter.convertTo(response, event); };