Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] main from nodejs:main #17

Merged
merged 3 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions .github/workflows/create-release-proposal.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# This action requires the following secrets to be set on the repository:
# GH_USER_NAME: GitHub user whose Jenkins and GitHub token are defined below
# GH_USER_TOKEN: GitHub user token, to be used by ncu and to push changes

name: Create Release Proposal
Expand Down Expand Up @@ -52,20 +51,18 @@ jobs:
run: |
ncu-config set branch "${RELEASE_BRANCH}"
ncu-config set upstream origin
ncu-config set username "$USERNAME"
ncu-config set username "$GITHUB_ACTOR"
ncu-config set token "$GH_TOKEN"
ncu-config set repo "$(echo "$GITHUB_REPOSITORY" | cut -d/ -f2)"
ncu-config set owner "${GITHUB_REPOSITORY_OWNER}"
env:
USERNAME: ${{ secrets.JENKINS_USER }}
GH_TOKEN: ${{ github.token }}

- name: Set up ghauth config (Ubuntu)
run: |
mkdir -p "${XDG_CONFIG_HOME:-~/.config}/changelog-maker"
echo '{}' | jq '{user: env.USERNAME, token: env.TOKEN}' > "${XDG_CONFIG_HOME:-~/.config}/changelog-maker/config.json"
echo '{}' | jq '{user: env.GITHUB_ACTOR, token: env.TOKEN}' > "${XDG_CONFIG_HOME:-~/.config}/changelog-maker/config.json"
env:
USERNAME: ${{ secrets.JENKINS_USER }}
TOKEN: ${{ github.token }}

- name: Setup git author
Expand Down
144 changes: 125 additions & 19 deletions doc/api/tls.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ The `node:tls` module provides an implementation of the Transport Layer Security
(TLS) and Secure Socket Layer (SSL) protocols that is built on top of OpenSSL.
The module can be accessed using:

```js
```mjs
import tls from 'node:tls';
```

```cjs
const tls = require('node:tls');
```

Expand Down Expand Up @@ -461,17 +465,31 @@ To adjust the security level in your Node.js application, you can include `@SECL
within a cipher string, where `X` is the desired security level. For example,
to set the security level to 0 while using the default OpenSSL cipher list, you could use:

```js
const tls = require('node:tls');
```mjs
import { createServer, connect } from 'node:tls';
const port = 443;

createServer({ ciphers: 'DEFAULT@SECLEVEL=0', minVersion: 'TLSv1' }, function(socket) {
console.log('Client connected with protocol:', socket.getProtocol());
socket.end();
this.close();
})
.listen(port, () => {
connect(port, { ciphers: 'DEFAULT@SECLEVEL=0', maxVersion: 'TLSv1' });
});
```

```cjs
const { createServer, connect } = require('node:tls');
const port = 443;

tls.createServer({ ciphers: 'DEFAULT@SECLEVEL=0', minVersion: 'TLSv1' }, function(socket) {
createServer({ ciphers: 'DEFAULT@SECLEVEL=0', minVersion: 'TLSv1' }, function(socket) {
console.log('Client connected with protocol:', socket.getProtocol());
socket.end();
this.close();
})
.listen(port, () => {
tls.connect(port, { ciphers: 'DEFAULT@SECLEVEL=0', maxVersion: 'TLSv1' });
connect(port, { ciphers: 'DEFAULT@SECLEVEL=0', maxVersion: 'TLSv1' });
});
```

Expand Down Expand Up @@ -1785,24 +1803,57 @@ to `host`.
The following illustrates a client for the echo server example from
[`tls.createServer()`][]:

```js
```mjs
// Assumes an echo server that is listening on port 8000.
const tls = require('node:tls');
const fs = require('node:fs');
import { connect } from 'node:tls';
import { readFileSync } from 'node:fs';
import { stdin } from 'node:process';

const options = {
// Necessary only if the server requires client certificate authentication.
key: readFileSync('client-key.pem'),
cert: readFileSync('client-cert.pem'),

// Necessary only if the server uses a self-signed certificate.
ca: [ readFileSync('server-cert.pem') ],

// Necessary only if the server's cert isn't for "localhost".
checkServerIdentity: () => { return null; },
};

const socket = connect(8000, options, () => {
console.log('client connected',
socket.authorized ? 'authorized' : 'unauthorized');
stdin.pipe(socket);
stdin.resume();
});
socket.setEncoding('utf8');
socket.on('data', (data) => {
console.log(data);
});
socket.on('end', () => {
console.log('server ends connection');
});
```

```cjs
// Assumes an echo server that is listening on port 8000.
const { connect } = require('node:tls');
const { readFileSync } = require('node:fs');

const options = {
// Necessary only if the server requires client certificate authentication.
key: fs.readFileSync('client-key.pem'),
cert: fs.readFileSync('client-cert.pem'),
key: readFileSync('client-key.pem'),
cert: readFileSync('client-cert.pem'),

// Necessary only if the server uses a self-signed certificate.
ca: [ fs.readFileSync('server-cert.pem') ],
ca: [ readFileSync('server-cert.pem') ],

// Necessary only if the server's cert isn't for "localhost".
checkServerIdentity: () => { return null; },
};

const socket = tls.connect(8000, options, () => {
const socket = connect(8000, options, () => {
console.log('client connected',
socket.authorized ? 'authorized' : 'unauthorized');
process.stdin.pipe(socket);
Expand All @@ -1817,6 +1868,20 @@ socket.on('end', () => {
});
```

To generate the certificate and key for this example, run:

```bash
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \
-keyout client-key.pem -out client-cert.pem
```

Then, to generate the `server-cert.pem` certificate for this example, run:

```bash
openssl pkcs12 -certpbe AES-256-CBC -export -out server-cert.pem \
-inkey client-key.pem -in client-cert.pem
```

## `tls.connect(path[, options][, callback])`

<!-- YAML
Expand Down Expand Up @@ -2228,22 +2293,22 @@ workers.

The following illustrates a simple echo server:

```js
const tls = require('node:tls');
const fs = require('node:fs');
```mjs
import { createServer } from 'node:tls';
import { readFileSync } from 'node:fs';

const options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem'),
key: readFileSync('server-key.pem'),
cert: readFileSync('server-cert.pem'),

// This is necessary only if using client certificate authentication.
requestCert: true,

// This is necessary only if the client uses a self-signed certificate.
ca: [ fs.readFileSync('client-cert.pem') ],
ca: [ readFileSync('client-cert.pem') ],
};

const server = tls.createServer(options, (socket) => {
const server = createServer(options, (socket) => {
console.log('server connected',
socket.authorized ? 'authorized' : 'unauthorized');
socket.write('welcome!\n');
Expand All @@ -2255,6 +2320,47 @@ server.listen(8000, () => {
});
```

```cjs
const { createServer } = require('node:tls');
const { readFileSync } = require('node:fs');

const options = {
key: readFileSync('server-key.pem'),
cert: readFileSync('server-cert.pem'),

// This is necessary only if using client certificate authentication.
requestCert: true,

// This is necessary only if the client uses a self-signed certificate.
ca: [ readFileSync('client-cert.pem') ],
};

const server = createServer(options, (socket) => {
console.log('server connected',
socket.authorized ? 'authorized' : 'unauthorized');
socket.write('welcome!\n');
socket.setEncoding('utf8');
socket.pipe(socket);
});
server.listen(8000, () => {
console.log('server bound');
});
```

To generate the certificate and key for this example, run:

```bash
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \
-keyout server-key.pem -out server-cert.pem
```

Then, to generate the `client-cert.pem` certificate for this example, run:

```bash
openssl pkcs12 -certpbe AES-256-CBC -export -out client-cert.pem \
-inkey server-key.pem -in server-cert.pem
```

The server can be tested by connecting to it using the example client from
[`tls.connect()`][].

Expand Down
4 changes: 3 additions & 1 deletion src/js_native_api_v8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2769,10 +2769,12 @@ napi_status NAPI_CDECL napi_create_reference(napi_env env,

// Deletes a reference. The referenced value is released, and may be GC'd unless
// there are other references to it.
// For a napi_reference returned from `napi_wrap`, this must be called in the
// finalizer.
napi_status NAPI_CDECL napi_delete_reference(napi_env env, napi_ref ref) {
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
// JS exceptions.
CHECK_ENV_NOT_IN_GC(env);
CHECK_ENV(env);
CHECK_ARG(env, ref);

delete reinterpret_cast<v8impl::Reference*>(ref);
Expand Down
37 changes: 34 additions & 3 deletions test/js-native-api/6_object_wrap/6_object_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,25 @@
#include "assert.h"
#include "myobject.h"

typedef int32_t FinalizerData;

napi_ref MyObject::constructor;

MyObject::MyObject(double value)
: value_(value), env_(nullptr), wrapper_(nullptr) {}

MyObject::~MyObject() { napi_delete_reference(env_, wrapper_); }

void MyObject::Destructor(
napi_env env, void* nativeObject, void* /*finalize_hint*/) {
void MyObject::Destructor(node_api_basic_env env,
void* nativeObject,
void* /*finalize_hint*/) {
MyObject* obj = static_cast<MyObject*>(nativeObject);
delete obj;

FinalizerData* data;
NODE_API_BASIC_CALL_RETURN_VOID(
env, napi_get_instance_data(env, reinterpret_cast<void**>(&data)));
*data += 1;
}

void MyObject::Init(napi_env env, napi_value exports) {
Expand Down Expand Up @@ -154,7 +162,7 @@ napi_value MyObject::Multiply(napi_env env, napi_callback_info info) {
}

// This finalizer should never be invoked.
void ObjectWrapDanglingReferenceFinalizer(napi_env env,
void ObjectWrapDanglingReferenceFinalizer(node_api_basic_env env,
void* finalize_data,
void* finalize_hint) {
assert(0 && "unreachable");
Expand Down Expand Up @@ -198,15 +206,38 @@ napi_value ObjectWrapDanglingReferenceTest(napi_env env,
return ret;
}

static napi_value GetFinalizerCallCount(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value argv[1];
FinalizerData* data;
napi_value result;

NODE_API_CALL(env,
napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
NODE_API_CALL(env,
napi_get_instance_data(env, reinterpret_cast<void**>(&data)));
NODE_API_CALL(env, napi_create_int32(env, *data, &result));
return result;
}

static void finalizeData(napi_env env, void* data, void* hint) {
delete reinterpret_cast<FinalizerData*>(data);
}

EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
FinalizerData* data = new FinalizerData;
*data = 0;
NODE_API_CALL(env, napi_set_instance_data(env, data, finalizeData, nullptr));

MyObject::Init(env, exports);

napi_property_descriptor descriptors[] = {
DECLARE_NODE_API_PROPERTY("objectWrapDanglingReference",
ObjectWrapDanglingReference),
DECLARE_NODE_API_PROPERTY("objectWrapDanglingReferenceTest",
ObjectWrapDanglingReferenceTest),
DECLARE_NODE_API_PROPERTY("getFinalizerCallCount", GetFinalizerCallCount),
};

NODE_API_CALL(
Expand Down
7 changes: 7 additions & 0 deletions test/js-native-api/6_object_wrap/binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
"sources": [
"6_object_wrap.cc"
]
},
{
"target_name": "6_object_wrap_basic_finalizer",
"defines": [ "NAPI_EXPERIMENTAL" ],
"sources": [
"6_object_wrap.cc"
]
}
]
}
4 changes: 3 additions & 1 deletion test/js-native-api/6_object_wrap/myobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
class MyObject {
public:
static void Init(napi_env env, napi_value exports);
static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);
static void Destructor(node_api_basic_env env,
void* nativeObject,
void* finalize_hint);

private:
explicit MyObject(double value_ = 0);
Expand Down
24 changes: 24 additions & 0 deletions test/js-native-api/6_object_wrap/test-basic-finalizer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Flags: --expose-gc

'use strict';
const common = require('../../common');
const assert = require('assert');
const addon = require(`./build/${common.buildType}/6_object_wrap_basic_finalizer`);

// This test verifies that ObjectWrap can be correctly finalized with a node_api_basic_finalizer
// in the current JS loop tick
(() => {
let obj = new addon.MyObject(9);
obj = null;
// Silent eslint about unused variables.
assert.strictEqual(obj, null);
})();

for (let i = 0; i < 10; ++i) {
global.gc();
if (addon.getFinalizerCallCount() === 1) {
break;
}
}

assert.strictEqual(addon.getFinalizerCallCount(), 1);
Loading