Skip to content

Commit

Permalink
Load the iframe-worker.html from a separate domain
Browse files Browse the repository at this point in the history
The Origin-Agent-Cluster: ?1 is used to request a separate process for the iframe-worker.html in Google Chrome. It worked perfectly when the root app was served from wasm.wordpress.net and the iframe-worker.html was served from wasm-worker.wordpress.net.

Recently both these domains have been merged to allow direct communication between the Service Worker and the iframe Worker Thread. Now I'm observing the following error message:

> The page requested an origin-keyed agent cluster using the Origin-Agent-Cluster header, but could not be origin-keyed since the origin 'https://wasm.wordpress.net' had previously been placed in a site-keyed agent cluster. Update your headers to uniformly request origin-keying for all pages on the origin.

Accordingly to the Origin-Agent-Cluster docs at https://web.dev/origin-agent-cluster/, the clustered resource must be loaded from a different origin. That’s what this commit does.

As a side-effect, the Service Worker can no longer directly communicate with the Worker Thread since they’re registered on separate origins now. The `worker-thread/window.library.ts` bridges that gap by proxying all the relevant BroadcastChannel communication to the Worker Thread.

Fixes #74
  • Loading branch information
adamziel committed Nov 23, 2022
1 parent 604a87e commit 6d78d38
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 28 deletions.
11 changes: 10 additions & 1 deletion esbuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const wasmWorkerBackend = process.env.WASM_WORKER_BACKEND || 'iframe';
let workerThreadScript;
if (wasmWorkerBackend === 'iframe') {
const wasmWorkerOrigin =
process.env.WASM_WORKER_ORIGIN || 'http://127.0.0.1:8777';
process.env.WASM_WORKER_ORIGIN || 'http://127.0.0.1:8778';
workerThreadScript = `${wasmWorkerOrigin}/iframe-worker.html?${CACHE_BUSTER}`;
} else {
workerThreadScript = `${serviceWorkerOrigin}/worker-thread.js?${CACHE_BUSTER}`;
Expand Down Expand Up @@ -160,6 +160,15 @@ async function main() {
},
],
});

// Serve the iframe worker from a different origin
// to make the Origin-Agent-Cluster header work.
// See https://web.dev/origin-agent-cluster/ for more info.
liveServer.start({
port: 8778,
root: __dirname + '/build',
open: false,
});
}
}

Expand Down
29 changes: 29 additions & 0 deletions src/php-wasm-browser/worker-thread/window-library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
postMessageExpectReply,
awaitReply,
MessageResponse,
responseTo,
} from '../messaging';
import { removeURLScope } from '../scope';
import { getPathQueryFragment } from '..';
Expand Down Expand Up @@ -58,6 +59,34 @@ export async function spawnPHPWorkerThread(
await sleep(50);
}

// Proxy the service worker messages to the worker thread:
const scope = await messageChannel.sendMessage({ type: 'getScope' }, 50);
const broadcastChannel = new BroadcastChannel('php-wasm-browser');
broadcastChannel.addEventListener(
'message',
async function onMessage(event) {
console.debug('broadcastChannel message', event);
/**
* Ignore events meant for other PHP instances to
* avoid handling the same event twice.
*
* This is important because BroadcastChannel transmits
* events to all the listeners across all browser tabs.
*/
if (scope && event.data.scope !== scope) {
return;
}

const result = await messageChannel.sendMessage(event.data);
// The service worker expects a response when it includes a `requestId` in the message:
if (event.data.requestId) {
broadcastChannel.postMessage(
responseTo(event.data.requestId, result)
);
}
}
);

const absoluteUrl = await messageChannel.sendMessage({
type: 'getAbsoluteUrl',
});
Expand Down
29 changes: 2 additions & 27 deletions src/php-wasm-browser/worker-thread/worker-library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ export async function initializeWorkerThread(
config: WorkerThreadConfiguration
): Promise<any> {
const phpBrowser = config.phpBrowser || (await defaultBootBrowser());
const broadcastChannel =
config.broadcastChannel || new BroadcastChannel('php-wasm-browser');

const absoluteUrl = phpBrowser.server.absoluteUrl;
const scope = getURLScope(new URL(absoluteUrl));
Expand All @@ -83,31 +81,6 @@ export async function initializeWorkerThread(
}
});

broadcastChannel.addEventListener(
'message',
async function onMessage(event) {
console.debug('broadcastChannel message', event);
/**
* Ignore events meant for other PHP instances to
* avoid handling the same event twice.
*
* This is important because BroadcastChannel transmits
* events to all the listeners across all browser tabs.
*/
if (scope && event.data.scope !== scope) {
return;
}

const result = await handleMessage(event.data);

// The service worker expects a response when it includes a `requestId` in the message:
if (event.data.requestId) {
const response = responseTo(event.data.requestId, result);
broadcastChannel.postMessage(response);
}
}
);

async function handleMessage(message) {
console.debug(
`[Worker Thread] "${message.type}" message received from a service worker`
Expand All @@ -117,6 +90,8 @@ export async function initializeWorkerThread(
return true;
} else if (message.type === 'getAbsoluteUrl') {
return phpBrowser.server.absoluteUrl;
} else if (message.type === 'getScope') {
return scope;
} else if (message.type === 'readFile') {
return phpBrowser.server.php.readFileAsText(message.path);
} else if (message.type === 'listFiles') {
Expand Down

0 comments on commit 6d78d38

Please sign in to comment.