pulsar-client 1.15.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ {
2
+ "recommendations": [
3
+ "Orta.vscode-jest",
4
+ "firsttris.vscode-jest-runner"
5
+ ]
6
+ }
7
+
@@ -0,0 +1,91 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+
5
+ {
6
+ "type": "node",
7
+ "request": "launch",
8
+ "name": "Debug examples/consumer.js",
9
+ "program": "${workspaceFolder}/examples/consumer.js",
10
+ "cwd": "${workspaceFolder}",
11
+ "console": "integratedTerminal",
12
+ "skipFiles": [
13
+ "<node_internals>/**"
14
+ ]
15
+ },
16
+ {
17
+ "type": "node",
18
+ "request": "launch",
19
+ "name": "Debug consumer.js (pause on start)",
20
+ "program": "${workspaceFolder}/examples/consumer.js",
21
+ "cwd": "${workspaceFolder}",
22
+ "runtimeArgs": ["--inspect-brk"],
23
+ "console": "integratedTerminal",
24
+ "skipFiles": [
25
+ "<node_internals>/**"
26
+ ]
27
+ },
28
+ {
29
+ "type": "node",
30
+ "request": "attach",
31
+ "name": "Attach to Node (9229)",
32
+ "port": 9229,
33
+ "restart": false
34
+ },
35
+ {
36
+ "type": "node",
37
+ "request": "launch",
38
+ "name": "Debug examples/producer.js",
39
+ "program": "${workspaceFolder}/examples/producer.js",
40
+ "cwd": "${workspaceFolder}",
41
+ "console": "integratedTerminal",
42
+ "skipFiles": [
43
+ "<node_internals>/**"
44
+ ]
45
+ },
46
+ {
47
+ "type": "node",
48
+ "request": "launch",
49
+ "name": "Debug producer.js (pause on start, port 9230)",
50
+ "program": "${workspaceFolder}/examples/producer.js",
51
+ "cwd": "${workspaceFolder}",
52
+ "runtimeArgs": ["--inspect-brk=9230"],
53
+ "console": "integratedTerminal",
54
+ "skipFiles": [
55
+ "<node_internals>/**"
56
+ ]
57
+ },
58
+ {
59
+ "type": "node",
60
+ "request": "launch",
61
+ "name": "Debug Active File",
62
+ "program": "${file}",
63
+ "cwd": "${workspaceFolder}",
64
+ "console": "integratedTerminal",
65
+ "skipFiles": [
66
+ "<node_internals>/**"
67
+ ]
68
+ },
69
+ {
70
+ "type": "node",
71
+ "request": "launch",
72
+ "name": "Debug Active File (pause on start)",
73
+ "program": "${file}",
74
+ "cwd": "${workspaceFolder}",
75
+ "runtimeArgs": ["--inspect-brk"],
76
+ "console": "integratedTerminal",
77
+ "skipFiles": [
78
+ "<node_internals>/**"
79
+ ]
80
+ }
81
+ ],
82
+ "compounds": [
83
+ {
84
+ "name": "Debug Consumer + Producer",
85
+ "configurations": [
86
+ "Debug examples/consumer.js",
87
+ "Debug examples/producer.js"
88
+ ]
89
+ }
90
+ ]
91
+ }
package/binding.gyp CHANGED
@@ -21,9 +21,9 @@
21
21
  "targets": [
22
22
  {
23
23
  "target_name": "pulsar",
24
- "cflags_cc": ["-std=gnu++11", "-fvisibility=hidden"],
24
+ "cflags_cc": ["-std=gnu++17", "-fvisibility=hidden"],
25
25
  "cflags!": ["-fno-exceptions"],
26
- "cflags_cc!": ["-fno-exceptions", "-std=gnu++14", "-std=gnu++17"],
26
+ "cflags_cc!": ["-fno-exceptions"],
27
27
  "include_dirs": [
28
28
  "<!@(node -p \"require('node-addon-api').include\")",
29
29
  ],
@@ -41,7 +41,8 @@
41
41
  "src/ConsumerConfig.cc",
42
42
  "src/Reader.cc",
43
43
  "src/ReaderConfig.cc",
44
- "src/ThreadSafeDeferred.cc"
44
+ "src/ThreadSafeDeferred.cc",
45
+ "src/CryptoKeyReader.cc"
45
46
  ],
46
47
  'conditions': [
47
48
  ['OS=="mac"', {
@@ -49,7 +50,7 @@
49
50
  'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
50
51
  'GCC_ENABLE_CPP_RTTI': 'YES',
51
52
  'MACOSX_DEPLOYMENT_TARGET': '11.0',
52
- 'CLANG_CXX_LANGUAGE_STANDARD': 'gnu++11',
53
+ 'CLANG_CXX_LANGUAGE_STANDARD': 'gnu++17',
53
54
  'OTHER_CFLAGS': [
54
55
  "-fPIC",
55
56
  ]
package/index.d.ts CHANGED
@@ -27,6 +27,8 @@ export interface ClientConfig {
27
27
  concurrentLookupRequest?: number;
28
28
  useTls?: boolean;
29
29
  tlsTrustCertsFilePath?: string;
30
+ tlsCertificateFilePath?: string;
31
+ tlsPrivateKeyFilePath?: string;
30
32
  tlsValidateHostname?: boolean;
31
33
  tlsAllowInsecureConnection?: boolean;
32
34
  statsIntervalInSeconds?: number;
@@ -63,6 +65,8 @@ export interface ProducerConfig {
63
65
  properties?: { [key: string]: string };
64
66
  publicKeyPath?: string;
65
67
  encryptionKey?: string;
68
+ encryptionKeys?: string[];
69
+ cryptoKeyReader?: CryptoKeyReader;
66
70
  cryptoFailureAction?: ProducerCryptoFailureAction;
67
71
  chunkingEnabled?: boolean;
68
72
  schema?: SchemaInfo;
@@ -97,6 +101,7 @@ export interface ConsumerConfig {
97
101
  listener?: (message: Message, consumer: Consumer) => void;
98
102
  readCompacted?: boolean;
99
103
  privateKeyPath?: string;
104
+ cryptoKeyReader?: CryptoKeyReader;
100
105
  cryptoFailureAction?: ConsumerCryptoFailureAction;
101
106
  maxPendingChunkedMessage?: number;
102
107
  autoAckOldestChunkedMessageOnQueueFull?: number;
@@ -168,6 +173,8 @@ export class Message {
168
173
  getRedeliveryCount(): number;
169
174
  getPartitionKey(): string;
170
175
  getOrderingKey(): string;
176
+ getProducerName(): string;
177
+ getEncryptionContext(): EncryptionContext | null;
171
178
  }
172
179
 
173
180
  export class MessageId {
@@ -195,6 +202,22 @@ export interface TopicMetadata {
195
202
  */
196
203
  export type MessageRouter = (message: Message, topicMetadata: TopicMetadata) => number;
197
204
 
205
+ export interface EncryptionKey {
206
+ key: string;
207
+ value: Buffer;
208
+ metadata: { [key: string]: string };
209
+ }
210
+
211
+ export interface EncryptionContext {
212
+ keys: EncryptionKey[];
213
+ param: Buffer;
214
+ algorithm: string;
215
+ compressionType: CompressionType;
216
+ uncompressedMessageSize: number;
217
+ batchSize: number;
218
+ isDecryptionFailed: boolean;
219
+ }
220
+
198
221
  export interface SchemaInfo {
199
222
  schemaType: SchemaType;
200
223
  name?: string;
@@ -282,6 +305,16 @@ export class AuthenticationBasic {
282
305
  });
283
306
  }
284
307
 
308
+ export interface EncryptionKeyInfo {
309
+ key: Buffer;
310
+ metadata: { [key: string]: string };
311
+ }
312
+
313
+ export class CryptoKeyReader {
314
+ getPublicKey(keyName: string, metadata: { [key: string]: string }): EncryptionKeyInfo;
315
+ getPrivateKey(keyName: string, metadata: { [key: string]: string }): EncryptionKeyInfo;
316
+ }
317
+
285
318
  export enum LogLevel {
286
319
  DEBUG = 0,
287
320
  INFO = 1,
@@ -300,6 +333,7 @@ export type HashingScheme =
300
333
  'JavaStringHash';
301
334
 
302
335
  export type CompressionType =
336
+ 'None' |
303
337
  'Zlib' |
304
338
  'LZ4' |
305
339
  'ZSTD' |
package/index.js CHANGED
@@ -37,6 +37,7 @@ const Pulsar = {
37
37
  Client,
38
38
  Message: PulsarBinding.Message,
39
39
  MessageId: PulsarBinding.MessageId,
40
+ CryptoKeyReader: PulsarBinding.CryptoKeyReader,
40
41
  AuthenticationTls,
41
42
  AuthenticationAthenz,
42
43
  AuthenticationToken,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pulsar-client",
3
- "version": "1.15.0",
3
+ "version": "1.16.0",
4
4
  "description": "Pulsar Node.js client",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -1,2 +1,2 @@
1
- CPP_CLIENT_BASE_URL=https://archive.apache.org/dist/pulsar/pulsar-client-cpp-3.7.0
2
- CPP_CLIENT_VERSION=3.7.0
1
+ CPP_CLIENT_BASE_URL=https://archive.apache.org/dist/pulsar/pulsar-client-cpp-4.0.0
2
+ CPP_CLIENT_VERSION=4.0.0
package/src/Client.cc CHANGED
@@ -39,6 +39,8 @@ static const std::string CFG_USE_TLS = "useTls";
39
39
  static const std::string CFG_TLS_TRUST_CERT = "tlsTrustCertsFilePath";
40
40
  static const std::string CFG_TLS_VALIDATE_HOSTNAME = "tlsValidateHostname";
41
41
  static const std::string CFG_TLS_ALLOW_INSECURE = "tlsAllowInsecureConnection";
42
+ static const std::string CFG_TLS_CERT_FILE = "tlsCertificateFilePath";
43
+ static const std::string CFG_TLS_PRIVATE_KEY_FILE = "tlsPrivateKeyFilePath";
42
44
  static const std::string CFG_STATS_INTERVAL = "statsIntervalInSeconds";
43
45
  static const std::string CFG_LOG = "log";
44
46
  static const std::string CFG_LOG_LEVEL = "logLevel";
@@ -196,6 +198,18 @@ Client::Client(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Client>(info)
196
198
  tlsTrustCertsFilePath.Utf8Value().c_str());
197
199
  }
198
200
 
201
+ if (clientConfig.Has(CFG_TLS_CERT_FILE) && clientConfig.Get(CFG_TLS_CERT_FILE).IsString()) {
202
+ Napi::String tlsCertFilePath = clientConfig.Get(CFG_TLS_CERT_FILE).ToString();
203
+ pulsar_client_configuration_set_tls_certificate_file_path(cClientConfig.get(),
204
+ tlsCertFilePath.Utf8Value().c_str());
205
+ }
206
+
207
+ if (clientConfig.Has(CFG_TLS_PRIVATE_KEY_FILE) && clientConfig.Get(CFG_TLS_PRIVATE_KEY_FILE).IsString()) {
208
+ Napi::String tlsPrivateKeyFilePath = clientConfig.Get(CFG_TLS_PRIVATE_KEY_FILE).ToString();
209
+ pulsar_client_configuration_set_tls_private_key_file_path(cClientConfig.get(),
210
+ tlsPrivateKeyFilePath.Utf8Value().c_str());
211
+ }
212
+
199
213
  if (clientConfig.Has(CFG_TLS_VALIDATE_HOSTNAME) &&
200
214
  clientConfig.Get(CFG_TLS_VALIDATE_HOSTNAME).IsBoolean()) {
201
215
  Napi::Boolean tlsValidateHostname = clientConfig.Get(CFG_TLS_VALIDATE_HOSTNAME).ToBoolean();
@@ -20,6 +20,7 @@
20
20
  #include "ConsumerConfig.h"
21
21
  #include "Consumer.h"
22
22
  #include "SchemaInfo.h"
23
+ #include "CryptoKeyReader.h"
23
24
  #include "Message.h"
24
25
  #include "pulsar/ConsumerConfiguration.h"
25
26
  #include <pulsar/c/consumer_configuration.h>
@@ -60,6 +61,7 @@ static const std::string CFG_KEY_SHARED_POLICY = "keySharedPolicy";
60
61
  static const std::string CFG_KEY_SHARED_POLICY_MODE = "keyShareMode";
61
62
  static const std::string CFG_KEY_SHARED_POLICY_ALLOW_OUT_OF_ORDER = "allowOutOfOrderDelivery";
62
63
  static const std::string CFG_KEY_SHARED_POLICY_STICKY_RANGES = "stickyRanges";
64
+ static const std::string CFG_CRYPTO_KEY_READER = "cryptoKeyReader";
63
65
 
64
66
  static const std::map<std::string, pulsar_consumer_type> SUBSCRIPTION_TYPE = {
65
67
  {"Exclusive", pulsar_ConsumerExclusive},
@@ -249,13 +251,21 @@ void ConsumerConfig::InitConfig(std::shared_ptr<ThreadSafeDeferred> deferred,
249
251
  std::string privateKeyPath = consumerConfig.Get(CFG_PRIVATE_KEY_PATH).ToString().Utf8Value();
250
252
  pulsar_consumer_configuration_set_default_crypto_key_reader(
251
253
  this->cConsumerConfig.get(), publicKeyPath.c_str(), privateKeyPath.c_str());
252
- if (consumerConfig.Has(CFG_CRYPTO_FAILURE_ACTION) &&
253
- consumerConfig.Get(CFG_CRYPTO_FAILURE_ACTION).IsString()) {
254
- std::string cryptoFailureAction = consumerConfig.Get(CFG_CRYPTO_FAILURE_ACTION).ToString().Utf8Value();
255
- if (CONSUMER_CRYPTO_FAILURE_ACTION.count(cryptoFailureAction)) {
256
- pulsar_consumer_configuration_set_crypto_failure_action(
257
- this->cConsumerConfig.get(), CONSUMER_CRYPTO_FAILURE_ACTION.at(cryptoFailureAction));
258
- }
254
+ }
255
+
256
+ if (consumerConfig.Has(CFG_CRYPTO_KEY_READER) && consumerConfig.Get(CFG_CRYPTO_KEY_READER).IsObject()) {
257
+ Napi::Object cryptoKeyReaderObj = consumerConfig.Get(CFG_CRYPTO_KEY_READER).As<Napi::Object>();
258
+ CryptoKeyReader *cryptoKeyReader = Napi::ObjectWrap<CryptoKeyReader>::Unwrap(cryptoKeyReaderObj);
259
+ this->cConsumerConfig.get()->consumerConfiguration.setCryptoKeyReader(
260
+ cryptoKeyReader->GetCCryptoKeyReader());
261
+ }
262
+
263
+ if (consumerConfig.Has(CFG_CRYPTO_FAILURE_ACTION) &&
264
+ consumerConfig.Get(CFG_CRYPTO_FAILURE_ACTION).IsString()) {
265
+ std::string cryptoFailureAction = consumerConfig.Get(CFG_CRYPTO_FAILURE_ACTION).ToString().Utf8Value();
266
+ if (CONSUMER_CRYPTO_FAILURE_ACTION.count(cryptoFailureAction)) {
267
+ pulsar_consumer_configuration_set_crypto_failure_action(
268
+ this->cConsumerConfig.get(), CONSUMER_CRYPTO_FAILURE_ACTION.at(cryptoFailureAction));
259
269
  }
260
270
  }
261
271
 
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+
20
+ #include "CryptoKeyReader.h"
21
+ #include <pulsar/Result.h>
22
+ #include <pulsar/EncryptionKeyInfo.h>
23
+ #include <thread>
24
+ #include <future>
25
+
26
+ class CryptoKeyReaderWrapper : public pulsar::CryptoKeyReader {
27
+ public:
28
+ CryptoKeyReaderWrapper(const Napi::Object& jsObject) : mainThreadId_(std::this_thread::get_id()) {
29
+ jsObject_.Reset(jsObject, 1);
30
+ tsfn_ = Napi::ThreadSafeFunction::New(
31
+ jsObject.Env(), Napi::Function::New(jsObject.Env(), [](const Napi::CallbackInfo& info) {}), jsObject,
32
+ "CryptoKeyReader", 0, 1);
33
+ }
34
+
35
+ ~CryptoKeyReaderWrapper() { tsfn_.Release(); }
36
+
37
+ pulsar::Result getPublicKey(const std::string& keyName, std::map<std::string, std::string>& metadata,
38
+ pulsar::EncryptionKeyInfo& encKeyInfo) const override {
39
+ return executeCallback("getPublicKey", keyName, metadata, encKeyInfo);
40
+ }
41
+
42
+ pulsar::Result getPrivateKey(const std::string& keyName, std::map<std::string, std::string>& metadata,
43
+ pulsar::EncryptionKeyInfo& encKeyInfo) const override {
44
+ return executeCallback("getPrivateKey", keyName, metadata, encKeyInfo);
45
+ }
46
+
47
+ private:
48
+ Napi::ObjectReference jsObject_;
49
+ Napi::ThreadSafeFunction tsfn_;
50
+ std::thread::id mainThreadId_;
51
+
52
+ static void parseEncryptionKeyInfo(const Napi::Object& obj, pulsar::EncryptionKeyInfo& info) {
53
+ if (obj.Has("key") && obj.Get("key").IsBuffer()) {
54
+ Napi::Buffer<char> keyBuf = obj.Get("key").As<Napi::Buffer<char>>();
55
+ info.setKey(std::string(keyBuf.Data(), keyBuf.Length()));
56
+ }
57
+ if (obj.Has("metadata") && obj.Get("metadata").IsObject()) {
58
+ std::map<std::string, std::string> metadata;
59
+ Napi::Object metaObj = obj.Get("metadata").As<Napi::Object>();
60
+ Napi::Array keys = metaObj.GetPropertyNames();
61
+ for (uint32_t i = 0; i < keys.Length(); i++) {
62
+ std::string k = keys.Get(i).ToString().Utf8Value();
63
+ std::string v = metaObj.Get(k).ToString().Utf8Value();
64
+ metadata[k] = v;
65
+ }
66
+ info.setMetadata(metadata);
67
+ }
68
+ }
69
+
70
+ pulsar::Result callJsMethod(Napi::Env env, const std::string& method, const std::string& keyName,
71
+ const std::map<std::string, std::string>& metadata,
72
+ pulsar::EncryptionKeyInfo& encKeyInfo) const {
73
+ Napi::HandleScope scope(env);
74
+
75
+ if (jsObject_.IsEmpty()) {
76
+ return pulsar::Result::ResultCryptoError;
77
+ }
78
+ Napi::Object obj = jsObject_.Value();
79
+
80
+ if (!obj.Has(method)) {
81
+ return pulsar::Result::ResultCryptoError;
82
+ }
83
+ Napi::Value funcVal = obj.Get(method);
84
+ if (!funcVal.IsFunction()) {
85
+ return pulsar::Result::ResultCryptoError;
86
+ }
87
+ Napi::Function func = funcVal.As<Napi::Function>();
88
+
89
+ Napi::Object metadataObj = Napi::Object::New(env);
90
+ for (const auto& kv : metadata) {
91
+ metadataObj.Set(kv.first, kv.second);
92
+ }
93
+
94
+ try {
95
+ Napi::Value result = func.Call(obj, {Napi::String::New(env, keyName), metadataObj});
96
+ if (result.IsObject()) {
97
+ parseEncryptionKeyInfo(result.As<Napi::Object>(), encKeyInfo);
98
+ return pulsar::Result::ResultOk;
99
+ }
100
+ } catch (const Napi::Error& e) {
101
+ return pulsar::Result::ResultCryptoError;
102
+ } catch (...) {
103
+ return pulsar::Result::ResultCryptoError;
104
+ }
105
+ return pulsar::Result::ResultCryptoError;
106
+ }
107
+
108
+ pulsar::Result executeCallback(const std::string& method, const std::string& keyName,
109
+ std::map<std::string, std::string>& metadata,
110
+ pulsar::EncryptionKeyInfo& encKeyInfo) const {
111
+ if (std::this_thread::get_id() == mainThreadId_) {
112
+ return callJsMethod(jsObject_.Env(), method, keyName, metadata, encKeyInfo);
113
+ } else {
114
+ auto promise = std::make_shared<std::promise<pulsar::Result>>();
115
+ auto future = promise->get_future();
116
+
117
+ napi_status status = tsfn_.BlockingCall([this, promise, &method, &keyName, &metadata, &encKeyInfo](
118
+ Napi::Env env, Napi::Function jsCallback) {
119
+ promise->set_value(callJsMethod(env, method, keyName, metadata, encKeyInfo));
120
+ });
121
+
122
+ if (status != napi_ok) {
123
+ return pulsar::Result::ResultCryptoError;
124
+ }
125
+
126
+ future.wait();
127
+ return future.get();
128
+ }
129
+ }
130
+ };
131
+
132
+ Napi::FunctionReference CryptoKeyReader::constructor;
133
+
134
+ void CryptoKeyReader::Init(Napi::Env env, Napi::Object exports) {
135
+ Napi::HandleScope scope(env);
136
+
137
+ Napi::Function func = DefineClass(env, "CryptoKeyReader", {});
138
+
139
+ constructor = Napi::Persistent(func);
140
+ constructor.SuppressDestruct();
141
+
142
+ exports.Set("CryptoKeyReader", func);
143
+ }
144
+
145
+ CryptoKeyReader::CryptoKeyReader(const Napi::CallbackInfo& info) : Napi::ObjectWrap<CryptoKeyReader>(info) {}
146
+
147
+ CryptoKeyReader::~CryptoKeyReader() {}
148
+
149
+ std::shared_ptr<pulsar::CryptoKeyReader> CryptoKeyReader::GetCCryptoKeyReader() {
150
+ return std::make_shared<CryptoKeyReaderWrapper>(Value());
151
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+
20
+ #ifndef CRYPTO_KEY_READER_H
21
+ #define CRYPTO_KEY_READER_H
22
+
23
+ #include <napi.h>
24
+ #include <pulsar/CryptoKeyReader.h>
25
+
26
+ class CryptoKeyReader : public Napi::ObjectWrap<CryptoKeyReader> {
27
+ public:
28
+ static void Init(Napi::Env env, Napi::Object exports);
29
+ static Napi::Object NewInstance(const Napi::CallbackInfo &info);
30
+ CryptoKeyReader(const Napi::CallbackInfo &info);
31
+ ~CryptoKeyReader();
32
+ std::shared_ptr<pulsar::CryptoKeyReader> GetCCryptoKeyReader();
33
+
34
+ private:
35
+ static Napi::FunctionReference constructor;
36
+ };
37
+
38
+ #endif
package/src/Message.cc CHANGED
@@ -20,6 +20,15 @@
20
20
  #include "Message.h"
21
21
  #include "MessageId.h"
22
22
  #include <pulsar/c/message.h>
23
+ #include <pulsar/Message.h>
24
+ #include <pulsar/MessageBuilder.h>
25
+ #include <pulsar/EncryptionContext.h>
26
+ #include <map>
27
+
28
+ struct _pulsar_message {
29
+ pulsar::MessageBuilder builder;
30
+ pulsar::Message message;
31
+ };
23
32
 
24
33
  static const std::string CFG_DATA = "data";
25
34
  static const std::string CFG_PROPS = "properties";
@@ -32,6 +41,12 @@ static const std::string CFG_DELIVER_AT = "deliverAt";
32
41
  static const std::string CFG_DISABLE_REPLICATION = "disableReplication";
33
42
  static const std::string CFG_ORDERING_KEY = "orderingKey";
34
43
 
44
+ static const std::map<pulsar::CompressionType, std::string> COMPRESSION_TYPE_MAP = {
45
+ {pulsar::CompressionNone, "None"}, {pulsar::CompressionLZ4, "LZ4"},
46
+ {pulsar::CompressionZLib, "Zlib"}, {pulsar::CompressionZSTD, "ZSTD"},
47
+ {pulsar::CompressionSNAPPY, "SNAPPY"},
48
+ };
49
+
35
50
  Napi::FunctionReference Message::constructor;
36
51
 
37
52
  Napi::Object Message::Init(Napi::Env env, Napi::Object exports) {
@@ -46,7 +61,9 @@ Napi::Object Message::Init(Napi::Env env, Napi::Object exports) {
46
61
  InstanceMethod("getEventTimestamp", &Message::GetEventTimestamp),
47
62
  InstanceMethod("getRedeliveryCount", &Message::GetRedeliveryCount),
48
63
  InstanceMethod("getPartitionKey", &Message::GetPartitionKey),
49
- InstanceMethod("getOrderingKey", &Message::GetOrderingKey)});
64
+ InstanceMethod("getOrderingKey", &Message::GetOrderingKey),
65
+ InstanceMethod("getProducerName", &Message::GetProducerName),
66
+ InstanceMethod("getEncryptionContext", &Message::GetEncryptionContext)});
50
67
 
51
68
  constructor = Napi::Persistent(func);
52
69
  constructor.SuppressDestruct();
@@ -147,6 +164,62 @@ Napi::Value Message::GetOrderingKey(const Napi::CallbackInfo &info) {
147
164
  return Napi::String::New(env, pulsar_message_get_orderingKey(this->cMessage.get()));
148
165
  }
149
166
 
167
+ Napi::Value Message::GetProducerName(const Napi::CallbackInfo &info) {
168
+ Napi::Env env = info.Env();
169
+ if (!ValidateCMessage(env)) {
170
+ return env.Null();
171
+ }
172
+ return Napi::String::New(env, pulsar_message_get_producer_name(this->cMessage.get()));
173
+ }
174
+
175
+ Napi::Value Message::GetEncryptionContext(const Napi::CallbackInfo &info) {
176
+ Napi::Env env = info.Env();
177
+ if (!ValidateCMessage(env)) {
178
+ return env.Null();
179
+ }
180
+
181
+ auto encCtxOpt = this->cMessage.get()->message.getEncryptionContext();
182
+ if (!encCtxOpt) {
183
+ return env.Null();
184
+ }
185
+
186
+ // getEncryptionContext returns std::optional<const EncryptionContext*>
187
+ const pulsar::EncryptionContext *encCtxPtr = *encCtxOpt;
188
+ if (!encCtxPtr) {
189
+ return env.Null();
190
+ }
191
+ const pulsar::EncryptionContext &encCtx = *encCtxPtr;
192
+
193
+ Napi::Object obj = Napi::Object::New(env);
194
+ Napi::Array keys = Napi::Array::New(env);
195
+ int i = 0;
196
+ for (const auto &keyInfo : encCtx.keys()) {
197
+ Napi::Object keyObj = Napi::Object::New(env);
198
+ keyObj.Set("key", Napi::String::New(env, keyInfo.key));
199
+ keyObj.Set("value", Napi::Buffer<char>::Copy(env, keyInfo.value.c_str(), keyInfo.value.length()));
200
+
201
+ Napi::Object metadataObj = Napi::Object::New(env);
202
+ for (const auto &meta : keyInfo.metadata) {
203
+ metadataObj.Set(meta.first, Napi::String::New(env, meta.second));
204
+ }
205
+ keyObj.Set("metadata", metadataObj);
206
+
207
+ keys.Set(i++, keyObj);
208
+ }
209
+ obj.Set("keys", keys);
210
+
211
+ obj.Set("param", Napi::Buffer<char>::Copy(env, encCtx.param().c_str(), encCtx.param().length()));
212
+ obj.Set("algorithm", Napi::String::New(env, encCtx.algorithm()));
213
+ const auto it = COMPRESSION_TYPE_MAP.find(encCtx.compressionType());
214
+ std::string compressionTypeStr = (it != COMPRESSION_TYPE_MAP.end()) ? it->second : "None";
215
+ obj.Set("compressionType", Napi::String::New(env, compressionTypeStr));
216
+ obj.Set("uncompressedMessageSize", Napi::Number::New(env, encCtx.uncompressedMessageSize()));
217
+ obj.Set("batchSize", Napi::Number::New(env, encCtx.batchSize()));
218
+ obj.Set("isDecryptionFailed", Napi::Boolean::New(env, encCtx.isDecryptionFailed()));
219
+
220
+ return obj;
221
+ }
222
+
150
223
  bool Message::ValidateCMessage(Napi::Env env) {
151
224
  if (this->cMessage.get()) {
152
225
  return true;
package/src/Message.h CHANGED
@@ -45,7 +45,9 @@ class Message : public Napi::ObjectWrap<Message> {
45
45
  Napi::Value GetEventTimestamp(const Napi::CallbackInfo &info);
46
46
  Napi::Value GetPartitionKey(const Napi::CallbackInfo &info);
47
47
  Napi::Value GetOrderingKey(const Napi::CallbackInfo &info);
48
+ Napi::Value GetProducerName(const Napi::CallbackInfo &info);
48
49
  Napi::Value GetRedeliveryCount(const Napi::CallbackInfo &info);
50
+ Napi::Value GetEncryptionContext(const Napi::CallbackInfo &info);
49
51
  bool ValidateCMessage(Napi::Env env);
50
52
 
51
53
  static char **NewStringArray(int size) { return (char **)calloc(sizeof(char *), size); }
@@ -18,6 +18,7 @@
18
18
  */
19
19
  #include "SchemaInfo.h"
20
20
  #include "ProducerConfig.h"
21
+ #include "CryptoKeyReader.h"
21
22
  #include "Message.h"
22
23
  #include <cstdio>
23
24
  #include <map>
@@ -45,6 +46,8 @@ static const std::string CFG_SCHEMA = "schema";
45
46
  static const std::string CFG_PROPS = "properties";
46
47
  static const std::string CFG_PUBLIC_KEY_PATH = "publicKeyPath";
47
48
  static const std::string CFG_ENCRYPTION_KEY = "encryptionKey";
49
+ static const std::string CFG_ENCRYPTION_KEYS = "encryptionKeys";
50
+ static const std::string CFG_CRYPTO_KEY_READER = "cryptoKeyReader";
48
51
  static const std::string CFG_CRYPTO_FAILURE_ACTION = "cryptoFailureAction";
49
52
  static const std::string CFG_CHUNK_ENABLED = "chunkingEnabled";
50
53
  static const std::string CFG_ACCESS_MODE = "accessMode";
@@ -67,10 +70,8 @@ static const std::map<std::string, pulsar_hashing_scheme> HASHING_SCHEME = {
67
70
  };
68
71
 
69
72
  static std::map<std::string, pulsar_compression_type> COMPRESSION_TYPE = {
70
- {"Zlib", pulsar_CompressionZLib},
71
- {"LZ4", pulsar_CompressionLZ4},
72
- {"ZSTD", pulsar_CompressionZSTD},
73
- {"SNAPPY", pulsar_CompressionSNAPPY},
73
+ {"None", pulsar_CompressionNone}, {"Zlib", pulsar_CompressionZLib}, {"LZ4", pulsar_CompressionLZ4},
74
+ {"ZSTD", pulsar_CompressionZSTD}, {"SNAPPY", pulsar_CompressionSNAPPY},
74
75
  };
75
76
 
76
77
  static std::map<std::string, pulsar_producer_crypto_failure_action> PRODUCER_CRYPTO_FAILURE_ACTION = {
@@ -239,15 +240,32 @@ ProducerConfig::ProducerConfig(const Napi::Object& producerConfig) : topic("") {
239
240
  std::string encryptionKey = producerConfig.Get(CFG_ENCRYPTION_KEY).ToString().Utf8Value();
240
241
  pulsar_producer_configuration_set_encryption_key(this->cProducerConfig.get(), encryptionKey.c_str());
241
242
  }
242
- if (producerConfig.Has(CFG_CRYPTO_FAILURE_ACTION) &&
243
- producerConfig.Get(CFG_CRYPTO_FAILURE_ACTION).IsString()) {
244
- std::string cryptoFailureAction = producerConfig.Get(CFG_CRYPTO_FAILURE_ACTION).ToString().Utf8Value();
245
- if (PRODUCER_CRYPTO_FAILURE_ACTION.count(cryptoFailureAction))
246
- pulsar_producer_configuration_set_crypto_failure_action(
247
- this->cProducerConfig.get(), PRODUCER_CRYPTO_FAILURE_ACTION.at(cryptoFailureAction));
243
+ }
244
+
245
+ if (producerConfig.Has(CFG_ENCRYPTION_KEYS) && producerConfig.Get(CFG_ENCRYPTION_KEYS).IsArray()) {
246
+ Napi::Array keys = producerConfig.Get(CFG_ENCRYPTION_KEYS).As<Napi::Array>();
247
+ for (uint32_t i = 0; i < keys.Length(); i++) {
248
+ if (keys.Get(i).IsString()) {
249
+ std::string key = keys.Get(i).ToString().Utf8Value();
250
+ this->cProducerConfig.get()->conf.addEncryptionKey(key);
251
+ }
248
252
  }
249
253
  }
250
254
 
255
+ if (producerConfig.Has(CFG_CRYPTO_KEY_READER) && producerConfig.Get(CFG_CRYPTO_KEY_READER).IsObject()) {
256
+ Napi::Object cryptoKeyReaderObj = producerConfig.Get(CFG_CRYPTO_KEY_READER).As<Napi::Object>();
257
+ CryptoKeyReader* cryptoKeyReader = Napi::ObjectWrap<CryptoKeyReader>::Unwrap(cryptoKeyReaderObj);
258
+ this->cProducerConfig.get()->conf.setCryptoKeyReader(cryptoKeyReader->GetCCryptoKeyReader());
259
+ }
260
+
261
+ if (producerConfig.Has(CFG_CRYPTO_FAILURE_ACTION) &&
262
+ producerConfig.Get(CFG_CRYPTO_FAILURE_ACTION).IsString()) {
263
+ std::string cryptoFailureAction = producerConfig.Get(CFG_CRYPTO_FAILURE_ACTION).ToString().Utf8Value();
264
+ if (PRODUCER_CRYPTO_FAILURE_ACTION.count(cryptoFailureAction))
265
+ pulsar_producer_configuration_set_crypto_failure_action(
266
+ this->cProducerConfig.get(), PRODUCER_CRYPTO_FAILURE_ACTION.at(cryptoFailureAction));
267
+ }
268
+
251
269
  if (producerConfig.Has(CFG_CHUNK_ENABLED) && producerConfig.Get(CFG_CHUNK_ENABLED).IsBoolean()) {
252
270
  bool chunkingEnabled = producerConfig.Get(CFG_CHUNK_ENABLED).ToBoolean().Value();
253
271
  pulsar_producer_configuration_set_chunking_enabled(this->cProducerConfig.get(), chunkingEnabled);
package/src/addon.cc CHANGED
@@ -24,6 +24,7 @@
24
24
  #include "Consumer.h"
25
25
  #include "Client.h"
26
26
  #include "Reader.h"
27
+ #include "CryptoKeyReader.h"
27
28
  #include <napi.h>
28
29
 
29
30
  Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
@@ -33,6 +34,7 @@ Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
33
34
  Producer::Init(env, exports);
34
35
  Consumer::Init(env, exports);
35
36
  Reader::Init(env, exports);
37
+ CryptoKeyReader::Init(env, exports);
36
38
  return Client::Init(env, exports);
37
39
  }
38
40