This is an automated email from the ASF dual-hosted git repository.

baodi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-node.git


The following commit(s) were added to refs/heads/master by this push:
     new 772ab49  Support separate TLS handshake certificate/private key in 
Client (#440)
772ab49 is described below

commit 772ab491e97e34919519b829890dab852ed3c34f
Author: Baodi Shi <[email protected]>
AuthorDate: Thu Nov 27 21:32:35 2025 +0800

    Support separate TLS handshake certificate/private key in Client (#440)
    
    ### Motivation
    Support separate TLS handshake certificate/private key parameters 
independent from auth, enabling proper mTLS handshake configuration.
    
    ### Modifications
    - Add  and  to .
    - Wire to C client via  and .
    - Update end-to-end test to provide client cert/key for TLS endpoints.
    
    ### Verifying this change
    - Ran  only for  cases; TLS endpoints pass with client cert/key.
    
    ### Documentation
    - [x] doc-not-needed (Type hints updated in ).
---
 index.d.ts                 |  2 ++
 src/Client.cc              | 14 ++++++++++++++
 tests/conf/standalone.conf |  4 +++-
 tests/end_to_end.test.js   |  4 ++++
 4 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/index.d.ts b/index.d.ts
index e9bf8e8..9dc3e8f 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -27,6 +27,8 @@ export interface ClientConfig {
   concurrentLookupRequest?: number;
   useTls?: boolean;
   tlsTrustCertsFilePath?: string;
+  tlsCertificateFilePath?: string;
+  tlsPrivateKeyFilePath?: string;
   tlsValidateHostname?: boolean;
   tlsAllowInsecureConnection?: boolean;
   statsIntervalInSeconds?: number;
diff --git a/src/Client.cc b/src/Client.cc
index 80052f0..29d542c 100644
--- a/src/Client.cc
+++ b/src/Client.cc
@@ -39,6 +39,8 @@ static const std::string CFG_USE_TLS = "useTls";
 static const std::string CFG_TLS_TRUST_CERT = "tlsTrustCertsFilePath";
 static const std::string CFG_TLS_VALIDATE_HOSTNAME = "tlsValidateHostname";
 static const std::string CFG_TLS_ALLOW_INSECURE = "tlsAllowInsecureConnection";
+static const std::string CFG_TLS_CERT_FILE = "tlsCertificateFilePath";
+static const std::string CFG_TLS_PRIVATE_KEY_FILE = "tlsPrivateKeyFilePath";
 static const std::string CFG_STATS_INTERVAL = "statsIntervalInSeconds";
 static const std::string CFG_LOG = "log";
 static const std::string CFG_LOG_LEVEL = "logLevel";
@@ -196,6 +198,18 @@ Client::Client(const Napi::CallbackInfo &info) : 
Napi::ObjectWrap<Client>(info)
                                                               
tlsTrustCertsFilePath.Utf8Value().c_str());
   }
 
+  if (clientConfig.Has(CFG_TLS_CERT_FILE) && 
clientConfig.Get(CFG_TLS_CERT_FILE).IsString()) {
+    Napi::String tlsCertFilePath = 
clientConfig.Get(CFG_TLS_CERT_FILE).ToString();
+    
pulsar_client_configuration_set_tls_certificate_file_path(cClientConfig.get(),
+                                                              
tlsCertFilePath.Utf8Value().c_str());
+  }
+
+  if (clientConfig.Has(CFG_TLS_PRIVATE_KEY_FILE) && 
clientConfig.Get(CFG_TLS_PRIVATE_KEY_FILE).IsString()) {
+    Napi::String tlsPrivateKeyFilePath = 
clientConfig.Get(CFG_TLS_PRIVATE_KEY_FILE).ToString();
+    
pulsar_client_configuration_set_tls_private_key_file_path(cClientConfig.get(),
+                                                              
tlsPrivateKeyFilePath.Utf8Value().c_str());
+  }
+
   if (clientConfig.Has(CFG_TLS_VALIDATE_HOSTNAME) &&
       clientConfig.Get(CFG_TLS_VALIDATE_HOSTNAME).IsBoolean()) {
     Napi::Boolean tlsValidateHostname = 
clientConfig.Get(CFG_TLS_VALIDATE_HOSTNAME).ToBoolean();
diff --git a/tests/conf/standalone.conf b/tests/conf/standalone.conf
index b3618fa..e0789a3 100755
--- a/tests/conf/standalone.conf
+++ b/tests/conf/standalone.conf
@@ -115,6 +115,9 @@ tlsKeyFilePath=/pulsar/test-conf/server.key
 # fails, then the certs are untrusted and the connections are dropped.
 tlsTrustCertsFilePath=/pulsar/test-conf/server.crt
 
+# Require clients to present a trusted certificate (enable mTLS)
+tlsRequireTrustedClientCertOnConnect=true
+
 ### --- Authentication --- ###
 
 # Enable authentication
@@ -305,4 +308,3 @@ brokerServicePurgeInactiveFrequencyInSeconds=60
 
 # The maximum netty frame size in bytes. Any message received larger than this 
will be rejected. The default value is 5MB.
 nettyMaxFrameSizeBytes=5253120
-
diff --git a/tests/end_to_end.test.js b/tests/end_to_end.test.js
index ce3693f..2c08418 100644
--- a/tests/end_to_end.test.js
+++ b/tests/end_to_end.test.js
@@ -34,6 +34,10 @@ const Pulsar = require('../index');
         operationTimeoutSeconds: 30,
         connectionTimeoutMs: 20000,
         listenerName,
+        ...(serviceUrl.startsWith('pulsar+ssl://') || 
serviceUrl.startsWith('https://') ? {
+          tlsCertificateFilePath: `${__dirname}/certificate/server.crt`,
+          tlsPrivateKeyFilePath: `${__dirname}/certificate/server.key`,
+        } : {}),
       });
 
       const topic = 'persistent://public/default/produce-consume';

Reply via email to