Skip to content

Commit b73943e

Browse files
aduh95BethGriggs
authored andcommitted
workers: add support for data: URLs
PR-URL: #34584 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Bradley Farias <[email protected]> Reviewed-By: Jan Krems <[email protected]>
1 parent 90abdd3 commit b73943e

File tree

7 files changed

+60
-7
lines changed

7 files changed

+60
-7
lines changed

doc/api/worker_threads.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,13 @@ if (isMainThread) {
621621
<!-- YAML
622622
added: v10.5.0
623623
changes:
624+
- version: REPLACEME
625+
pr-url: https://github.com/nodejs/node/pull/34584
626+
description: The `filename` parameter can be a WHATWG `URL` object using
627+
`data:` protocol.
628+
- version: REPLACEME
629+
pr-url: https://github.com/nodejs/node/pull/34394
630+
description: The `trackUnmanagedFds` option was set to `true` by default.
624631
- version:
625632
- v14.6.0
626633
pr-url: https://github.com/nodejs/node/pull/34303
@@ -647,7 +654,9 @@ changes:
647654
* `filename` {string|URL} The path to the Worker’s main script or module. Must
648655
be either an absolute path or a relative path (i.e. relative to the
649656
current working directory) starting with `./` or `../`, or a WHATWG `URL`
650-
object using `file:` protocol.
657+
object using `file:` or `data:` protocol.
658+
When using a [`data:` URL][], the data is interpreted based on MIME type using
659+
the [ECMAScript module loader][].
651660
If `options.eval` is `true`, this is a string containing JavaScript code
652661
rather than a path.
653662
* `options` {Object}
@@ -893,6 +902,7 @@ active handle in the event system. If the worker is already `unref()`ed calling
893902
[`AsyncResource`]: async_hooks.html#async_hooks_class_asyncresource
894903
[`Buffer`]: buffer.html
895904
[`Buffer.allocUnsafe()`]: buffer.html#buffer_static_method_buffer_allocunsafe_size
905+
[ECMAScript module loader]: esm.html#esm_data_imports
896906
[`ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST`]: errors.html#errors_err_missing_message_port_in_transfer_list
897907
[`ERR_WORKER_NOT_RUNNING`]: errors.html#ERR_WORKER_NOT_RUNNING
898908
[`EventEmitter`]: events.html
@@ -944,3 +954,4 @@ active handle in the event system. If the worker is already `unref()`ed calling
944954
[child processes]: child_process.html
945955
[contextified]: vm.html#vm_what_does_it_mean_to_contextify_an_object
946956
[v8.serdes]: v8.html#v8_serialization_api
957+
[`data:` URL]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs

lib/internal/errors.js

+3
Original file line numberDiff line numberDiff line change
@@ -1451,6 +1451,9 @@ E('ERR_WORKER_PATH', (filename) =>
14511451
(filename.startsWith('file://') ?
14521452
' Wrap file:// URLs with `new URL`.' : ''
14531453
) +
1454+
(filename.startsWith('data:text/javascript') ?
1455+
' Wrap data: URLs with `new URL`.' : ''
1456+
) +
14541457
` Received "${filename}"`,
14551458
TypeError);
14561459
E('ERR_WORKER_UNSERIALIZABLE_ERROR',

lib/internal/main/worker_thread.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ port.on('message', (message) => {
149149
debug(`[${threadId}] starts worker script ${filename} ` +
150150
`(eval = ${eval}) at cwd = ${process.cwd()}`);
151151
port.postMessage({ type: UP_AND_RUNNING });
152-
if (doEval) {
152+
if (doEval === 'classic') {
153153
const { evalScript } = require('internal/process/execution');
154154
const name = '[worker eval]';
155155
// This is necessary for CJS module compilation.
@@ -161,6 +161,11 @@ port.on('message', (message) => {
161161
});
162162
process.argv.splice(1, 0, name);
163163
evalScript(name, filename);
164+
} else if (doEval === 'module') {
165+
const { evalModule } = require('internal/process/execution');
166+
evalModule(filename).catch((e) => {
167+
workerOnGlobalUncaughtException(e, true);
168+
});
164169
} else {
165170
// script filename
166171
// runMain here might be monkey-patched by users in --require.

lib/internal/process/execution.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ function evalModule(source, print) {
4545
const { log } = require('internal/console/global');
4646
const { loadESM } = require('internal/process/esm_loader');
4747
const { handleMainPromise } = require('internal/modules/run_main');
48-
handleMainPromise(loadESM(async (loader) => {
48+
return handleMainPromise(loadESM(async (loader) => {
4949
const { result } = await loader.eval(source);
5050
if (print) {
5151
log(result);

lib/internal/worker.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
const {
66
ArrayIsArray,
7+
JSONStringify,
78
MathMax,
89
ObjectCreate,
910
ObjectEntries,
@@ -100,7 +101,7 @@ class Worker extends EventEmitter {
100101
argv = options.argv.map(String);
101102
}
102103

103-
let url;
104+
let url, doEval;
104105
if (options.eval) {
105106
if (typeof filename !== 'string') {
106107
throw new ERR_INVALID_ARG_VALUE(
@@ -110,7 +111,13 @@ class Worker extends EventEmitter {
110111
);
111112
}
112113
url = null;
114+
doEval = 'classic';
115+
} else if (isURLInstance(filename) && filename.protocol === 'data:') {
116+
url = null;
117+
doEval = 'module';
118+
filename = `import ${JSONStringify(`${filename}`)}`;
113119
} else {
120+
doEval = false;
114121
if (isURLInstance(filename)) {
115122
url = filename;
116123
filename = fileURLToPath(filename);
@@ -201,7 +208,7 @@ class Worker extends EventEmitter {
201208
argv,
202209
type: messageTypes.LOAD_SCRIPT,
203210
filename,
204-
doEval: !!options.eval,
211+
doEval,
205212
cwdCounter: cwdCounter || workerIo.sharedCwdCounter,
206213
workerData: options.workerData,
207214
publicPort: port2,

test/parallel/test-worker-data-url.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const { Worker } = require('worker_threads');
5+
const assert = require('assert');
6+
7+
new Worker(new URL('data:text/javascript,'))
8+
.on('error', common.mustNotCall(() => {}));
9+
new Worker(new URL('data:text/javascript,export{}'))
10+
.on('error', common.mustNotCall(() => {}));
11+
12+
new Worker(new URL('data:text/plain,'))
13+
.on('error', common.mustCall(() => {}));
14+
new Worker(new URL('data:text/javascript,module.exports={}'))
15+
.on('error', common.mustCall(() => {}));
16+
17+
new Worker(new URL('data:text/javascript,await Promise.resolve()'))
18+
.on('error', common.mustNotCall(() => {}));
19+
new Worker(new URL('data:text/javascript,await Promise.reject()'))
20+
.on('error', common.mustCall(() => {}));
21+
new Worker(new URL('data:text/javascript,await new Promise(()=>{})'))
22+
.on(
23+
'exit',
24+
common.mustCall((exitCode) => { assert.strictEqual(exitCode, 13); })
25+
);

test/parallel/test-worker-unsupported-path.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ const { Worker } = require('worker_threads');
3333
() => { new Worker('file:///file_url'); },
3434
/Wrap file:\/\/ URLs with `new URL`/
3535
);
36+
assert.throws(
37+
() => { new Worker('data:text/javascript,'); },
38+
/Wrap data: URLs with `new URL`/
39+
);
3640
assert.throws(
3741
() => { new Worker('relative_no_dot'); },
3842
// eslint-disable-next-line node-core/no-unescaped-regexp-dot
@@ -47,6 +51,4 @@ const { Worker } = require('worker_threads');
4751
};
4852
assert.throws(() => { new Worker(new URL('https://www.url.com')); },
4953
expectedErr);
50-
assert.throws(() => { new Worker(new URL('data:application/javascript,')); },
51-
expectedErr);
5254
}

0 commit comments

Comments
 (0)