pulsar-client 1.15.0-rc.1 → 1.16.0-rc.1
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.
- package/.vscode/extensions.json +7 -0
- package/.vscode/launch.json +91 -0
- package/binding.gyp +5 -4
- package/index.d.ts +34 -0
- package/index.js +1 -0
- package/package.json +1 -1
- package/pulsar-client-cpp.txt +2 -2
- package/src/Client.cc +14 -0
- package/src/ConsumerConfig.cc +17 -7
- package/src/CryptoKeyReader.cc +151 -0
- package/src/CryptoKeyReader.h +38 -0
- package/src/Message.cc +74 -1
- package/src/Message.h +2 -0
- package/src/ProducerConfig.cc +28 -10
- package/src/addon.cc +2 -0
|
@@ -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++
|
|
24
|
+
"cflags_cc": ["-std=gnu++17", "-fvisibility=hidden"],
|
|
25
25
|
"cflags!": ["-fno-exceptions"],
|
|
26
|
-
"cflags_cc!": ["-fno-exceptions"
|
|
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++
|
|
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
package/package.json
CHANGED
package/pulsar-client-cpp.txt
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
CPP_CLIENT_BASE_URL=https://archive.apache.org/dist/pulsar/pulsar-client-cpp-
|
|
2
|
-
CPP_CLIENT_VERSION=
|
|
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();
|
package/src/ConsumerConfig.cc
CHANGED
|
@@ -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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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); }
|
package/src/ProducerConfig.cc
CHANGED
|
@@ -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
|
-
{"
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
|