Skip to content

Commit b687dff

Browse files
edmundhungpenalosa
andauthored
fix(playground-preview-worker,edge-preview-authenticated-proxy): allow overriding raw request method with the X-CF-Http-Method header (#7630)
Co-authored-by: Samuel Macleod <[email protected]>
1 parent 8fb0f25 commit b687dff

File tree

5 files changed

+91
-0
lines changed

5 files changed

+91
-0
lines changed

.changeset/dull-coins-scream.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"edge-preview-authenticated-proxy": patch
3+
"playground-preview-worker": patch
4+
---
5+
6+
fix OPTIONS raw http request support by overriding raw request method with the X-CF-Http-Method header

packages/edge-preview-authenticated-proxy/src/index.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,9 @@ async function handleRawHttp(request: Request, url: URL) {
204204
const token = requestHeaders.get("X-CF-Token");
205205
const remote = requestHeaders.get("X-CF-Remote");
206206

207+
// Fallback to the request method for backward compatiblility
208+
const method = requestHeaders.get("X-CF-Http-Method") ?? request.method;
209+
207210
if (!token || !remote) {
208211
throw new RawHttpFailed();
209212
}
@@ -216,6 +219,7 @@ async function handleRawHttp(request: Request, url: URL) {
216219
// request due to exceeding size limits if the value is included twice.
217220
requestHeaders.delete("X-CF-Token");
218221
requestHeaders.delete("X-CF-Remote");
222+
requestHeaders.delete("X-CF-Http-Method");
219223

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

@@ -229,6 +233,7 @@ async function handleRawHttp(request: Request, url: URL) {
229233
const workerResponse = await fetch(
230234
switchRemote(url, remote),
231235
new Request(request, {
236+
method,
232237
headers: requestHeaders,
233238
redirect: "manual",
234239
})
@@ -245,6 +250,20 @@ async function handleRawHttp(request: Request, url: URL) {
245250
Vary: "Origin",
246251
});
247252

253+
// Pass the raw content type back so that clients can decode the body correctly
254+
const contentType = responseHeaders.get("Content-Type");
255+
if (contentType) {
256+
rawHeaders.set("Content-Type", contentType);
257+
}
258+
const contentEncoding = responseHeaders.get("Content-Encoding");
259+
if (contentEncoding) {
260+
rawHeaders.set("Content-Encoding", contentEncoding);
261+
}
262+
const transferEncoding = responseHeaders.get("Transfer-Encoding");
263+
if (transferEncoding) {
264+
rawHeaders.set("Transfer-Encoding", transferEncoding);
265+
}
266+
248267
// The client needs the raw headers from the worker
249268
// Prefix them with `cf-ew-raw-`, so that response headers from _this_ worker don't interfere
250269
const setCookieHeader = responseHeaders.getSetCookie();

packages/edge-preview-authenticated-proxy/tests/index.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,41 @@ compatibility_date = "2023-01-01"
469469
`);
470470
});
471471

472+
it("should use the method specified on the X-CF-Http-Method header", async () => {
473+
const token = randomBytes(4096).toString("hex");
474+
const resp = await worker.fetch(
475+
`https://0000.rawhttp.devprod.cloudflare.dev/method`,
476+
{
477+
method: "POST",
478+
headers: {
479+
origin: "https://cloudflare.dev",
480+
"X-CF-Token": token,
481+
"X-CF-Remote": `http://127.0.0.1:${remote.port}`,
482+
"X-CF-Http-Method": "PUT",
483+
},
484+
}
485+
);
486+
487+
expect(await resp.text()).toEqual("PUT");
488+
});
489+
490+
it("should fallback to the request method if the X-CF-Http-Method header is missing", async () => {
491+
const token = randomBytes(4096).toString("hex");
492+
const resp = await worker.fetch(
493+
`https://0000.rawhttp.devprod.cloudflare.dev/method`,
494+
{
495+
method: "PUT",
496+
headers: {
497+
origin: "https://cloudflare.dev",
498+
"X-CF-Token": token,
499+
"X-CF-Remote": `http://127.0.0.1:${remote.port}`,
500+
},
501+
}
502+
);
503+
504+
expect(await resp.text()).toEqual("PUT");
505+
});
506+
472507
it("should strip cf-ew-raw- prefix from headers which have it before hitting the user-worker", async () => {
473508
const token = randomBytes(4096).toString("hex");
474509
const resp = await worker.fetch(

packages/playground-preview-worker/src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ async function handleRawHttp(request: Request, url: URL, env: Env) {
6363
// request due to exceeding size limits if the value is included twice.
6464

6565
const headers = new Headers(request.headers);
66+
67+
// Fallback to the request method for backward compatiblility
68+
const method = request.headers.get("X-CF-Http-Method") ?? request.method;
69+
70+
headers.delete("X-CF-Http-Method");
6671
headers.delete("X-CF-Token");
6772

6873
const headerEntries = [...headers.entries()];
@@ -77,6 +82,7 @@ async function handleRawHttp(request: Request, url: URL, env: Env) {
7782
const workerResponse = await userObject.fetch(
7883
url,
7984
new Request(request, {
85+
method,
8086
headers: {
8187
...Object.fromEntries(headers),
8288
"cf-run-user-worker": "true",

packages/playground-preview-worker/tests/index.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,31 @@ describe("Preview Worker", () => {
244244
);
245245
expect(await resp.text()).toMatchInlineSnapshot('"custom"');
246246
});
247+
it("should return method specified on the X-CF-Http-Method header", async () => {
248+
const resp = await fetch(`${PREVIEW_REMOTE}/method`, {
249+
method: "POST",
250+
headers: {
251+
"X-CF-Token": defaultUserToken,
252+
"X-CF-Http-Method": "PUT",
253+
"CF-Raw-HTTP": "true",
254+
},
255+
redirect: "manual",
256+
});
257+
258+
expect(await resp.text()).toEqual("PUT");
259+
});
260+
it("should fallback to the request method if the X-CF-Http-Method header is missing", async () => {
261+
const resp = await fetch(`${PREVIEW_REMOTE}/method`, {
262+
method: "PUT",
263+
headers: {
264+
"X-CF-Token": defaultUserToken,
265+
"CF-Raw-HTTP": "true",
266+
},
267+
redirect: "manual",
268+
});
269+
270+
expect(await resp.text()).toEqual("PUT");
271+
});
247272
it("should reject no token for raw HTTP response", async () => {
248273
const resp = await fetch(`${PREVIEW_REMOTE}/header`, {
249274
headers: {

0 commit comments

Comments
 (0)