diff --git a/.changeset/little-badgers-sparkle.md b/.changeset/little-badgers-sparkle.md new file mode 100644 index 000000000..887b31bb9 --- /dev/null +++ b/.changeset/little-badgers-sparkle.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/aws": patch +--- + +Fix external rewrites for binary data diff --git a/examples/pages-router/next.config.ts b/examples/pages-router/next.config.ts index 4d31fbe7c..8ed04b226 100644 --- a/examples/pages-router/next.config.ts +++ b/examples/pages-router/next.config.ts @@ -38,6 +38,10 @@ const nextConfig: NextConfig = { }, ], }, + { + source: "/external-on-image", + destination: "https://opennext.js.org/share.png", + }, ], redirects: async () => [ { diff --git a/packages/open-next/src/overrides/proxyExternalRequest/node.ts b/packages/open-next/src/overrides/proxyExternalRequest/node.ts index d1d29d943..35273dd34 100644 --- a/packages/open-next/src/overrides/proxyExternalRequest/node.ts +++ b/packages/open-next/src/overrides/proxyExternalRequest/node.ts @@ -47,19 +47,19 @@ const nodeProxy: ProxyExternalRequest = { rejectUnauthorized: false, }, (_res) => { + const resHeaders = _res.headers; const nodeReadableStream = - _res.headers["content-encoding"] === "br" + resHeaders["content-encoding"] === "br" ? _res.pipe(require("node:zlib").createBrotliDecompress()) - : _res.headers["content-encoding"] === "gzip" + : resHeaders["content-encoding"] === "gzip" ? _res.pipe(require("node:zlib").createGunzip()) : _res; - const isBase64Encoded = - isBinaryContentType(headers["content-type"]) || - !!headers["content-encoding"]; + isBinaryContentType(resHeaders["content-type"]) || + !!resHeaders["content-encoding"]; const result: InternalResult = { type: "core", - headers: filterHeadersForProxy(_res.headers), + headers: filterHeadersForProxy(resHeaders), statusCode: _res.statusCode ?? 200, // TODO: check base64 encoding isBase64Encoded, diff --git a/packages/tests-e2e/tests/appRouter/og.test.ts b/packages/tests-e2e/tests/appRouter/og.test.ts index 734d99b56..f7fcdee63 100644 --- a/packages/tests-e2e/tests/appRouter/og.test.ts +++ b/packages/tests-e2e/tests/appRouter/og.test.ts @@ -1,14 +1,10 @@ -import { createHash } from "node:crypto"; import { expect, test } from "@playwright/test"; +import { validateMd5 } from "../utils"; // This is the md5sums of the expected PNGs generated with `md5sum ` const OG_MD5 = "6e5e794ac0c27598a331690f96f05d00"; const API_OG_MD5 = "cac95fc3e2d4d52870c0536bb18ba85b"; -function validateMd5(data: Buffer, expectedHash: string) { - return createHash("md5").update(data).digest("hex") === expectedHash; -} - test("Open-graph image to be in metatags and present", async ({ page, request, diff --git a/packages/tests-e2e/tests/pagesRouter/rewrite.test.ts b/packages/tests-e2e/tests/pagesRouter/rewrite.test.ts index 44864b750..6e40302c9 100644 --- a/packages/tests-e2e/tests/pagesRouter/rewrite.test.ts +++ b/packages/tests-e2e/tests/pagesRouter/rewrite.test.ts @@ -1,4 +1,7 @@ import { expect, test } from "@playwright/test"; +import { validateMd5 } from "../utils"; + +const EXT_PNG_MD5 = "405f45cc3397b09717a13ebd6f1e027b"; test("Single Rewrite", async ({ page }) => { await page.goto("/rewrite"); @@ -13,3 +16,10 @@ test("Rewrite with query", async ({ page }) => { const el = page.getByText("SSR"); await expect(el).toBeVisible(); }); + +test("Rewrite to external image", async ({ request }) => { + const response = await request.get("/external-on-image"); + expect(response.status()).toBe(200); + expect(response.headers()["content-type"]).toBe("image/png"); + expect(validateMd5(await response.body(), EXT_PNG_MD5)).toBe(true); +}); diff --git a/packages/tests-e2e/tests/utils.ts b/packages/tests-e2e/tests/utils.ts new file mode 100644 index 000000000..04242c5f9 --- /dev/null +++ b/packages/tests-e2e/tests/utils.ts @@ -0,0 +1,5 @@ +import { createHash } from "node:crypto"; + +export function validateMd5(data: Buffer, expectedHash: string) { + return createHash("md5").update(data).digest("hex") === expectedHash; +}