Skip to content

Commit

Permalink
fix(playground-preview-worker,edge-preview-authenticated-proxy): all…
Browse files Browse the repository at this point in the history
…ow overriding raw request method with the X-CF-Http-Method header (#7630)

Co-authored-by: Samuel Macleod <[email protected]>
  • Loading branch information
edmundhung and penalosa authored Jan 13, 2025
1 parent 8fb0f25 commit b687dff
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .changeset/dull-coins-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"edge-preview-authenticated-proxy": patch
"playground-preview-worker": patch
---

fix OPTIONS raw http request support by overriding raw request method with the X-CF-Http-Method header
19 changes: 19 additions & 0 deletions packages/edge-preview-authenticated-proxy/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ async function handleRawHttp(request: Request, url: URL) {
const token = requestHeaders.get("X-CF-Token");
const remote = requestHeaders.get("X-CF-Remote");

// Fallback to the request method for backward compatiblility
const method = requestHeaders.get("X-CF-Http-Method") ?? request.method;

if (!token || !remote) {
throw new RawHttpFailed();
}
Expand All @@ -216,6 +219,7 @@ async function handleRawHttp(request: Request, url: URL) {
// request due to exceeding size limits if the value is included twice.
requestHeaders.delete("X-CF-Token");
requestHeaders.delete("X-CF-Remote");
requestHeaders.delete("X-CF-Http-Method");

const headerEntries = [...requestHeaders.entries()];

Expand All @@ -229,6 +233,7 @@ async function handleRawHttp(request: Request, url: URL) {
const workerResponse = await fetch(
switchRemote(url, remote),
new Request(request, {
method,
headers: requestHeaders,
redirect: "manual",
})
Expand All @@ -245,6 +250,20 @@ async function handleRawHttp(request: Request, url: URL) {
Vary: "Origin",
});

// Pass the raw content type back so that clients can decode the body correctly
const contentType = responseHeaders.get("Content-Type");
if (contentType) {
rawHeaders.set("Content-Type", contentType);
}
const contentEncoding = responseHeaders.get("Content-Encoding");
if (contentEncoding) {
rawHeaders.set("Content-Encoding", contentEncoding);
}
const transferEncoding = responseHeaders.get("Transfer-Encoding");
if (transferEncoding) {
rawHeaders.set("Transfer-Encoding", transferEncoding);
}

// The client needs the raw headers from the worker
// Prefix them with `cf-ew-raw-`, so that response headers from _this_ worker don't interfere
const setCookieHeader = responseHeaders.getSetCookie();
Expand Down
35 changes: 35 additions & 0 deletions packages/edge-preview-authenticated-proxy/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,41 @@ compatibility_date = "2023-01-01"
`);
});

it("should use the method specified on the X-CF-Http-Method header", async () => {
const token = randomBytes(4096).toString("hex");
const resp = await worker.fetch(
`https://0000.rawhttp.devprod.cloudflare.dev/method`,
{
method: "POST",
headers: {
origin: "https://cloudflare.dev",
"X-CF-Token": token,
"X-CF-Remote": `http://127.0.0.1:${remote.port}`,
"X-CF-Http-Method": "PUT",
},
}
);

expect(await resp.text()).toEqual("PUT");
});

it("should fallback to the request method if the X-CF-Http-Method header is missing", async () => {
const token = randomBytes(4096).toString("hex");
const resp = await worker.fetch(
`https://0000.rawhttp.devprod.cloudflare.dev/method`,
{
method: "PUT",
headers: {
origin: "https://cloudflare.dev",
"X-CF-Token": token,
"X-CF-Remote": `http://127.0.0.1:${remote.port}`,
},
}
);

expect(await resp.text()).toEqual("PUT");
});

it("should strip cf-ew-raw- prefix from headers which have it before hitting the user-worker", async () => {
const token = randomBytes(4096).toString("hex");
const resp = await worker.fetch(
Expand Down
6 changes: 6 additions & 0 deletions packages/playground-preview-worker/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ async function handleRawHttp(request: Request, url: URL, env: Env) {
// request due to exceeding size limits if the value is included twice.

const headers = new Headers(request.headers);

// Fallback to the request method for backward compatiblility
const method = request.headers.get("X-CF-Http-Method") ?? request.method;

headers.delete("X-CF-Http-Method");
headers.delete("X-CF-Token");

const headerEntries = [...headers.entries()];
Expand All @@ -77,6 +82,7 @@ async function handleRawHttp(request: Request, url: URL, env: Env) {
const workerResponse = await userObject.fetch(
url,
new Request(request, {
method,
headers: {
...Object.fromEntries(headers),
"cf-run-user-worker": "true",
Expand Down
25 changes: 25 additions & 0 deletions packages/playground-preview-worker/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,31 @@ describe("Preview Worker", () => {
);
expect(await resp.text()).toMatchInlineSnapshot('"custom"');
});
it("should return method specified on the X-CF-Http-Method header", async () => {
const resp = await fetch(`${PREVIEW_REMOTE}/method`, {
method: "POST",
headers: {
"X-CF-Token": defaultUserToken,
"X-CF-Http-Method": "PUT",
"CF-Raw-HTTP": "true",
},
redirect: "manual",
});

expect(await resp.text()).toEqual("PUT");
});
it("should fallback to the request method if the X-CF-Http-Method header is missing", async () => {
const resp = await fetch(`${PREVIEW_REMOTE}/method`, {
method: "PUT",
headers: {
"X-CF-Token": defaultUserToken,
"CF-Raw-HTTP": "true",
},
redirect: "manual",
});

expect(await resp.text()).toEqual("PUT");
});
it("should reject no token for raw HTTP response", async () => {
const resp = await fetch(`${PREVIEW_REMOTE}/header`, {
headers: {
Expand Down

0 comments on commit b687dff

Please sign in to comment.