From 539ad7696d4094811ede0645fc9014bf1d219f08 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Thu, 5 Sep 2024 09:45:11 -0400 Subject: [PATCH] Fix issue with 304 response sin single fetch --- .changeset/bright-rabbits-attend.md | 5 +++ integration/helpers/create-fixture.ts | 5 ++- integration/single-fetch-test.ts | 50 +++++++++++++++++++++++++ packages/remix-server-runtime/server.ts | 5 +++ 4 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 .changeset/bright-rabbits-attend.md diff --git a/.changeset/bright-rabbits-attend.md b/.changeset/bright-rabbits-attend.md new file mode 100644 index 00000000000..fc6e428fc28 --- /dev/null +++ b/.changeset/bright-rabbits-attend.md @@ -0,0 +1,5 @@ +--- +"@remix-run/server-runtime": patch +--- + +Single Fetch: Do not try to encode a `turbo-stream` body into 304 responses diff --git a/integration/helpers/create-fixture.ts b/integration/helpers/create-fixture.ts index fe228466541..5f7966923bc 100644 --- a/integration/helpers/create-fixture.ts +++ b/integration/helpers/create-fixture.ts @@ -138,12 +138,13 @@ export async function createFixture(init: FixtureInit, mode?: ServerMode) { let url = new URL(href, "test://test"); let request = new Request(url.toString(), init); let response = await handler(request); - let decoded = await decodeViaTurboStream(response.body!, global); return { status: response.status, statusText: response.statusText, headers: response.headers, - data: decoded.value, + data: response.body + ? (await decodeViaTurboStream(response.body!, global)).value + : null, }; }; diff --git a/integration/single-fetch-test.ts b/integration/single-fetch-test.ts index ec3de5e2ae8..870a97b94c6 100644 --- a/integration/single-fetch-test.ts +++ b/integration/single-fetch-test.ts @@ -1924,6 +1924,56 @@ test.describe("single-fetch", () => { ]); }); + test("does not try to encode a turbo-stream body into 304 responses", async () => { + let fixture = await createFixture({ + config: { + future: { + unstable_singleFetch: true, + }, + }, + files: { + ...files, + "app/routes/_index.tsx": js` + import { json } from "@remix-run/node"; + import { useLoaderData } from "@remix-run/react"; + + const eTag = "1234"; + export function loader({ request }) { + if (request.headers.get("If-None-Match") === eTag) { + throw new Response(null, { status: 304 }); + } + return { message: "Hello from the loader!" }; + }; + + export default function Index() { + const { message } = useLoaderData(); + return

{message}

+ } + `, + }, + }); + let res = await fixture.requestSingleFetchData("/_root.data"); + expect(res.data).toEqual({ + root: { + data: { + message: "ROOT", + }, + }, + "routes/_index": { + data: { + message: "Hello from the loader!", + }, + }, + }); + res = await fixture.requestSingleFetchData("/_root.data", { + headers: { + "If-None-Match": "1234", + }, + }); + expect(res.status).toBe(304); + expect(res.data).toBeNull(); + }); + test.describe("revalidations/_routes param", () => { test("does not make a server call if no loaders need to run", async ({ page, diff --git a/packages/remix-server-runtime/server.ts b/packages/remix-server-runtime/server.ts index 9c404322d95..a28aff97369 100644 --- a/packages/remix-server-runtime/server.ts +++ b/packages/remix-server-runtime/server.ts @@ -413,6 +413,11 @@ async function handleSingleFetchRequest( let resultHeaders = new Headers(headers); resultHeaders.set("X-Remix-Response", "yes"); + // 304 responses should not have a body + if (status === 304) { + return new Response(null, { status: 304, headers: resultHeaders }); + } + // We use a less-descriptive `text/x-script` here instead of something like // `text/x-turbo` to enable compression when deployed via Cloudflare. See: // - https://github.com/remix-run/remix/issues/9884