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