Skip to content

Commit 06fed35

Browse files
committed
fix: load Worker entries served over HTTP
new Worker() resolved every specifier as a file and read it from disk, so a dev server URL (new URL('./x.worker.ts', import.meta.url) under HMR) failed with os error 123. HTTP entries now skip the disk read and the worker's thread imports them through the native module loader, so the entry's own imports resolve against the URL. A failed load is reported as a worker error.
1 parent b02d1d7 commit 06fed35

3 files changed

Lines changed: 33 additions & 1 deletion

File tree

‎runtime/src/esm_http_test.rs‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,24 @@ fn http_import_failures_reject_with_the_cause() {
248248
assert_eq!(invalid, "TypeError");
249249
}
250250

251+
#[test]
252+
fn worker_entry_served_over_http() {
253+
let server = TestServer::start();
254+
let o = server.origin.clone();
255+
server.route("/ns/m/echo.worker.js", "text/javascript", "import { add } from './add.js';\nself.onmessage = (event) => self.postMessage(add(event.data));");
256+
server.route("/ns/m/add.js", "text/javascript", "export const add = (n) => n + 1;");
257+
let mut runtime = Runtime::new(".");
258+
runtime.register_delegate_isolate_ptr();
259+
let run = |path: &str| {
260+
format!(
261+
"new Promise((resolve, reject) => {{ const worker = new Worker(new URL('{o}{path}')); worker.onmessage = (event) => {{ worker.terminate(); event.type === 'messageerror' ? reject(event.error) : resolve(event.data); }}; worker.postMessage(41); }})"
262+
)
263+
};
264+
assert_eq!(await_js(&mut runtime, &run("/ns/m/echo.worker.js")), "ok:42");
265+
let missing = await_js(&mut runtime, &run("/ns/m/missing.worker.js"));
266+
assert!(missing.starts_with("err:") && missing.contains("status=404"), "{missing}");
267+
}
268+
251269
#[test]
252270
fn native_websocket_round_trip() {
253271
let server = TestServer::start();

‎runtime/src/worker_support.rs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ pub fn install_worker_runtime(scope: &mut ContextScope<HandleScope>) {
2626
return { source: input, filename: '[worker:eval]' };
2727
}
2828
29+
// A dev server's URL (`new URL('./x.worker.ts', import.meta.url)` under HMR) isn't a
30+
// file: the worker's thread imports it, so its own imports resolve against the URL.
31+
if (/^https?:\/\//.test(input)) {
32+
return { source: '', filename: input };
33+
}
34+
2935
if (typeof globalThis.__nsResolveModulePath !== 'function' || typeof globalThis.__nsReadTextFile !== 'function') {
3036
throw new Error('Worker file loading is unavailable: missing runtime host hooks');
3137
}
@@ -90,6 +96,10 @@ pub fn install_worker_runtime(scope: &mut ContextScope<HandleScope>) {
9096
typeof globalThis.__nsWorkerPollMessagesBlocking === 'function' &&
9197
typeof globalThis.__nsWorkerTerminate === 'function';
9298
99+
if (!canUseThreadedHost && /^https?:\/\//.test(resolved.filename)) {
100+
throw new Error('Loading a Worker from a URL needs the threaded worker host: ' + resolved.filename);
101+
}
102+
93103
var channel = new MessageChannel();
94104
var mainPort = channel.port1;
95105
var workerPort = channel.port2;

‎runtime/src/worker_threads.rs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,11 @@ fn worker_bootstrap_script(source: &str, filename: &str) -> Result<String, Strin
173173
}});
174174
}};
175175
176-
if (typeof globalThis.__nsEvalAsModule === 'function') {{
176+
if (/^https?:\/\//.test(__workerFilename)) {{
177+
import(__workerFilename).catch(function (error) {{
178+
__queueMessage({{ __workerError: String((error && error.message) || error) }});
179+
}});
180+
}} else if (typeof globalThis.__nsEvalAsModule === 'function') {{
177181
globalThis.__nsEvalAsModule(__workerSource, __workerFilename || '[worker]');
178182
}} else {{
179183
const exec = new Function('__filename', __workerSource);

0 commit comments

Comments
 (0)