diff --git a/packages/playground/blueprints/src/lib/compile.ts b/packages/playground/blueprints/src/lib/compile.ts index 30c31a9cbd..98de27178d 100644 --- a/packages/playground/blueprints/src/lib/compile.ts +++ b/packages/playground/blueprints/src/lib/compile.ts @@ -65,11 +65,11 @@ export interface CompileBlueprintOptions { /** * Proxy URL to use for cross-origin requests. * - * For example, if corsProxy is set to "https://cors.wordpress.net/proxy.php", + * For example, if corsProxyUrl is set to "https://cors.wordpress.net/proxy.php", * then the CORS requests to https://github.com/WordPress/gutenberg.git would actually * be made to https://cors.wordpress.net/proxy.php?https://github.com/WordPress/gutenberg.git. */ - corsProxy?: string; + corsProxyUrl?: string; } /** @@ -86,7 +86,7 @@ export function compileBlueprint( progress = new ProgressTracker(), semaphore = new Semaphore({ concurrency: 3 }), onStepCompleted = () => {}, - corsProxy, + corsProxyUrl, }: CompileBlueprintOptions = {} ): CompiledBlueprint { // Deep clone the blueprint to avoid mutating the input @@ -260,7 +260,7 @@ export function compileBlueprint( semaphore, rootProgressTracker: progress, totalProgressWeight, - corsProxy, + corsProxyUrl, }) ); @@ -422,9 +422,9 @@ interface CompileStepArgsOptions { /** * Proxy URL to use for cross-origin requests. * - * @see CompileBlueprintOptions.corsProxy + * @see CompileBlueprintOptions.corsProxyUrl */ - corsProxy?: string; + corsProxyUrl?: string; } /** @@ -441,7 +441,7 @@ function compileStep( semaphore, rootProgressTracker, totalProgressWeight, - corsProxy, + corsProxyUrl, }: CompileStepArgsOptions ): { run: CompiledStep; step: S; resources: Array> } { const stepProgress = rootProgressTracker.stage( @@ -454,7 +454,7 @@ function compileStep( if (isResourceReference(value)) { value = Resource.create(value, { semaphore, - corsProxy, + corsProxyUrl, }); } args[key] = value; diff --git a/packages/playground/blueprints/src/lib/resources.ts b/packages/playground/blueprints/src/lib/resources.ts index e9524aa21a..f21e14b098 100644 --- a/packages/playground/blueprints/src/lib/resources.ts +++ b/packages/playground/blueprints/src/lib/resources.ts @@ -134,12 +134,12 @@ export abstract class Resource { { semaphore, progress, - corsProxy, + corsProxyUrl, }: { /** Optional semaphore to limit concurrent downloads */ semaphore?: Semaphore; progress?: ProgressTracker; - corsProxy?: string; + corsProxyUrl?: string; } ): Resource { let resource: Resource; @@ -161,7 +161,7 @@ export abstract class Resource { break; case 'git:directory': resource = new GitDirectoryResource(ref, progress, { - corsProxy, + corsProxyUrl, }); break; case 'literal:directory': @@ -452,14 +452,14 @@ export class GitDirectoryResource extends Resource { constructor( private reference: GitDirectoryReference, public override _progress?: ProgressTracker, - private options?: { corsProxy?: string } + private options?: { corsProxyUrl?: string } ) { super(); } async resolve() { - const repoUrl = this.options?.corsProxy - ? `${this.options.corsProxy}?${this.reference.url}` + const repoUrl = this.options?.corsProxyUrl + ? `${this.options.corsProxyUrl}?${this.reference.url}` : this.reference.url; const ref = ['', 'HEAD'].includes(this.reference.ref) ? 'HEAD' diff --git a/packages/playground/client/src/index.ts b/packages/playground/client/src/index.ts index 7e7a891f43..0818447f09 100644 --- a/packages/playground/client/src/index.ts +++ b/packages/playground/client/src/index.ts @@ -78,7 +78,7 @@ export interface StartPlaygroundOptions { /** * Proxy URL to use for cross-origin requests. * - * For example, if corsProxy is set to "https://cors.wordpress.net/proxy.php", + * For example, if corsProxyUrl is set to "https://cors.wordpress.net/proxy.php", * then the CORS requests to https://github.com/WordPress/wordpress-playground.git would actually * be made to https://cors.wordpress.net/proxy.php?https://github.com/WordPress/wordpress-playground.git. * @@ -86,7 +86,7 @@ export interface StartPlaygroundOptions { * to proxy every single request, do not use this option. Instead, you should preprocess * your Blueprint to replace all cross-origin URLs with the proxy URL. */ - corsProxy?: string; + corsProxyUrl?: string; } /** @@ -108,7 +108,7 @@ export async function startPlaygroundWeb({ onBeforeBlueprint, mounts, scope, - corsProxy, + corsProxyUrl, shouldInstallWordPress, }: StartPlaygroundOptions): Promise { assertValidRemote(remoteUrl); @@ -127,7 +127,7 @@ export async function startPlaygroundWeb({ const compiled = compileBlueprint(blueprint, { progress: progressTracker.stage(0.5), onStepCompleted: onBlueprintStepCompleted, - corsProxy, + corsProxyUrl, }); await new Promise((resolve) => { @@ -153,6 +153,7 @@ export async function startPlaygroundWeb({ phpVersion: compiled.versions.php, wpVersion: compiled.versions.wp, withNetworking: compiled.features.networking, + corsProxyUrl, }); await playground.isReady(); downloadPHPandWP.finish(); diff --git a/packages/playground/php-cors-proxy/cors-proxy-dev-setup.php b/packages/playground/php-cors-proxy/cors-proxy-dev-setup.php new file mode 100644 index 0000000000..5f39c0a763 --- /dev/null +++ b/packages/playground/php-cors-proxy/cors-proxy-dev-setup.php @@ -0,0 +1,4 @@ +; - boot(options: WorkerBootOptions): Promise; + boot(options: WebClientBootOptions): Promise; } /** diff --git a/packages/playground/remote/src/lib/setup-fetch-network-transport.ts b/packages/playground/remote/src/lib/setup-fetch-network-transport.ts index 87c0912282..a0279351ce 100644 --- a/packages/playground/remote/src/lib/setup-fetch-network-transport.ts +++ b/packages/playground/remote/src/lib/setup-fetch-network-transport.ts @@ -19,7 +19,10 @@ export interface RequestMessage { * * @param playground the Playground instance to set up with network support. */ -export async function setupFetchNetworkTransport(playground: UniversalPHP) { +export async function setupFetchNetworkTransport( + playground: UniversalPHP, + { corsProxyUrl }: { corsProxyUrl?: string } +) { await defineWpConfigConsts(playground, { consts: { USE_FETCH_FOR_REQUESTS: true, @@ -58,24 +61,31 @@ export async function setupFetchNetworkTransport(playground: UniversalPHP) { data.headers['x-request-issuer'] = 'php'; } - return handleRequest(data); + return handleRequest(data, { + corsProxyUrl, + }); }); } -export async function handleRequest(data: RequestData, fetchFn = fetch) { +export async function handleRequest( + data: RequestData, + options: { corsProxyUrl?: string; fetchFn?: typeof fetch } +) { + const { corsProxyUrl, fetchFn = fetch } = options; const hostname = new URL(data.url).hostname; - const fetchUrl = ['w.org', 's.w.org'].includes(hostname) + const isWdotOrg = ['w.org', 's.w.org'].includes(hostname); + const fetchUrl = isWdotOrg ? `/plugin-proxy.php?url=${encodeURIComponent(data.url)}` : data.url; + const fetchMethod = data.method || 'GET'; + const fetchHeaders = data.headers || {}; + if (fetchMethod == 'POST') { + fetchHeaders['Content-Type'] = 'application/x-www-form-urlencoded'; + } + let response; try { - const fetchMethod = data.method || 'GET'; - const fetchHeaders = data.headers || {}; - if (fetchMethod == 'POST') { - fetchHeaders['Content-Type'] = 'application/x-www-form-urlencoded'; - } - response = await fetchFn(fetchUrl, { method: fetchMethod, headers: fetchHeaders, @@ -83,9 +93,23 @@ export async function handleRequest(data: RequestData, fetchFn = fetch) { credentials: 'omit', }); } catch (e) { - return new TextEncoder().encode( - `HTTP/1.1 400 Invalid Request\r\ncontent-type: text/plain\r\n\r\nPlayground could not serve the request.` - ); + if (isWdotOrg || !corsProxyUrl) { + return new TextEncoder().encode( + `HTTP/1.1 400 Invalid Request\r\ncontent-type: text/plain\r\n\r\nPlayground could not serve the request.` + ); + } + try { + response = await fetchFn(`${corsProxyUrl}?${fetchUrl}`, { + method: fetchMethod, + headers: fetchHeaders, + body: data.data, + credentials: 'omit', + }); + } catch (e) { + return new TextEncoder().encode( + `HTTP/1.1 400 Invalid Request\r\ncontent-type: text/plain\r\n\r\nPlayground could not serve the request.` + ); + } } const responseHeaders: string[] = []; response.headers.forEach((value, key) => { diff --git a/packages/playground/remote/src/test/setup-fetch-network-transport.spec.ts b/packages/playground/remote/src/test/setup-fetch-network-transport.spec.ts index c86a123052..0d75faa138 100644 --- a/packages/playground/remote/src/test/setup-fetch-network-transport.spec.ts +++ b/packages/playground/remote/src/test/setup-fetch-network-transport.spec.ts @@ -19,7 +19,7 @@ describe('handleRequest', () => { url: 'https://playground.wordpress.net/', headers: { 'Content-type': 'text/html' }, }, - fetchMock as any + { fetchFn: fetchMock as any } ); expect(new TextDecoder().decode(response)).toBe( `HTTP/1.1 200 OK\r\ncontent-type: text/html\r\n\r\nHello, world!` @@ -43,7 +43,7 @@ describe('handleRequest', () => { url: 'https://playground.wordpress.net/', headers: { 'Content-type': 'text/html' }, }, - fetchMock as any + { fetchFn: fetchMock as any } ); expect(new TextDecoder().decode(response)).toBe( `HTTP/1.1 400 Invalid Request\r\ncontent-type: text/plain\r\n\r\nPlayground could not serve the request.` diff --git a/packages/playground/website/src/lib/state/redux/boot-site-client.ts b/packages/playground/website/src/lib/state/redux/boot-site-client.ts index 6543c086dd..7045c043f8 100644 --- a/packages/playground/website/src/lib/state/redux/boot-site-client.ts +++ b/packages/playground/website/src/lib/state/redux/boot-site-client.ts @@ -133,7 +133,7 @@ export function bootSiteClient( ] : [], shouldInstallWordPress: !isWordPressInstalled, - corsProxy: corsProxyUrl, + corsProxyUrl, }); // @TODO: Remove backcompat code after 2024-12-01.