Notable changes: build: * (SEMVER-MAJOR) remove the bundled FIPS provider build (Filip Skokan) https://github.com/nodejs/node/pull/64777 * (SEMVER-MAJOR) require OpenSSL 3 or later (Filip Skokan) https://github.com/nodejs/node/pull/64777 * (SEMVER-MAJOR) downgrade Intel macOS support to experimental (Antoine du Hamel) https://github.com/nodejs/node/pull/65427 crypto: * (SEMVER-MAJOR) move DEP0093 to End-of-Life (Filip Skokan) https://github.com/nodejs/node/pull/64777 * (SEMVER-MAJOR) remove legacy OpenSSL code paths (Filip Skokan) https://github.com/nodejs/node/pull/64777 * (SEMVER-MAJOR) validate digest output encodings (Filip Skokan) https://github.com/nodejs/node/pull/66247 * (SEMVER-MAJOR) validate update input encodings (Filip Skokan) https://github.com/nodejs/node/pull/66247 * (SEMVER-MAJOR) validate inputEncoding in Cipher/Decipher update (Maruthan G) https://github.com/nodejs/node/pull/66247 * (SEMVER-MAJOR) move DEP0198 to End-of-Life (Filip Skokan) https://github.com/nodejs/node/pull/64000 * (SEMVER-MAJOR) move DEP0203 and DEP0204 to End-of-Life (Filip Skokan) https://github.com/nodejs/node/pull/63188 * (SEMVER-MAJOR) reject resizable backing stores in Web Crypto buffer parameters (Filip Skokan) https://github.com/nodejs/node/pull/63925 * (SEMVER-MAJOR) throw on uninitialized DH computeSecret (Filip Skokan) https://github.com/nodejs/node/pull/63922 * (SEMVER-MAJOR) runtime-deprecate calling Hmac.digest() more than once (Madelyn) https://github.com/nodejs/node/pull/63624 crypto,https,tls: * (SEMVER-MAJOR) end OpenSSL engine support (Filip Skokan) https://github.com/nodejs/node/pull/64777 deps: * (SEMVER-MAJOR) update archs files for openssl-4.1.0 (Node.js GitHub Bot) https://github.com/nodejs/node/pull/66971 * (SEMVER-MAJOR) upgrade openssl sources to openssl-4.1.0 (Node.js GitHub Bot) https://github.com/nodejs/node/pull/66971 * (SEMVER-MAJOR) remove ncrypto legacy OpenSSL backend (Filip Skokan) https://github.com/nodejs/node/pull/64777 * (SEMVER-MAJOR) update V8 to 15.6.75.13 (Joyee Cheung) https://github.com/nodejs/node/pull/66601 * (SEMVER-MAJOR) upgrade npm to 12.1.0 (npm team) https://github.com/nodejs/node/pull/66212 dgram: * (SEMVER-MAJOR) skip the custom lookup for literal IP addresses (Ruben Bridgewater) https://github.com/nodejs/node/pull/64131 dns: * (SEMVER-MAJOR) do not consult hosts file in reverse lookups (Archkon) https://github.com/nodejs/node/pull/64268 * (SEMVER-MAJOR) stop using deprecated functions in `SetServers` (Antoine du Hamel) https://github.com/nodejs/node/pull/64356 doc: * (SEMVER-MAJOR) remove OpenSSL 1.x references (Filip Skokan) https://github.com/nodejs/node/pull/64777 * (SEMVER-MAJOR) update Node.js 27 official binary toolchains (Richard Lau) https://github.com/nodejs/node/pull/66377 domain: * (SEMVER-MAJOR) runtime-deprecate the module (Matteo Collina) https://github.com/nodejs/node/pull/65074 * (SEMVER-MAJOR) port to AsyncLocalStorage (Matteo Collina) https://github.com/nodejs/node/pull/61095 fs: * (SEMVER-MAJOR) implement glob natively (Aviv Keller) https://github.com/nodejs/node/pull/65392 * (SEMVER-MAJOR) return Buffer from mkdtemp when prefix is a Buffer (Hamid Reza Ghavami) https://github.com/nodejs/node/pull/64397 http: * (SEMVER-MAJOR) reject responses exceeding header limit (Matteo Collina) https://github.com/nodejs/node/pull/65010 * (SEMVER-MAJOR) runtime deprecate instantiating without new (Yagiz Nizipli) https://github.com/nodejs/node/pull/64853 * (SEMVER-MAJOR) increase keepAliveTimeout default to 65 seconds (Tim Perry) https://github.com/nodejs/node/pull/62782 * (SEMVER-MAJOR) make req.headers have a null prototype (Matteo Collina) https://github.com/nodejs/node/pull/62900 * (SEMVER-MAJOR) reject addTrailers after finish (Ryuhei Shima) https://github.com/nodejs/node/pull/62832 http2: * (SEMVER-MAJOR) increase default window sizes (Matteo Collina) https://github.com/nodejs/node/pull/64623 lib: * (SEMVER-MAJOR) deprecate _http_* (Sebastian Beltran) https://github.com/nodejs/node/pull/58535 * (SEMVER-MAJOR) insert argv[0] at argv[1] in setDeserializeMainFunction (Daijiro Wachi) https://github.com/nodejs/node/pull/62928 * (SEMVER-MAJOR) use `Object.freeze` to avoid defensive cloning in `SourceMap` (Daijiro Wachi) https://github.com/nodejs/node/pull/62830 * (SEMVER-MAJOR) reject SharedArrayBuffer in Blob constructor per spec (Ali Hassan) https://github.com/nodejs/node/pull/63046 lib,src: * (SEMVER-MAJOR) remove `--no-experimental-websocket` CLI flag (Antoine du Hamel) https://github.com/nodejs/node/pull/65284 net: * (SEMVER-MAJOR) race family autoselection attempts (Matteo Collina) https://github.com/nodejs/node/pull/66229 * (SEMVER-MAJOR) throw on multiple listen calls (Matteo Collina) https://github.com/nodejs/node/pull/64871 * (SEMVER-MAJOR) runtime-deprecate Server.prototype._listen2 (Guilherme Araújo) https://github.com/nodejs/node/pull/64794 * (SEMVER-MAJOR) add experimental net/promises API (Ethan Arrowood) https://github.com/nodejs/node/pull/63965 repl: * (SEMVER-MAJOR) use inspector over vm (Aviv Keller) https://github.com/nodejs/node/pull/64034 src: * (SEMVER-MAJOR) officially deprecate `node::ObjectWrap` (Anna Henningsen) https://github.com/nodejs/node/pull/63756 * (SEMVER-MAJOR) merge `CreateEnvironment()` overloads (Anna Henningsen) https://github.com/nodejs/node/pull/63596 src,lib: * (SEMVER-MAJOR) add --allow-env permission (James M Snell) https://github.com/nodejs/node/pull/66132 stream: * (SEMVER-MAJOR) destroy source when map iterator closes early (Y1D7NG) https://github.com/nodejs/node/pull/64264 test_runner: * use AsyncLocalStorage instead of async_hooks (Matteo Collina) https://github.com/nodejs/node/pull/66795 tls: * (SEMVER-MAJOR) move _tls_* to end-of-life (Sebastian Beltran) https://github.com/nodejs/node/pull/60658 zlib: * (SEMVER-MAJOR) convert classes to modern syntax and eol DEP0184 (Yagiz Nizipli) https://github.com/nodejs/node/pull/64849 PR-URL: https://github.com/nodejs/node/pull/67062 Signed-off-by: RafaelGSS <rafael.nunu@hotmail.com>
9.0 KiB
C++ embedder API
Node.js provides a number of C++ APIs that can be used to execute JavaScript in a Node.js environment from other C++ software.
The documentation for these APIs can be found in src/node.h in the Node.js source tree. In addition to the APIs exposed by Node.js, some required concepts are provided by the V8 embedder API.
Because using Node.js as an embedded library is different from writing code that is executed by Node.js, breaking changes do not follow typical Node.js deprecation policy and may occur on each semver-major release without prior warning.
Example embedding application
The following sections will provide an overview over how to use these APIs
to create an application from scratch that will perform the equivalent of
node -e <code>, i.e. that will take a piece of JavaScript and run it in
a Node.js-specific environment.
The full code can be found in the Node.js source tree.
Setting up a per-process state
Node.js requires some per-process state management in order to run:
- Arguments parsing for Node.js CLI options,
- V8 per-process requirements, such as a
v8::Platforminstance.
The following example shows how these can be set up. Some class names are from
the node and v8 C++ namespaces, respectively.
int main(int argc, char** argv) {
argv = uv_setup_args(argc, argv);
std::vector<std::string> args(argv, argv + argc);
// Parse Node.js CLI options, and print any errors that have occurred while
// trying to parse them.
std::unique_ptr<node::InitializationResult> result =
node::InitializeOncePerProcess(args, {
node::ProcessInitializationFlags::kNoInitializeV8,
node::ProcessInitializationFlags::kNoInitializeNodeV8Platform
});
for (const std::string& error : result->errors())
fprintf(stderr, "%s: %s\n", args[0].c_str(), error.c_str());
if (result->early_return() != 0) {
return result->exit_code();
}
// Create a v8::Platform instance. `MultiIsolatePlatform::Create()` is a way
// to create a v8::Platform instance that Node.js can use when creating
// Worker threads. When no `MultiIsolatePlatform` instance is present,
// Worker threads are disabled.
std::unique_ptr<MultiIsolatePlatform> platform =
MultiIsolatePlatform::Create(4);
V8::InitializePlatform(platform.get());
V8::Initialize();
// See below for the contents of this function.
int ret = RunNodeInstance(
platform.get(), result->args(), result->exec_args());
V8::Dispose();
V8::DisposePlatform();
node::TearDownOncePerProcess();
return ret;
}
Restricting access to environment variables
When the arguments passed to node::InitializeOncePerProcess() enable the
Permission Model without --allow-env=*, the process environment must not
contain any variable that --allow-env does not grant access to.
node::InitializeOncePerProcess() fails otherwise. Unlike the node
executable, embedders own the process environment, so Node.js does not remove
these variables itself.
node::ScrubProcessEnvironment() removes them. Because it modifies the process
environment without any locking that native code calling getenv()
participates in, it must be called before starting any thread that may read the
environment, and before node::InitializeOncePerProcess():
int main(int argc, char** argv) {
argv = uv_setup_args(argc, argv);
std::vector<std::string> args(argv, argv + argc);
// Keep the variables the embedder itself reads, in addition to the ones
// Node.js reads (see node::GetRuntimeEnvironmentDefaults()).
node::ProcessEnvironmentScrubOptions scrub_options;
scrub_options.allow = {"PORT", "APP_*"};
if (node::ScrubProcessEnvironment(scrub_options).IsNothing()) {
return 1;
}
// args contains, for example, --permission --allow-env=PORT
std::unique_ptr<node::InitializationResult> result =
node::InitializeOncePerProcess(args, {
node::ProcessInitializationFlags::kNoInitializeV8,
node::ProcessInitializationFlags::kNoInitializeNodeV8Platform
});
// ...
}
process.permission.drop('env', name) removes a variable from the process
environment, so it throws when called from a node::Environment created
without node::EnvironmentFlags::kOwnsProcessState.
Setting up a per-instance state
Node.js has a concept of a “Node.js instance”, that is commonly being referred
to as node::Environment. Each node::Environment is associated with:
- Exactly one
v8::Isolate, i.e. one JS Engine instance, - Exactly one
uv_loop_t, i.e. one event loop, - A number of
v8::Contexts, but exactly one mainv8::Context, and - One
node::IsolateDatainstance that contains information that could be shared by multiplenode::Environments. The embedder should make sure thatnode::IsolateDatais shared only amongnode::Environments that use the samev8::Isolate, Node.js does not perform this check.
node::Environments that share a node::IsolateData also share its
uv_loop_t. node::FreeEnvironment() runs that loop until the handles of the
node::Environment being freed have closed. Timers, I/O callbacks and thread
pool completions of the other node::Environments that become due in those
loop iterations run normally, including their JavaScript; only the
node::Environment being freed can no longer call into JavaScript.
In order to set up a v8::Isolate, an v8::ArrayBuffer::Allocator needs
to be provided. One possible choice is the default Node.js allocator, which
can be created through node::ArrayBufferAllocator::Create(). Using the Node.js
allocator allows minor performance optimizations when addons use the Node.js
C++ Buffer API, and is required in order to track ArrayBuffer memory in
process.memoryUsage().
Additionally, each v8::Isolate that is used for a Node.js instance needs to
be registered and unregistered with the MultiIsolatePlatform instance, if one
is being used, in order for the platform to know which event loop to use
for tasks scheduled by the v8::Isolate.
The node::NewIsolate() helper function creates a v8::Isolate,
sets it up with some Node.js-specific hooks (e.g. the Node.js error handler),
and registers it with the platform automatically.
int RunNodeInstance(MultiIsolatePlatform* platform,
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args) {
int exit_code = 0;
// Set up a libuv event loop, v8::Isolate, and Node.js Environment.
std::vector<std::string> errors;
std::unique_ptr<CommonEnvironmentSetup> setup =
CommonEnvironmentSetup::Create(platform, &errors, args, exec_args);
if (!setup) {
for (const std::string& err : errors)
fprintf(stderr, "%s: %s\n", args[0].c_str(), err.c_str());
return 1;
}
Isolate* isolate = setup->isolate();
Environment* env = setup->env();
{
Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
// The v8::Context needs to be entered when node::CreateEnvironment() and
// node::LoadEnvironment() are being called.
Context::Scope context_scope(setup->context());
// Set up the Node.js instance for execution, and run code inside of it.
// There is also a variant that takes a callback and provides it with
// the `require` and `process` objects, so that it can manually compile
// and run scripts as needed.
// The `require` function inside this script does *not* access the file
// system, and can only load built-in Node.js modules.
// `module.createRequire()` is being used to create one that is able to
// load files from the disk, and uses the standard CommonJS file loader
// instead of the internal-only `require` function.
MaybeLocal<Value> loadenv_ret = node::LoadEnvironment(
env,
"const publicRequire ="
" require('node:module').createRequire(process.cwd() + '/');"
"globalThis.require = publicRequire;"
"require('node:vm').runInThisContext(process.argv[1]);");
if (loadenv_ret.IsEmpty()) // There has been a JS exception.
return 1;
exit_code = node::SpinEventLoop(env).FromMaybe(1);
// node::Stop() can be used to explicitly stop the event loop and keep
// further JavaScript from running. It can be called from any thread,
// and will act like worker.terminate() if called from another thread.
node::Stop(env);
}
return exit_code;
}