Skip to content

Commit 5449a79

Browse files
committed
refactor(miniflare): move platform proxy API to cdn-cgi
1 parent e1f29ad commit 5449a79

File tree

5 files changed

+44
-21
lines changed

5 files changed

+44
-21
lines changed

packages/miniflare/src/http/fetch.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,11 @@ export class DispatchFetchDispatcher extends undici.Dispatcher {
197197
path: string // Including query parameters
198198
) {
199199
// Reconstruct URL using runtime origin specified with `dispatchFetch()`
200-
const originalURL = this.userRuntimeOrigin + path;
201-
headers.set(CoreHeaders.ORIGINAL_URL, originalURL);
200+
// Only set if not already present (e.g., set by #fetcherFetchCall in proxy client)
201+
if (!headers.has(CoreHeaders.ORIGINAL_URL)) {
202+
const originalURL = this.userRuntimeOrigin + path;
203+
headers.set(CoreHeaders.ORIGINAL_URL, originalURL);
204+
}
202205
headers.set(CoreHeaders.DISABLE_PRETTY_ERROR, "true");
203206
if (this.cfBlobJson !== undefined) {
204207
// Only add this header if a `cf` override was set

packages/miniflare/src/plugins/core/proxy/client.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { prefixStream, readPrefix } from "../../../shared";
1010
import {
1111
Awaitable,
1212
CoreHeaders,
13+
CorePaths,
1314
createHTTPReducers,
1415
createHTTPRevivers,
1516
isDurableObjectStub,
@@ -97,7 +98,10 @@ export class ProxyClient {
9798
#bridge: ProxyClientBridge;
9899

99100
constructor(runtimeEntryURL: URL, dispatchFetch: DispatchFetch) {
100-
this.#bridge = new ProxyClientBridge(runtimeEntryURL, dispatchFetch);
101+
this.#bridge = new ProxyClientBridge(
102+
new URL(CorePaths.PLATFORM_PROXY, runtimeEntryURL),
103+
dispatchFetch
104+
);
101105
}
102106

103107
// Lazily initialise proxies as required
@@ -120,7 +124,7 @@ export class ProxyClient {
120124
setRuntimeEntryURL(runtimeEntryURL: URL) {
121125
// This function will be called whenever the runtime restarts. The URL may
122126
// be different if the port has changed.
123-
this.#bridge.url = runtimeEntryURL;
127+
this.#bridge.url = new URL(CorePaths.PLATFORM_PROXY, runtimeEntryURL);
124128
}
125129

126130
dispose(): Promise<void> {
@@ -719,9 +723,12 @@ class ProxyStubHandler<T extends object>
719723
#fetcherFetchCall(args: unknown[]) {
720724
// @ts-expect-error `...args` isn't type-safe here, but `undici` should
721725
// validate types at runtime, and throw appropriate errors
722-
const request = new Request(...args);
726+
const userRequest = new Request(...args);
727+
// Create a new request with the proxy URL, preserving the original request
728+
const request = new Request(this.bridge.url, userRequest);
723729
// If adding new headers here, remember to `delete()` them in `ProxyServer`
724730
// before calling `fetch()`.
731+
request.headers.set(CoreHeaders.ORIGINAL_URL, userRequest.url);
725732
request.headers.set(CoreHeaders.OP_SECRET, PROXY_SECRET_HEX);
726733
request.headers.set(CoreHeaders.OP, ProxyOps.CALL);
727734
request.headers.set(CoreHeaders.OP_TARGET, this.#stringifiedTarget);

packages/miniflare/src/plugins/core/proxy/fetch-sync.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,14 @@ let dispatcher;
5050
5151
port.addEventListener("message", async (event) => {
5252
const { id, method, url, headers, body } = event.data;
53-
if (dispatcherUrl !== url) {
54-
dispatcherUrl = url;
55-
dispatcher = new Pool(url, {
56-
connect: { rejectUnauthorized: false },
57-
});
58-
}
59-
headers["${CoreHeaders.OP_SYNC}"] = "true";
6053
try {
54+
if (dispatcherUrl !== url) {
55+
dispatcherUrl = url;
56+
dispatcher = new Pool(new URL(url).origin, {
57+
connect: { rejectUnauthorized: false },
58+
});
59+
}
60+
headers["${CoreHeaders.OP_SYNC}"] = "true";
6161
// body cannot be a ReadableStream, so no need to specify duplex
6262
const response = await fetch(url, { method, headers, body, dispatcher });
6363
const responseBody = response.headers.get("${CoreHeaders.OP_RESULT_TYPE}") === "ReadableStream"
@@ -82,9 +82,10 @@ port.addEventListener("message", async (event) => {
8282
// If error failed to serialise, post simplified version
8383
port.postMessage({ id, error: new Error(String(error)) });
8484
}
85+
} finally {
86+
Atomics.store(notifyHandle, /* index */ 0, /* value */ 1);
87+
Atomics.notify(notifyHandle, /* index */ 0);
8588
}
86-
Atomics.store(notifyHandle, /* index */ 0, /* value */ 1);
87-
Atomics.notify(notifyHandle, /* index */ 0);
8889
});
8990
9091
port.start();

packages/miniflare/src/workers/core/entry.worker.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -382,9 +382,14 @@ export default <ExportedHandler<Env>>{
382382
};
383383
request = new Request(request, { cf });
384384

385-
// The magic proxy client (used by getPlatformProxy) will always specify an operation
386-
const isProxy = request.headers.get(CoreHeaders.OP) !== null;
387-
if (isProxy) return handleProxy(request, env);
385+
// The magic proxy client (used by getPlatformProxy)
386+
if (new URL(request.url).pathname === CorePaths.PLATFORM_PROXY) {
387+
if (request.headers.get(CoreHeaders.OP) !== null) {
388+
return handleProxy(request, env);
389+
}
390+
391+
return new Response("Invalid proxy request", { status: 400 });
392+
}
388393

389394
// `dispatchFetch()` will always inject this header. When
390395
// calling this function, we never want to display the pretty-error page.

packages/miniflare/test/plugins/core/proxy/client.spec.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { text } from "node:stream/consumers";
55
import { ReadableStream, WritableStream } from "node:stream/web";
66
import util from "node:util";
77
import {
8+
CorePaths,
89
DeferredPromise,
910
fetch,
1011
MessageEvent,
@@ -329,32 +330,38 @@ describe("ProxyClient", () => {
329330
const mf = new Miniflare({ script: nullScript });
330331
useDispose(mf);
331332
const url = await mf.ready;
333+
const proxyUrl = new URL(CorePaths.PLATFORM_PROXY, url);
332334

333335
// Check validates `Host` header
334336
const statusPromise = new DeferredPromise<number>();
335337
const req = http.get(
336-
url,
338+
proxyUrl,
337339
{ setHost: false, headers: { "MF-Op": "GET", Host: "localhost" } },
338340
(res) => statusPromise.resolve(res.statusCode ?? 0)
339341
);
340342
req.on("error", (error) => statusPromise.reject(error));
341343
expect(await statusPromise).toBe(401);
342344

343345
// Check validates `MF-Op-Secret` header
344-
let res = await fetch(url, {
346+
let res = await fetch(proxyUrl, {
345347
headers: { "MF-Op": "GET" }, // (missing)
346348
});
347349
expect(res.status).toBe(401);
348350
await res.arrayBuffer(); // (drain)
349-
res = await fetch(url, {
351+
res = await fetch(proxyUrl, {
350352
headers: { "MF-Op": "GET", "MF-Op-Secret": "aaaa" }, // (too short)
351353
});
352354
expect(res.status).toBe(401);
353355
await res.arrayBuffer(); // (drain)
354-
res = await fetch(url, {
356+
res = await fetch(proxyUrl, {
355357
headers: { "MF-Op": "GET", "MF-Op-Secret": "a".repeat(32) }, // (wrong)
356358
});
357359
expect(res.status).toBe(401);
358360
await res.arrayBuffer(); // (drain)
361+
362+
// Check requests to proxy path without MF-Op header return 400
363+
res = await fetch(proxyUrl);
364+
expect(res.status).toBe(400);
365+
await res.arrayBuffer(); // (drain)
359366
});
360367
});

0 commit comments

Comments
 (0)