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‎

Lines changed: 12 additions & 1 deletion
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‎

Lines changed: 3 additions & 0 deletions
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‎

Lines changed: 6 additions & 1 deletion
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
constname='[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+
}elseif(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‎

Lines changed: 1 addition & 1 deletion
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+
returnhandleMainPromise(loadESM(async(loader)=>{
4949
const{ result }=awaitloader.eval(source);
5050
if(print){
5151
log(result);

‎lib/internal/worker.js‎

Lines changed: 9 additions & 2 deletions
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-
leturl;
104+
leturl,doEval;
104105
if(options.eval){
105106
if(typeoffilename!=='string'){
106107
thrownewERR_INVALID_ARG_VALUE(
@@ -110,7 +111,13 @@ class Worker extends EventEmitter{
110111
);
111112
}
112113
url=null;
114+
doEval='classic';
115+
}elseif(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,
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use strict';
2+
3+
constcommon=require('../common');
4+
const{ Worker }=require('worker_threads');
5+
constassert=require('assert');
6+
7+
newWorker(newURL('data:text/javascript,'))
8+
.on('error',common.mustNotCall(()=>{}));
9+
newWorker(newURL('data:text/javascript,export{}'))
10+
.on('error',common.mustNotCall(()=>{}));
11+
12+
newWorker(newURL('data:text/plain,'))
13+
.on('error',common.mustCall(()=>{}));
14+
newWorker(newURL('data:text/javascript,module.exports={}'))
15+
.on('error',common.mustCall(()=>{}));
16+
17+
newWorker(newURL('data:text/javascript,await Promise.resolve()'))
18+
.on('error',common.mustNotCall(()=>{}));
19+
newWorker(newURL('data:text/javascript,await Promise.reject()'))
20+
.on('error',common.mustCall(()=>{}));
21+
newWorker(newURL('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‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ const{Worker } = require('worker_threads');
3333
()=>{newWorker('file:///file_url');},
3434
/Wrapfile:\/\/URLswith`newURL`/
3535
);
36+
assert.throws(
37+
()=>{newWorker('data:text/javascript,');},
38+
/Wrapdata:URLswith`newURL`/
39+
);
3640
assert.throws(
3741
()=>{newWorker('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(()=>{newWorker(newURL('https://www.url.com'));},
4953
expectedErr);
50-
assert.throws(()=>{newWorker(newURL('data:application/javascript,'));},
51-
expectedErr);
5254
}

0 commit comments

Comments
(0)