Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions .changeset/bump-undici-remove-patch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"miniflare": patch
"wrangler": patch
---

Fix POST/PUT requests with non-2xx responses throwing "fetch failed"

Previously, sending a POST or PUT request that received a non-2xx response (e.g. 401, 400, 403) would throw a `TypeError: fetch failed` error. This was caused by an undici bug where `isTraversableNavigable()` incorrectly returned `true`, causing the 401 credential-retry block to execute in Node.js and fail on stream-backed request bodies. This has been fixed upstream in undici v7.24.8, so we've bumped our dependency and removed the previous pnpm patch workaround.
3 changes: 0 additions & 3 deletions packages/miniflare/src/http/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ export class Request<
return this[kCf];
}

// JSDoc comment so retained when bundling types with api-extractor
/** @ts-expect-error `clone` is actually defined as a method internally */
clone(): Request<CfType> {
// @ts-ignore
const request = super.clone() as Request<CfType>;
// Update prototype so cloning a clone clones `cf`
Object.setPrototypeOf(request, Request.prototype);
Expand Down
3 changes: 0 additions & 3 deletions packages/miniflare/src/http/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,10 @@ export class Response extends BaseResponse {
return this[kWebSocket];
}

// JSDoc comment so retained when bundling types with api-extractor
/** @ts-expect-error `clone` is actually defined as a method internally */
clone(): Response {
if (this[kWebSocket]) {
throw new TypeError("Cannot clone a response to a WebSocket handshake.");
}
// @ts-ignore
const response = super.clone() as Response;
Object.setPrototypeOf(response, Response.prototype);
return response;
Expand Down
57 changes: 57 additions & 0 deletions packages/miniflare/test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3745,3 +3745,60 @@ test("Miniflare: MINIFLARE_WORKERD_CONFIG_DEBUG controls workerd config file cre
expect(existsSync(configFilePath)).toBe(true);
await mf.dispose();
});

// https://github.com/cloudflare/workers-sdk/issues/13013
test("Miniflare: dispatchFetch handles POST/PUT with non-2xx status", async ({
expect,
}) => {
const mf = new Miniflare({
modules: true,
script: `export default {
async fetch(request) {
const url = new URL(request.url);
const status = parseInt(url.searchParams.get("status") ?? "200");
return Response.json(
{ method: request.method, status },
{ status }
);
}
}`,
});
useDispose(mf);

// GET + non-2xx (worked before the fix too)
for (const status of [400, 401, 403, 404, 500]) {
const res = await mf.dispatchFetch(`http://localhost/?status=${status}`);
expect(res.status).toBe(status);
expect(await res.json()).toEqual({ method: "GET", status });
}

// POST + 2xx
for (const status of [200, 201]) {
const res = await mf.dispatchFetch(`http://localhost/?status=${status}`, {
method: "POST",
body: JSON.stringify({ data: "test" }),
});
expect(res.status).toBe(status);
expect(await res.json()).toEqual({ method: "POST", status });
}

// POST + non-2xx (previously threw "fetch failed" due to undici bug)
for (const status of [400, 401, 403, 404, 500]) {
const res = await mf.dispatchFetch(`http://localhost/?status=${status}`, {
method: "POST",
body: JSON.stringify({ data: "test" }),
});
expect(res.status).toBe(status);
expect(await res.json()).toEqual({ method: "POST", status });
}

// PUT + non-2xx (also affected by the same bug)
for (const status of [400, 401, 403, 404, 500]) {
const res = await mf.dispatchFetch(`http://localhost/?status=${status}`, {
method: "PUT",
body: JSON.stringify({ data: "test" }),
});
expect(res.status).toBe(status);
expect(await res.json()).toEqual({ method: "PUT", status });
}
});
5 changes: 2 additions & 3 deletions packages/miniflare/test/plugins/r2/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import type {
R2ListOptions,
R2Object,
R2ObjectBody,
R2Objects,
} from "@cloudflare/workers-types/experimental";
import type { MiniflareOptions, ReplaceWorkersTypes } from "miniflare";

Expand Down Expand Up @@ -916,9 +915,9 @@ test("list: returns correct delimitedPrefixes for delimiter and prefix", async (
const allKeys = Object.keys(values);
for (const [key, value] of Object.entries(values)) await r2.put(key, value);

const keys = (result: R2Objects) =>
const keys = (result: Awaited<ReturnType<typeof r2.list>>) =>
result.objects.map(({ key }) => key.substring(ns.length));
const delimitedPrefixes = (result: R2Objects) =>
const delimitedPrefixes = (result: Awaited<ReturnType<typeof r2.list>>) =>
result.delimitedPrefixes.map((prefix) => prefix.substring(ns.length));
const allKeysWithout = (...exclude: string[]) =>
allKeys.filter((value) => !exclude.includes(value));
Expand Down
4 changes: 2 additions & 2 deletions packages/vite-plugin-cloudflare/src/websockets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createHeaders } from "@remix-run/node-fetch-server";
import { CoreHeaders, coupleWebSocket } from "miniflare";
import { WebSocketServer } from "ws";
import { UNKNOWN_HOST } from "./shared";
import type { Miniflare } from "miniflare";
import type { Headers, Miniflare } from "miniflare";
import type { IncomingMessage } from "node:http";
import type { Duplex } from "node:stream";
import type * as vite from "vite";
Expand Down Expand Up @@ -45,7 +45,7 @@ export function handleWebSocket(
}

const response = await miniflare.dispatchFetch(url, {
headers,
headers: headers as unknown as Headers,
method: request.method,
});
const workerWebSocket = response.webSocket;
Expand Down
19 changes: 0 additions & 19 deletions patches/undici@7.24.4.patch

This file was deleted.

Loading
Loading