Skip to content
Open
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
9 changes: 9 additions & 0 deletions .changeset/v1-r2-s3-signed-encoding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@cloudflare/vite-plugin": patch
---

Fix signed Accept-Encoding headers on the experimental local R2 S3 endpoint

Upgrade Miniflare to `5.20261023.3-beta` and Wrangler to `4.156.0` so S3 clients that sign `Accept-Encoding: identity` can use the endpoint exposed by Vite development and preview without `SignatureDoesNotMatch` errors.

Preserve the original S3 request header in the Vite request adapter so the runtime verifies the exact value signed by the client.
Comment thread
petebacondarwin marked this conversation as resolved.
Resolved
2 changes: 1 addition & 1 deletion fixtures/dev-registry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
"typescript": "catalog:default",
"vite": "catalog:default",
"vitest": "catalog:default",
"wrangler": "4.155.0"
"wrangler": "4.156.0"
}
}
4 changes: 2 additions & 2 deletions fixtures/shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"private": true,
"description": "Shared fixtures for testing",
"devDependencies": {
"@cloudflare/workers-utils": "0.50.0",
"wrangler": "4.155.0"
"@cloudflare/workers-utils": "0.50.1",
"wrangler": "4.156.0"
}
}
2 changes: 1 addition & 1 deletion packages/mock-npm-registry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"test:ci": "vitest run"
},
"devDependencies": {
"@cloudflare/workers-utils": "0.50.0",
"@cloudflare/workers-utils": "0.50.1",
"@types/node": "catalog:default",
"@verdaccio/types": "^10.8.0",
"get-port": "^7.0.0",
Expand Down
8 changes: 4 additions & 4 deletions packages/remote-bindings/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@
"test:watch": "vitest"
},
"dependencies": {
"@cloudflare/cli-shared-helpers": "0.2.10",
"@cloudflare/deploy-helpers": "0.24.2",
"@cloudflare/cli-shared-helpers": "0.3.0",
"@cloudflare/deploy-helpers": "0.24.3",
"@cloudflare/workers-auth": "0.15.1",
"@cloudflare/workers-utils": "0.50.0",
"@cloudflare/workers-utils": "0.50.1",
"chalk": "catalog:default",
"miniflare": "5.20261023.2-beta",
"miniflare": "5.20261023.3-beta",
"undici": "catalog:default",
"ws": "catalog:default"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"@cloudflare/vite-plugin": "*",
"@cloudflare/workers-types": "^5.20260706.1",
"globals": "^15.14.0",
"miniflare": "5.20261023.2-beta",
"miniflare": "5.20261023.3-beta",
"typescript": "~5.7.2",
"vite": "^8.0.0",
"wrangler": "*"
Expand Down
10 changes: 5 additions & 5 deletions packages/vite-plugin-cloudflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,19 @@
},
"dependencies": {
"@cloudflare/unenv-preset": "2.16.2",
"miniflare": "5.20261023.2-beta",
"miniflare": "5.20261023.3-beta",
"unenv": "2.0.0-rc.24",
"wrangler": "4.155.0",
"wrangler": "4.156.0",
"ws": "8.22.0"
},
"devDependencies": {
"@cloudflare/containers-shared": "0.23.4",
"@cloudflare/containers-shared": "0.23.5",
"@cloudflare/mock-npm-registry": "workspace:*",
"@cloudflare/remote-bindings": "workspace:*",
"@cloudflare/workers-shared": "workspace:*",
"@cloudflare/workers-tsconfig": "workspace:*",
"@cloudflare/workers-types": "5.20261023.1",
"@cloudflare/workers-utils": "0.50.0",
"@cloudflare/workers-utils": "0.50.1",
"@remix-run/node-fetch-server": "1.0.0",
"@types/node": "22.15.17",
"@types/semver": "7.8.0",
Expand All @@ -82,7 +82,7 @@
},
"peerDependencies": {
"vite": "^6.1.0 || ^7.0.0 || ^8.0.0",
"wrangler": "^4.155.0"
"wrangler": "^4.156.0"
},
"workers-sdk": {
"prerelease": true
Expand Down
55 changes: 55 additions & 0 deletions packages/vite-plugin-cloudflare/src/__tests__/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,61 @@ describe("createRequestHandler", () => {
);
}

test.for([
{
pathname: "/cdn-cgi/local/r2/s3",
encoding: "identity",
expected: "identity",
},
{
pathname: "/cdn-cgi/local/r2/s3/bucket/object",
encoding: "identity",
expected: "identity",
},
{
pathname: "/cdn-cgi/local/r2/s3/bucket/object",
encoding: "gzip, deflate",
expected: "gzip, deflate",
},
{
pathname: "/cdn-cgi/local/r2/s3/bucket/object",
encoding: undefined,
expected: null,
},
{
pathname: "/cdn-cgi/local/r2/s3-other",
encoding: "gzip",
expected: "identity, gzip",
},
{ pathname: "/worker", encoding: "gzip", expected: "identity, gzip" },
])(
"forwards Accept-Encoding for $pathname ($encoding)",
async ({ pathname, encoding, expected }, { expect }) => {
await startServer(async (request) =>
MiniflareResponse.json({
acceptEncoding: request.headers.get("accept-encoding"),
})
);
// Use Node HTTP because Fetch supplies its own Accept-Encoding when absent.
const response = await new Promise<http.IncomingMessage>(
(resolve, reject) => {
http
.get(
`http://127.0.0.1:${port}${pathname}`,
{
headers:
encoding === undefined ? {} : { "Accept-Encoding": encoding },
},
resolve
)
.on("error", reject);
}
);
expect(response.statusCode).toBe(200);
expect(await json(response)).toEqual({ acceptEncoding: expected });
}
);

test("preserves repeated request Set-Cookie headers", async ({ expect }) => {
await startServer(async (request) =>
MiniflareResponse.json({
Expand Down
10 changes: 9 additions & 1 deletion packages/vite-plugin-cloudflare/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ function createCancellableRequestBody(
});
}

/** Convert a Vite request for the local runtime, preserving signed S3 headers. */
export function toMiniflareRequest(request: Request): MiniflareRequest {
const host = request.headers.get("Host") ?? new URL(request.url).host;
const xForwardedHost = request.headers.get("X-Forwarded-Host");
Expand All @@ -259,9 +260,16 @@ export function toMiniflareRequest(request: Request): MiniflareRequest {
if (secFetchMode) {
request.headers.set(CoreHeaders.SEC_FETCH_MODE, secFetchMode);
}
const pathname = new URL(request.url).pathname;
// S3 signatures include the original header value; adding identity invalidates them.
const isLocalS3Request =
pathname === "/cdn-cgi/local/r2/s3" ||
pathname.startsWith("/cdn-cgi/local/r2/s3/");
return new MiniflareRequest(request.url, {
method: request.method,
headers: [["accept-encoding", "identity"], ...request.headers],
headers: isLocalS3Request
? [...request.headers]
: [["accept-encoding", "identity"], ...request.headers],
body: request.body,
duplex: "half",
signal: request.signal,
Expand Down
Loading
Loading