@openziti/ziti-sdk-nodejs 0.7.0 → 0.8.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,749 @@
1
+ /*
2
+ Copyright Netfoundry, Inc.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ https://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ #include "ziti-nodejs.h"
18
+ #include <string.h>
19
+
20
+ // An item that will be generated here and passed into the JavaScript on_listen_client callback
21
+ typedef struct OnClientItem {
22
+
23
+ int status;
24
+ ziti_connection client;
25
+ int64_t js_arb_data;
26
+ char *caller_id;
27
+ uint8_t *app_data;
28
+ size_t app_data_sz;
29
+
30
+ } OnClientItem;
31
+
32
+
33
+ /**
34
+ * This function is responsible for calling the JavaScript 'on_listen_client_data' callback function
35
+ * that was specified when the ziti_listen(...) was called from JavaScript.
36
+ */
37
+ static void CallJs_on_listen_client_data(napi_env env, napi_value js_cb, void* context, void* data) {
38
+ napi_status status;
39
+
40
+ // This parameter is not used.
41
+ (void) context;
42
+
43
+ // Retrieve the OnClientItem created by the worker thread.
44
+ OnClientItem* item = (OnClientItem*)data;
45
+ ZITI_NODEJS_LOG(INFO, "CallJs_on_listen_client_data: client: %p, app_data: %p, len: %zu", item->client, item->app_data, item->app_data_sz);
46
+
47
+ // env and js_cb may both be NULL if Node.js is in its cleanup phase, and
48
+ // items are left over from earlier thread-safe calls from the worker thread.
49
+ // When env is NULL, we simply skip over the call into Javascript
50
+ if (env != NULL) {
51
+
52
+ // const obj = {}
53
+ napi_value undefined, js_client_item, js_client, js_buffer;
54
+ void* result_data;
55
+
56
+ // Retrieve the JavaScript `undefined` value so we can use it as the `this`
57
+ // value of the JavaScript function call.
58
+ status = napi_get_undefined(env, &undefined);
59
+ if (status != napi_ok) {
60
+ napi_throw_error(env, NULL, "Unable to napi_get_undefined");
61
+ }
62
+
63
+ int rc = napi_create_object(env, &js_client_item);
64
+ if (rc != napi_ok) {
65
+ napi_throw_error(env, "EINVAL", "failure to create object");
66
+ }
67
+
68
+ // js_client_item.client = client
69
+ napi_create_int64(env, (int64_t)item->client, &js_client);
70
+ if (rc != napi_ok) {
71
+ napi_throw_error(env, "EINVAL", "failure to create js_client_item.client");
72
+ }
73
+ rc = napi_set_named_property(env, js_client_item, "client", js_client);
74
+ if (rc != napi_ok) {
75
+ napi_throw_error(env, "EINVAL", "failure to set named property client");
76
+ }
77
+ ZITI_NODEJS_LOG(DEBUG, "client: %p", item->client);
78
+
79
+ // js_client_item.app_data = app_data
80
+ if (NULL != item->app_data) {
81
+ rc = napi_create_buffer_copy(env, item->app_data_sz, (const void*)item->app_data, (void**)&result_data, &js_buffer);
82
+ if (rc != napi_ok) {
83
+ napi_throw_error(env, "EINVAL", "failure to create js_client_item.app_data");
84
+ }
85
+ rc = napi_set_named_property(env, js_client_item, "app_data", js_buffer);
86
+ if (rc != napi_ok) {
87
+ napi_throw_error(env, "EINVAL", "failure to set named property app_data");
88
+ }
89
+ } else {
90
+ rc = napi_set_named_property(env, js_client_item, "app_data", undefined);
91
+ }
92
+ ZITI_NODEJS_LOG(DEBUG, "app_data: %p", item->app_data);
93
+
94
+
95
+ ZITI_NODEJS_LOG(INFO, "calling JS on_listen_client_data callback...");
96
+
97
+ // Call the JavaScript function and pass it the data
98
+ status = napi_call_function(
99
+ env,
100
+ undefined,
101
+ js_cb,
102
+ 1,
103
+ &js_client_item,
104
+ NULL
105
+ );
106
+ if (status != napi_ok) {
107
+ napi_throw_error(env, NULL, "Unable to napi_call_function");
108
+ }
109
+
110
+ }
111
+
112
+ }
113
+
114
+ static ssize_t on_listen_client_data(ziti_connection client, const uint8_t *data, ssize_t len) {
115
+
116
+ ZITI_NODEJS_LOG(DEBUG, "on_listen_client_data: client: %p, data: %p, len: %zu", client, data, len);
117
+
118
+ ListenAddonData* addon_data = (ListenAddonData*) ziti_conn_data(client);
119
+
120
+ OnClientItem* item = memset(malloc(sizeof(*item)), 0, sizeof(*item));
121
+ item->client = client;
122
+ item->js_arb_data = addon_data->js_arb_data;
123
+
124
+ if ((NULL != data) && ((len > 0))) {
125
+ item->app_data = calloc(1, len + 1);
126
+ memcpy((void*)item->app_data, data, len);
127
+ item->app_data_sz = len;
128
+ }
129
+
130
+ if (len > 0) {
131
+ /* NOP */
132
+ }
133
+ else if (len == ZITI_EOF) {
134
+ ZITI_NODEJS_LOG(DEBUG, "on_listen_client_data: client disconnected");
135
+ ziti_close_write(client);
136
+ }
137
+ else {
138
+ ZITI_NODEJS_LOG(ERROR, "on_listen_client_data: error: %zd(%s)", len, ziti_errorstr(len));
139
+ ziti_close(client, NULL);
140
+ }
141
+
142
+ // Initiate the call into the JavaScript callback.
143
+ // The call into JavaScript will not have happened
144
+ // when this function returns, but it will be queued.
145
+ napi_status nstatus = napi_call_threadsafe_function(
146
+ addon_data->tsfn_on_listen_client_data,
147
+ item, // Send the status we received over to the JS callback
148
+ napi_tsfn_blocking);
149
+ if (nstatus != napi_ok) {
150
+ ZITI_NODEJS_LOG(ERROR, "Unable to napi_call_threadsafe_function");
151
+ }
152
+
153
+ return len;
154
+ }
155
+
156
+
157
+ /**
158
+ * This function is responsible for calling the JavaScript 'on_listen_client_connect' callback function
159
+ * that was specified when the ziti_listen(...) was called from JavaScript.
160
+ */
161
+ static void CallJs_on_listen_client_connect(napi_env env, napi_value js_cb, void* context, void* data) {
162
+ napi_status status;
163
+
164
+ // This parameter is not used.
165
+ (void) context;
166
+
167
+ // Retrieve the OnClientItem created by the worker thread.
168
+ OnClientItem* item = (OnClientItem*)data;
169
+ ZITI_NODEJS_LOG(INFO, "CallJs_on_listen_client_connect: client: %p, status: %d", item->client, item->status);
170
+
171
+ // env and js_cb may both be NULL if Node.js is in its cleanup phase, and
172
+ // items are left over from earlier thread-safe calls from the worker thread.
173
+ // When env is NULL, we simply skip over the call into Javascript
174
+ if (env != NULL) {
175
+
176
+ // const obj = {}
177
+ napi_value undefined, js_client_item, js_client, js_clt_ctx, js_status, js_arb_data;
178
+
179
+ // Retrieve the JavaScript `undefined` value so we can use it as the `this`
180
+ // value of the JavaScript function call.
181
+ status = napi_get_undefined(env, &undefined);
182
+ if (status != napi_ok) {
183
+ napi_throw_error(env, NULL, "Unable to napi_get_undefined");
184
+ }
185
+
186
+ int rc = napi_create_object(env, &js_client_item);
187
+ if (rc != napi_ok) {
188
+ napi_throw_error(env, "EINVAL", "failure to create object");
189
+ }
190
+ rc = napi_create_object(env, &js_clt_ctx);
191
+ if (rc != napi_ok) {
192
+ napi_throw_error(env, "EINVAL", "failure to create object");
193
+ }
194
+
195
+ // js_client_item.status = status
196
+ rc = napi_create_int32(env, item->status, &js_status);
197
+ if (rc != napi_ok) {
198
+ napi_throw_error(env, "EINVAL", "failure to create obj.status");
199
+ }
200
+ rc = napi_set_named_property(env, js_client_item, "status", js_status);
201
+ if (rc != napi_ok) {
202
+ napi_throw_error(env, "EINVAL", "failure to set named property status");
203
+ }
204
+ ZITI_NODEJS_LOG(DEBUG, "status: %d", item->status);
205
+
206
+ // js_client_item.js_arb_data = js_arb_data
207
+ if (item->js_arb_data) {
208
+ rc = napi_create_int64(env, item->js_arb_data, &js_arb_data);
209
+ if (rc != napi_ok) {
210
+ napi_throw_error(env, "EINVAL", "failure to create obj.js_arb_data");
211
+ }
212
+ rc = napi_set_named_property(env, js_client_item, "js_arb_data", js_arb_data);
213
+ if (rc != napi_ok) {
214
+ napi_throw_error(env, "EINVAL", "failure to set named property status");
215
+ }
216
+ ZITI_NODEJS_LOG(DEBUG, "js_arb_data: %lld", item->js_arb_data);
217
+ } else {
218
+ rc = napi_set_named_property(env, js_client_item, "js_arb_data", undefined);
219
+ }
220
+
221
+ // js_client_item.client = client
222
+ napi_create_int64(env, (int64_t)item->client, &js_client);
223
+ if (rc != napi_ok) {
224
+ napi_throw_error(env, "EINVAL", "failure to create obj.client");
225
+ }
226
+ rc = napi_set_named_property(env, js_client_item, "client", js_client);
227
+ if (rc != napi_ok) {
228
+ napi_throw_error(env, "EINVAL", "failure to set named property req");
229
+ }
230
+ ZITI_NODEJS_LOG(DEBUG, "client: %p", item->client);
231
+
232
+
233
+ ZITI_NODEJS_LOG(INFO, "calling JS on_listen_client_connect callback...");
234
+
235
+ // Call the JavaScript function and pass it the data
236
+ status = napi_call_function(
237
+ env,
238
+ undefined,
239
+ js_cb,
240
+ 1,
241
+ &js_client_item,
242
+ NULL
243
+ );
244
+ if (status != napi_ok) {
245
+ napi_throw_error(env, NULL, "Unable to napi_call_function");
246
+ }
247
+
248
+ }
249
+ }
250
+
251
+ static void on_listen_client_connect(ziti_connection client, int status) {
252
+
253
+ ZITI_NODEJS_LOG(DEBUG, "on_listen_client_connect: client: %p, status: %d", client, status);
254
+
255
+ ListenAddonData* addon_data = (ListenAddonData*) ziti_conn_data(client);
256
+
257
+ OnClientItem* item = memset(malloc(sizeof(*item)), 0, sizeof(*item));
258
+ item->status = status;
259
+ item->client = client;
260
+ item->js_arb_data = addon_data->js_arb_data;
261
+
262
+ // Initiate the call into the JavaScript callback.
263
+ // The call into JavaScript will not have happened
264
+ // when this function returns, but it will be queued.
265
+ napi_status nstatus = napi_call_threadsafe_function(
266
+ addon_data->tsfn_on_listen_client_connect,
267
+ item, // Send the status we received over to the JS callback
268
+ napi_tsfn_blocking);
269
+ if (nstatus != napi_ok) {
270
+ ZITI_NODEJS_LOG(ERROR, "Unable to napi_call_threadsafe_function");
271
+ }
272
+
273
+ }
274
+
275
+ /**
276
+ * This function is responsible for calling the JavaScript 'on_listen' callback function
277
+ * that was specified when the ziti_listen(...) was called from JavaScript.
278
+ */
279
+ static void CallJs_on_listen(napi_env env, napi_value js_cb, void* context, void* data) {
280
+ napi_status status;
281
+
282
+ // This parameter is not used.
283
+ (void) context;
284
+
285
+ // env and js_cb may both be NULL if Node.js is in its cleanup phase, and
286
+ // items are left over from earlier thread-safe calls from the worker thread.
287
+ // When env is NULL, we simply skip over the call into Javascript
288
+ if (env != NULL) {
289
+ napi_value undefined, js_rc;
290
+
291
+ ZITI_NODEJS_LOG(INFO, "CallJs_on_listen: data: %lld", (int64_t)data);
292
+
293
+ // Retrieve the rc created by the worker thread.
294
+ int64_t rc = (int64_t)data;
295
+ status = napi_create_int64(env, (int64_t)rc, &js_rc);
296
+ if (status != napi_ok) {
297
+ napi_throw_error(env, NULL, "Failed to napi_create_int64");
298
+ }
299
+
300
+ // Retrieve the JavaScript `undefined` value so we can use it as the `this`
301
+ // value of the JavaScript function call.
302
+ status = napi_get_undefined(env, &undefined);
303
+ if (status != napi_ok) {
304
+ napi_throw_error(env, NULL, "Unable to napi_get_undefined");
305
+ }
306
+
307
+ ZITI_NODEJS_LOG(INFO, "calling JS on_listen callback...");
308
+
309
+ // Call the JavaScript function and pass it the rc
310
+ status = napi_call_function(
311
+ env,
312
+ undefined,
313
+ js_cb,
314
+ 1,
315
+ &js_rc,
316
+ NULL
317
+ );
318
+ ZITI_NODEJS_LOG(INFO, "returned from JS on_listen callback...");
319
+ if (status != napi_ok) {
320
+ napi_throw_error(env, NULL, "Failed to napi_call_function");
321
+ }
322
+ }
323
+ }
324
+
325
+
326
+ /**
327
+ * This function is responsible for calling the JavaScript 'data' callback function
328
+ * that was specified when the ziti_dial(...) was called from JavaScript.
329
+ */
330
+ static void CallJs_on_listen_client(napi_env env, napi_value js_cb, void* context, void* data) {
331
+ napi_status status;
332
+
333
+ // This parameter is not used.
334
+ (void) context;
335
+
336
+ // Retrieve the OnClientItem created by the worker thread.
337
+ OnClientItem* item = (OnClientItem*)data;
338
+ ZITI_NODEJS_LOG(INFO, "CallJs_on_listen_client: client: %p, status: %d, caller_id: %p, app_data: %p, app_data_sz: %zu", item->client, item->status, item->caller_id, item->app_data, item->app_data_sz);
339
+
340
+ // env and js_cb may both be NULL if Node.js is in its cleanup phase, and
341
+ // items are left over from earlier thread-safe calls from the worker thread.
342
+ // When env is NULL, we simply skip over the call into Javascript and free the
343
+ // items.
344
+ if (env != NULL) {
345
+
346
+ napi_value undefined;
347
+
348
+ // const obj = {}
349
+ napi_value js_client_item, js_client, js_clt_ctx, js_status, js_arb_data, js_caller_id, js_buffer;
350
+ void* result_data;
351
+
352
+ // Retrieve the JavaScript `undefined` value so we can use it as the `this`
353
+ // value of the JavaScript function call.
354
+ status = napi_get_undefined(env, &undefined);
355
+ if (status != napi_ok) {
356
+ napi_throw_error(env, NULL, "Unable to napi_get_undefined (3)");
357
+ }
358
+
359
+ int rc = napi_create_object(env, &js_client_item);
360
+ if (rc != napi_ok) {
361
+ napi_throw_error(env, "EINVAL", "failure to create object");
362
+ }
363
+ rc = napi_create_object(env, &js_clt_ctx);
364
+ if (rc != napi_ok) {
365
+ napi_throw_error(env, "EINVAL", "failure to create object");
366
+ }
367
+
368
+ // js_client_item.status = status
369
+ rc = napi_create_int32(env, item->status, &js_status);
370
+ if (rc != napi_ok) {
371
+ napi_throw_error(env, "EINVAL", "failure to create obj.status");
372
+ }
373
+ rc = napi_set_named_property(env, js_client_item, "status", js_status);
374
+ if (rc != napi_ok) {
375
+ napi_throw_error(env, "EINVAL", "failure to set named property status");
376
+ }
377
+ ZITI_NODEJS_LOG(DEBUG, "status: %d", item->status);
378
+
379
+ // js_client_item.js_arb_data = js_arb_data
380
+ if (item->js_arb_data) {
381
+ rc = napi_create_int64(env, item->js_arb_data, &js_arb_data);
382
+ if (rc != napi_ok) {
383
+ napi_throw_error(env, "EINVAL", "failure to create obj.js_arb_data");
384
+ }
385
+ rc = napi_set_named_property(env, js_client_item, "js_arb_data", js_arb_data);
386
+ if (rc != napi_ok) {
387
+ napi_throw_error(env, "EINVAL", "failure to set named property status");
388
+ }
389
+ ZITI_NODEJS_LOG(DEBUG, "js_arb_data: %lld", item->js_arb_data);
390
+ } else {
391
+ rc = napi_set_named_property(env, js_client_item, "js_arb_data", undefined);
392
+ }
393
+
394
+ // js_client_item.client = client
395
+ napi_create_int64(env, (int64_t)item->client, &js_client);
396
+ if (rc != napi_ok) {
397
+ napi_throw_error(env, "EINVAL", "failure to create obj.client");
398
+ }
399
+ rc = napi_set_named_property(env, js_client_item, "client", js_client);
400
+ if (rc != napi_ok) {
401
+ napi_throw_error(env, "EINVAL", "failure to set named property req");
402
+ }
403
+ ZITI_NODEJS_LOG(DEBUG, "client: %p", item->client);
404
+
405
+ // js_clt_ctx.caller_id = caller_id
406
+ if (NULL != item->caller_id) {
407
+ rc = napi_create_string_utf8(env, (const char*)item->caller_id, strlen(item->caller_id), &js_caller_id);
408
+ if (rc != napi_ok) {
409
+ napi_throw_error(env, "EINVAL", "failure to create js_clt_ctx.caller_id");
410
+ }
411
+ rc = napi_set_named_property(env, js_clt_ctx, "caller_id", js_caller_id);
412
+ if (rc != napi_ok) {
413
+ napi_throw_error(env, "EINVAL", "failure to set named property caller_id");
414
+ }
415
+ ZITI_NODEJS_LOG(DEBUG, "caller_id: %s", item->caller_id);
416
+ } else {
417
+ rc = napi_set_named_property(env, js_clt_ctx, "caller_id", undefined);
418
+ }
419
+
420
+ // js_clt_ctx.app_data = app_data
421
+ if (NULL != item->app_data) {
422
+ rc = napi_create_buffer_copy(env, item->app_data_sz, (const void*)item->app_data, (void**)&result_data, &js_buffer);
423
+ if (rc != napi_ok) {
424
+ napi_throw_error(env, "EINVAL", "failure to create js_clt_ctx.app_data");
425
+ }
426
+ rc = napi_set_named_property(env, js_clt_ctx, "app_data", js_buffer);
427
+ if (rc != napi_ok) {
428
+ napi_throw_error(env, "EINVAL", "failure to set named property app_data");
429
+ }
430
+ } else {
431
+ rc = napi_set_named_property(env, js_clt_ctx, "app_data", undefined);
432
+ }
433
+
434
+ // js_client_item.clt_ctx = js_clt_ctx
435
+ rc = napi_set_named_property(env, js_client_item, "clt_ctx", js_clt_ctx);
436
+ if (rc != napi_ok) {
437
+ napi_throw_error(env, "EINVAL", "failure to set named property js_clt_ctx");
438
+ }
439
+ ZITI_NODEJS_LOG(DEBUG, "js_clt_ctx added");
440
+
441
+ // Call the JavaScript function and pass it the data
442
+ status = napi_call_function(
443
+ env,
444
+ undefined,
445
+ js_cb,
446
+ 1,
447
+ &js_client_item,
448
+ NULL
449
+ );
450
+ if (status != napi_ok) {
451
+ napi_throw_error(env, NULL, "Unable to napi_call_function");
452
+ }
453
+ }
454
+ }
455
+
456
+
457
+
458
+ /**
459
+ *
460
+ */
461
+ void on_listen_client(ziti_connection serv, ziti_connection client, int status, ziti_client_ctx *clt_ctx) {
462
+
463
+ ZITI_NODEJS_LOG(INFO, "on_listen_client: client: %p, status: %d, clt_ctx: %p", client, status, clt_ctx);
464
+
465
+ napi_status nstatus;
466
+
467
+ ListenAddonData* addon_data = (ListenAddonData*) ziti_conn_data(serv);
468
+
469
+ if (status == ZITI_OK) {
470
+
471
+ ziti_conn_set_data(client, addon_data);
472
+
473
+ const char *source_identity = clt_ctx->caller_id;
474
+ if (source_identity != NULL) {
475
+ ZITI_NODEJS_LOG(DEBUG, "on_listen_client: incoming connection from '%s'\n", source_identity );
476
+ }
477
+ else {
478
+ ZITI_NODEJS_LOG(DEBUG, "on_listen_client: incoming connection from unidentified client" );
479
+ }
480
+ if (clt_ctx->app_data != NULL) {
481
+ ZITI_NODEJS_LOG(DEBUG, "on_listen_client: got app data '%.*s'!\n", (int) clt_ctx->app_data_sz, clt_ctx->app_data );
482
+ }
483
+
484
+ ziti_accept(client, on_listen_client_connect, on_listen_client_data);
485
+
486
+ } else {
487
+ ZITI_NODEJS_LOG(DEBUG, "on_listen_client: failed to accept client: %s(%d)\n", ziti_errorstr(status), status );
488
+ }
489
+
490
+ OnClientItem* item = memset(malloc(sizeof(*item)), 0, sizeof(*item));
491
+ item->status = status;
492
+ item->js_arb_data = addon_data->js_arb_data;
493
+ item->client = client;
494
+ item->caller_id = strdup(clt_ctx->caller_id);
495
+ if (NULL != clt_ctx->app_data) {
496
+ item->app_data_sz = clt_ctx->app_data_sz;
497
+ item->app_data = calloc(1, clt_ctx->app_data_sz + 1);
498
+ memcpy((void*)item->app_data, clt_ctx->app_data, clt_ctx->app_data_sz);
499
+ }
500
+
501
+ // Initiate the call into the JavaScript callback.
502
+ // The call into JavaScript will not have happened
503
+ // when this function returns, but it will be queued.
504
+ nstatus = napi_call_threadsafe_function(
505
+ addon_data->tsfn_on_listen_client,
506
+ item, // Send the client ctx we received over to the JS callback
507
+ napi_tsfn_blocking);
508
+ if (nstatus != napi_ok) {
509
+ ZITI_NODEJS_LOG(ERROR, "Unable to napi_call_threadsafe_function");
510
+ }
511
+
512
+ }
513
+
514
+
515
+ /**
516
+ *
517
+ */
518
+ void on_listen(ziti_connection serv, int status) {
519
+ napi_status nstatus;
520
+
521
+ ListenAddonData* addon_data = (ListenAddonData*) ziti_conn_data(serv);
522
+
523
+ if (status == ZITI_OK) {
524
+ ZITI_NODEJS_LOG(DEBUG, "on_listen: successfully bound to service[%s]", addon_data->service_name );
525
+ }
526
+ else {
527
+ ZITI_NODEJS_LOG(DEBUG, "on_listen: failed to bind to service[%s]\n", addon_data->service_name );
528
+ ziti_close(serv, NULL);
529
+ }
530
+
531
+ // Initiate the call into the JavaScript callback.
532
+ // The call into JavaScript will not have happened
533
+ // when this function returns, but it will be queued.
534
+ nstatus = napi_call_threadsafe_function(
535
+ addon_data->tsfn_on_listen,
536
+ (void*)(int64_t)status, // Send the status over to the JS callback
537
+ napi_tsfn_blocking);
538
+ if (nstatus != napi_ok) {
539
+ ZITI_NODEJS_LOG(ERROR, "Unable to napi_call_threadsafe_function");
540
+ }
541
+ }
542
+
543
+
544
+ /**
545
+ *
546
+ */
547
+ napi_value _ziti_listen(napi_env env, const napi_callback_info info) {
548
+
549
+ napi_status status;
550
+ size_t argc = 6;
551
+ napi_value args[6];
552
+ status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
553
+ if (status != napi_ok) {
554
+ napi_throw_error(env, NULL, "Failed to parse arguments");
555
+ }
556
+
557
+ if (argc < 6) {
558
+ napi_throw_error(env, "EINVAL", "Too few arguments");
559
+ return NULL;
560
+ }
561
+
562
+ // Obtain service name
563
+ size_t result;
564
+ char ServiceName[256]; //TODO: make this smarter
565
+ status = napi_get_value_string_utf8(env, args[0], ServiceName, 256, &result);
566
+ if (status != napi_ok) {
567
+ napi_throw_error(env, NULL, "Failed to get Service Name");
568
+ }
569
+ ZITI_NODEJS_LOG(DEBUG, "ServiceName: %s", ServiceName);
570
+
571
+ ListenAddonData* addon_data = memset(malloc(sizeof(*addon_data)), 0, sizeof(*addon_data));
572
+
573
+ // Obtain (optional) arbitrary-data value
574
+ int64_t js_arb_data;
575
+ status = napi_get_value_int64(env, args[1], &js_arb_data);
576
+ if (status != napi_ok) {
577
+ napi_throw_error(env, NULL, "Failed to get js_arb_data");
578
+ }
579
+ addon_data->js_arb_data = js_arb_data;
580
+ ZITI_NODEJS_LOG(DEBUG, "js_arb_data: %lld", js_arb_data);
581
+
582
+ // Obtain ptr to JS 'on_listen' callback function
583
+ napi_value js_on_listen = args[2];
584
+ napi_value work_name_listen;
585
+
586
+ // Create a string to describe this asynchronous operation.
587
+ status = napi_create_string_utf8(
588
+ env,
589
+ "N-API on_listen",
590
+ NAPI_AUTO_LENGTH,
591
+ &work_name_listen);
592
+ if (status != napi_ok) {
593
+ napi_throw_error(env, NULL, "Unable to napi_create_string_utf8");
594
+ }
595
+
596
+ // Convert the callback retrieved from JavaScript into a thread-safe function (tsfn)
597
+ // which we can call from a worker thread.
598
+ status = napi_create_threadsafe_function(
599
+ env,
600
+ js_on_listen,
601
+ NULL,
602
+ work_name_listen,
603
+ 0,
604
+ 1,
605
+ NULL,
606
+ NULL,
607
+ NULL,
608
+ CallJs_on_listen,
609
+ &(addon_data->tsfn_on_listen));
610
+ if (status != napi_ok) {
611
+ napi_throw_error(env, NULL, "Unable to napi_create_threadsafe_function");
612
+ }
613
+
614
+ // Obtain ptr to JS 'on_listen_client' callback function
615
+ napi_value js_on_listen_client = args[3];
616
+ napi_value work_name_client;
617
+
618
+ // Create a string to describe this asynchronous operation.
619
+ status = napi_create_string_utf8(
620
+ env,
621
+ "N-API on_listen_client",
622
+ NAPI_AUTO_LENGTH,
623
+ &work_name_client);
624
+ if (status != napi_ok) {
625
+ napi_throw_error(env, NULL, "Unable to napi_create_string_utf8");
626
+ }
627
+
628
+ // Convert the callback retrieved from JavaScript into a thread-safe function (tsfn)
629
+ // which we can call from a worker thread.
630
+ status = napi_create_threadsafe_function(
631
+ env,
632
+ js_on_listen_client,
633
+ NULL,
634
+ work_name_client,
635
+ 0,
636
+ 1,
637
+ NULL,
638
+ NULL,
639
+ NULL,
640
+ CallJs_on_listen_client,
641
+ &(addon_data->tsfn_on_listen_client));
642
+ if (status != napi_ok) {
643
+ napi_throw_error(env, NULL, "Unable to napi_create_threadsafe_function");
644
+ }
645
+
646
+ // Obtain ptr to JS 'on_listen_client_connect' callback function
647
+ napi_value js_on_listen_client_connect = args[4];
648
+
649
+ // Create a string to describe this asynchronous operation.
650
+ status = napi_create_string_utf8(
651
+ env,
652
+ "N-API on_listen_client_connect",
653
+ NAPI_AUTO_LENGTH,
654
+ &work_name_client);
655
+ if (status != napi_ok) {
656
+ napi_throw_error(env, NULL, "Unable to napi_create_string_utf8");
657
+ }
658
+
659
+ // Convert the callback retrieved from JavaScript into a thread-safe function (tsfn)
660
+ // which we can call from a worker thread.
661
+ status = napi_create_threadsafe_function(
662
+ env,
663
+ js_on_listen_client_connect,
664
+ NULL,
665
+ work_name_client,
666
+ 0,
667
+ 1,
668
+ NULL,
669
+ NULL,
670
+ NULL,
671
+ CallJs_on_listen_client_connect,
672
+ &(addon_data->tsfn_on_listen_client_connect));
673
+ if (status != napi_ok) {
674
+ napi_throw_error(env, NULL, "Unable to napi_create_threadsafe_function");
675
+ }
676
+
677
+ // Obtain ptr to JS 'on_listen_client_data' callback function
678
+ napi_value js_on_listen_client_data = args[5];
679
+
680
+ // Create a string to describe this asynchronous operation.
681
+ status = napi_create_string_utf8(
682
+ env,
683
+ "N-API on_listen_client_data",
684
+ NAPI_AUTO_LENGTH,
685
+ &work_name_client);
686
+ if (status != napi_ok) {
687
+ napi_throw_error(env, NULL, "Unable to napi_create_string_utf8");
688
+ }
689
+
690
+ // Convert the callback retrieved from JavaScript into a thread-safe function (tsfn)
691
+ // which we can call from a worker thread.
692
+ status = napi_create_threadsafe_function(
693
+ env,
694
+ js_on_listen_client_data,
695
+ NULL,
696
+ work_name_client,
697
+ 0,
698
+ 1,
699
+ NULL,
700
+ NULL,
701
+ NULL,
702
+ CallJs_on_listen_client_data,
703
+ &(addon_data->tsfn_on_listen_client_data));
704
+ if (status != napi_ok) {
705
+ napi_throw_error(env, NULL, "Unable to napi_create_threadsafe_function");
706
+ }
707
+
708
+ // Init a Ziti connection object, and attach our add-on data to it so we can
709
+ // pass context around between our callbacks, as propagate it all the way out
710
+ // to the JavaScript callbacks
711
+ addon_data->service_name = strdup(ServiceName);
712
+ int rc = ziti_conn_init(ztx, &addon_data->server, addon_data);
713
+ if (rc != ZITI_OK) {
714
+ napi_throw_error(env, NULL, "failure in 'ziti_conn_init");
715
+ }
716
+
717
+ // Start listening
718
+ ZITI_NODEJS_LOG(DEBUG, "calling ziti_listen_with_options: %p", ztx);
719
+ ziti_listen_opts listen_opts = {
720
+ .bind_using_edge_identity = false,
721
+ };
722
+
723
+ ziti_listen_with_options(addon_data->server, ServiceName, &listen_opts, on_listen, on_listen_client);
724
+ // ziti_listen_with_options(addon_data->server, ServiceName, NULL, on_listen, on_listen_client);
725
+
726
+ return NULL;
727
+ }
728
+
729
+
730
+
731
+ /**
732
+ *
733
+ */
734
+ void expose_ziti_listen(napi_env env, napi_value exports) {
735
+ napi_status status;
736
+ napi_value fn;
737
+
738
+ status = napi_create_function(env, NULL, 0, _ziti_listen, NULL, &fn);
739
+ if (status != napi_ok) {
740
+ napi_throw_error(env, NULL, "Unable to wrap native function '_ziti_listen");
741
+ }
742
+
743
+ status = napi_set_named_property(env, exports, "ziti_listen", fn);
744
+ if (status != napi_ok) {
745
+ napi_throw_error(env, NULL, "Unable to populate exports for 'ziti_listen");
746
+ }
747
+
748
+ }
749
+