node-datachannel 0.5.5 → 0.7.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,43 @@
1
+ #ifndef WEB_SOCKET_SERVER_WRAPPER_H
2
+ #define WEB_SOCKET_SERVER_WRAPPER_H
3
+
4
+ #include <iostream>
5
+ #include <string>
6
+ #include <variant>
7
+ #include <memory>
8
+ #include <unordered_set>
9
+
10
+ #include <napi.h>
11
+ #include <rtc/rtc.hpp>
12
+
13
+ #include "web-socket-wrapper.h"
14
+ #include "thread-safe-callback.h"
15
+
16
+ class WebSocketServerWrapper : public Napi::ObjectWrap<WebSocketServerWrapper>
17
+ {
18
+ public:
19
+ static Napi::FunctionReference constructor;
20
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
21
+ WebSocketServerWrapper(const Napi::CallbackInfo &info);
22
+ ~WebSocketServerWrapper();
23
+
24
+ // Functions
25
+
26
+ void stop(const Napi::CallbackInfo &info);
27
+ Napi::Value port(const Napi::CallbackInfo &info);
28
+
29
+ // Callbacks
30
+ void onClient(const Napi::CallbackInfo &info);
31
+
32
+ // Close all existing WebSocketServers
33
+ static void StopAll();
34
+
35
+ private:
36
+ static std::unordered_set<WebSocketServerWrapper*> instances;
37
+ std::unique_ptr<rtc::WebSocketServer> mWebSocketServerPtr = nullptr;
38
+ std::unique_ptr<ThreadSafeCallback> mOnClientCallback = nullptr;
39
+
40
+ void doStop();
41
+ };
42
+
43
+ #endif // WEB_SOCKET_SERVER_WRAPPER_H
@@ -0,0 +1,664 @@
1
+ #include "web-socket-wrapper.h"
2
+
3
+ #include "plog/Log.h"
4
+
5
+ Napi::FunctionReference WebSocketWrapper::constructor;
6
+ std::unordered_set<WebSocketWrapper *> WebSocketWrapper::instances;
7
+
8
+ void WebSocketWrapper::CloseAll()
9
+ {
10
+ PLOG_DEBUG << "CloseAll() called";
11
+ auto copy(instances);
12
+ for (auto inst : copy)
13
+ inst->doClose();
14
+ }
15
+
16
+ void WebSocketWrapper::CleanupAll()
17
+ {
18
+ PLOG_DEBUG << "CleanupAll() called";
19
+ auto copy(instances);
20
+ for (auto inst : copy)
21
+ inst->doCleanup();
22
+ }
23
+
24
+ Napi::Object WebSocketWrapper::Init(Napi::Env env, Napi::Object exports)
25
+ {
26
+ Napi::HandleScope scope(env);
27
+
28
+ Napi::Function func = DefineClass(
29
+ env,
30
+ "WebSocket",
31
+ {
32
+ InstanceMethod("open", &WebSocketWrapper::open),
33
+ InstanceMethod("close", &WebSocketWrapper::close),
34
+ InstanceMethod("sendMessage", &WebSocketWrapper::sendMessage),
35
+ InstanceMethod("sendMessageBinary", &WebSocketWrapper::sendMessageBinary),
36
+ InstanceMethod("isOpen", &WebSocketWrapper::isOpen),
37
+ InstanceMethod("bufferedAmount", &WebSocketWrapper::bufferedAmount),
38
+ InstanceMethod("maxMessageSize", &WebSocketWrapper::maxMessageSize),
39
+ InstanceMethod("setBufferedAmountLowThreshold", &WebSocketWrapper::setBufferedAmountLowThreshold),
40
+ InstanceMethod("onOpen", &WebSocketWrapper::onOpen),
41
+ InstanceMethod("onClosed", &WebSocketWrapper::onClosed),
42
+ InstanceMethod("onError", &WebSocketWrapper::onError),
43
+ InstanceMethod("onBufferedAmountLow", &WebSocketWrapper::onBufferedAmountLow),
44
+ InstanceMethod("onMessage", &WebSocketWrapper::onMessage),
45
+ });
46
+
47
+ constructor = Napi::Persistent(func);
48
+ constructor.SuppressDestruct();
49
+
50
+ exports.Set("WebSocket", func);
51
+ return exports;
52
+ }
53
+
54
+ WebSocketWrapper::WebSocketWrapper(const Napi::CallbackInfo &info) : Napi::ObjectWrap<WebSocketWrapper>(info)
55
+ {
56
+ PLOG_DEBUG << "Constructor called";
57
+ Napi::Env env = info.Env();
58
+
59
+ // Create WebSocket using rtc::WebSocket provided by WebSocketServer
60
+ if (info.Length() > 1)
61
+ {
62
+ mWebSocketPtr = *(info[1].As<Napi::External<std::shared_ptr<rtc::WebSocket>>>().Data());
63
+ PLOG_DEBUG << "Using WebSocket got from WebSocketServer";
64
+ instances.insert(this);
65
+ return;
66
+ }
67
+
68
+ // Create WebSocket without config
69
+ if (info.Length() == 0)
70
+ {
71
+ try
72
+ {
73
+ PLOG_DEBUG << "Creating a new WebSocket without config";
74
+ mWebSocketPtr = std::make_unique<rtc::WebSocket>();
75
+ }
76
+ catch (std::exception &ex)
77
+ {
78
+ Napi::Error::New(env, std::string("libdatachannel error while creating WebSocket without config: ") + ex.what()).ThrowAsJavaScriptException();
79
+ return;
80
+ }
81
+ instances.insert(this);
82
+ return;
83
+ }
84
+
85
+ // Create WebSocket with config
86
+ PLOG_DEBUG << "Creating a new WebSocket with config";
87
+
88
+ Napi::Object config = info[0].As<Napi::Object>();
89
+ rtc::WebSocketConfiguration webSocketConfig;
90
+
91
+ if (config.Has("disableTlsVerification"))
92
+ {
93
+ if (!config.Get("disableTlsVerification").IsBoolean())
94
+ {
95
+ Napi::TypeError::New(info.Env(), "disableTlsVerification must be boolean").ThrowAsJavaScriptException();
96
+ return;
97
+ }
98
+ webSocketConfig.disableTlsVerification = config.Get("disableTlsVerification").ToBoolean();
99
+ }
100
+
101
+ // Proxy Server
102
+ if (config.Has("proxyServer") && config.Get("proxyServer").IsObject())
103
+ {
104
+ Napi::Object proxyServer = config.Get("proxyServer").As<Napi::Object>();
105
+
106
+ // IP
107
+ std::string ip = proxyServer.Get("ip").As<Napi::String>();
108
+
109
+ // Port
110
+ uint16_t port = proxyServer.Get("port").As<Napi::Number>().Uint32Value();
111
+
112
+ // Type
113
+ std::string strType = proxyServer.Get("type").As<Napi::String>().ToString();
114
+ rtc::ProxyServer::Type type = rtc::ProxyServer::Type::Http;
115
+
116
+ if (strType == "Socks5")
117
+ type = rtc::ProxyServer::Type::Socks5;
118
+
119
+ // Username & Password
120
+ std::string username = "";
121
+ std::string password = "";
122
+
123
+ if (proxyServer.Get("username").IsString())
124
+ username = proxyServer.Get("username").As<Napi::String>().ToString();
125
+ if (proxyServer.Get("password").IsString())
126
+ password = proxyServer.Get("password").As<Napi::String>().ToString();
127
+
128
+ webSocketConfig.proxyServer = rtc::ProxyServer(type, ip, port, username, password);
129
+ }
130
+
131
+ if (config.Has("protocols"))
132
+ {
133
+ if (!config.Get("protocols").IsArray())
134
+ {
135
+ Napi::TypeError::New(info.Env(), "protocols must be an array").ThrowAsJavaScriptException();
136
+ return;
137
+ }
138
+ Napi::Array protocols = config.Get("protocols").As<Napi::Array>();
139
+ for (uint32_t i = 0; i < protocols.Length(); i++)
140
+ {
141
+ webSocketConfig.protocols.push_back(protocols.Get(i).ToString());
142
+ }
143
+ }
144
+
145
+ if (config.Has("connectionTimeout"))
146
+ {
147
+ if (!config.Get("connectionTimeout").IsNumber())
148
+ {
149
+ Napi::TypeError::New(info.Env(), "connectionTimeout must be a number").ThrowAsJavaScriptException();
150
+ return;
151
+ }
152
+ webSocketConfig.connectionTimeout = std::chrono::milliseconds(config.Get("connectionTimeout").ToNumber().Int64Value());
153
+ }
154
+
155
+ if (config.Has("pingInterval"))
156
+ {
157
+ if (!config.Get("pingInterval").IsNumber())
158
+ {
159
+ Napi::TypeError::New(info.Env(), "pingInterval must be a number").ThrowAsJavaScriptException();
160
+ return;
161
+ }
162
+ webSocketConfig.pingInterval = std::chrono::milliseconds(config.Get("pingInterval").ToNumber().Int64Value());
163
+ }
164
+
165
+ if (config.Has("maxOutstandingPings"))
166
+ {
167
+ if (!config.Get("maxOutstandingPings").IsNumber())
168
+ {
169
+ Napi::TypeError::New(info.Env(), "maxOutstandingPings must be a number").ThrowAsJavaScriptException();
170
+ return;
171
+ }
172
+ webSocketConfig.maxOutstandingPings = config.Get("maxOutstandingPings").ToNumber().Int32Value();
173
+ }
174
+
175
+ if (config.Has("caCertificatePemFile"))
176
+ {
177
+ if (!config.Get("caCertificatePemFile").IsString())
178
+ {
179
+ Napi::TypeError::New(info.Env(), "caCertificatePemFile must be a string").ThrowAsJavaScriptException();
180
+ return;
181
+ }
182
+ webSocketConfig.caCertificatePemFile = config.Get("caCertificatePemFile").ToString();
183
+ }
184
+
185
+ if (config.Has("certificatePemFile"))
186
+ {
187
+ if (!config.Get("certificatePemFile").IsString())
188
+ {
189
+ Napi::TypeError::New(info.Env(), "certificatePemFile must be a string").ThrowAsJavaScriptException();
190
+ return;
191
+ }
192
+ webSocketConfig.certificatePemFile = config.Get("certificatePemFile").ToString();
193
+ }
194
+
195
+ if (config.Has("keyPemFile"))
196
+ {
197
+ if (!config.Get("keyPemFile").IsString())
198
+ {
199
+ Napi::TypeError::New(info.Env(), "keyPemFile must be a string").ThrowAsJavaScriptException();
200
+ return;
201
+ }
202
+ webSocketConfig.keyPemFile = config.Get("keyPemFile").ToString();
203
+ }
204
+
205
+ if (config.Has("keyPemPass"))
206
+ {
207
+ if (!config.Get("keyPemPass").IsString())
208
+ {
209
+ Napi::TypeError::New(info.Env(), "keyPemPass must be a string").ThrowAsJavaScriptException();
210
+ return;
211
+ }
212
+ webSocketConfig.keyPemPass = config.Get("keyPemPass").ToString();
213
+ }
214
+
215
+ if (config.Has("maxMessageSize"))
216
+ {
217
+ if (!config.Get("maxMessageSize").IsNumber())
218
+ {
219
+ Napi::TypeError::New(info.Env(), "maxMessageSize must be a number").ThrowAsJavaScriptException();
220
+ return;
221
+ }
222
+ webSocketConfig.maxMessageSize = config.Get("maxMessageSize").ToNumber().Int32Value();
223
+ }
224
+
225
+ // Create WebSocket
226
+ try
227
+ {
228
+ PLOG_DEBUG << "Creating a new WebSocket";
229
+ mWebSocketPtr = std::make_unique<rtc::WebSocket>(webSocketConfig);
230
+ }
231
+ catch (std::exception &ex)
232
+ {
233
+ Napi::Error::New(env, std::string("libdatachannel error while creating WebSocket: ") + ex.what()).ThrowAsJavaScriptException();
234
+ return;
235
+ }
236
+
237
+ PLOG_DEBUG << "WebSocket created";
238
+ instances.insert(this);
239
+ }
240
+
241
+ WebSocketWrapper::~WebSocketWrapper()
242
+ {
243
+ PLOG_DEBUG << "Destructor called";
244
+ doClose();
245
+ }
246
+
247
+ void WebSocketWrapper::doClose()
248
+ {
249
+ PLOG_DEBUG << "doClose() called";
250
+ if (mWebSocketPtr)
251
+ {
252
+ PLOG_DEBUG << "Closing...";
253
+ try
254
+ {
255
+ mWebSocketPtr->close();
256
+ mWebSocketPtr.reset();
257
+ }
258
+ catch (std::exception &ex)
259
+ {
260
+ std::cerr << std::string("libWebSocket error while closing WebSocket: ") + ex.what() << std::endl;
261
+ return;
262
+ }
263
+ }
264
+
265
+ mOnOpenCallback.reset();
266
+ mOnErrorCallback.reset();
267
+ mOnBufferedAmountLowCallback.reset();
268
+ mOnMessageCallback.reset();
269
+ }
270
+
271
+ void WebSocketWrapper::doCleanup()
272
+ {
273
+ PLOG_DEBUG << "doCleanup() called";
274
+ mOnClosedCallback.reset();
275
+ instances.erase(this);
276
+ }
277
+
278
+ void WebSocketWrapper::open(const Napi::CallbackInfo &info)
279
+ {
280
+ PLOG_DEBUG << "open() called";
281
+ Napi::Env env = info.Env();
282
+
283
+ if (!mWebSocketPtr)
284
+ {
285
+ Napi::Error::New(env, "open() called on destroyed WebSocket").ThrowAsJavaScriptException();
286
+ return;
287
+ }
288
+ if (info.Length() < 1 || !info[0].IsString())
289
+ {
290
+ Napi::TypeError::New(env, "url must be string").ThrowAsJavaScriptException();
291
+ return;
292
+ }
293
+
294
+ try
295
+ {
296
+ mWebSocketPtr->open(info[0].As<Napi::String>().ToString());
297
+ }
298
+ catch (std::exception &ex)
299
+ {
300
+ Napi::Error::New(env, std::string("libWebSocket error while opening WebSocket: ") + ex.what()).ThrowAsJavaScriptException();
301
+ return;
302
+ }
303
+ }
304
+
305
+ void WebSocketWrapper::close(const Napi::CallbackInfo &info)
306
+ {
307
+ PLOG_DEBUG << "close() called";
308
+ doClose();
309
+ }
310
+
311
+ Napi::Value WebSocketWrapper::sendMessage(const Napi::CallbackInfo &info)
312
+ {
313
+ PLOG_DEBUG << "sendMessage() called";
314
+ if (!mWebSocketPtr)
315
+ {
316
+ Napi::Error::New(info.Env(), "sendMessage() called on destroyed channel").ThrowAsJavaScriptException();
317
+ return info.Env().Null();
318
+ }
319
+
320
+ Napi::Env env = info.Env();
321
+ int length = info.Length();
322
+
323
+ // Allow call with NULL
324
+ if (length < 1 || (!info[0].IsString() && !info[0].IsNull()))
325
+ {
326
+ Napi::TypeError::New(env, "String or Null expected").ThrowAsJavaScriptException();
327
+ return info.Env().Null();
328
+ }
329
+
330
+ try
331
+ {
332
+ return Napi::Boolean::New(info.Env(), mWebSocketPtr->send(info[0].As<Napi::String>().ToString()));
333
+ }
334
+ catch (std::exception &ex)
335
+ {
336
+ Napi::Error::New(env, std::string("libWebSocket error while sending data channel message: ") + ex.what()).ThrowAsJavaScriptException();
337
+ return Napi::Boolean::New(info.Env(), false);
338
+ }
339
+ }
340
+
341
+ Napi::Value WebSocketWrapper::sendMessageBinary(const Napi::CallbackInfo &info)
342
+ {
343
+ PLOG_DEBUG << "sendMessageBinary() called";
344
+ if (!mWebSocketPtr)
345
+ {
346
+ Napi::Error::New(info.Env(), "sendMessagBinary() called on destroyed channel").ThrowAsJavaScriptException();
347
+ return info.Env().Null();
348
+ }
349
+
350
+ Napi::Env env = info.Env();
351
+ int length = info.Length();
352
+
353
+ if (length < 1 || !info[0].IsBuffer())
354
+ {
355
+ Napi::TypeError::New(env, "Buffer expected").ThrowAsJavaScriptException();
356
+ return info.Env().Null();
357
+ }
358
+
359
+ try
360
+ {
361
+ Napi::Uint8Array buffer = info[0].As<Napi::Uint8Array>();
362
+ return Napi::Boolean::New(info.Env(), mWebSocketPtr->send((std::byte *)buffer.Data(), buffer.ByteLength()));
363
+ }
364
+ catch (std::exception &ex)
365
+ {
366
+ Napi::Error::New(env, std::string("libWebSocket error while sending data channel message: ") + ex.what()).ThrowAsJavaScriptException();
367
+ return Napi::Boolean::New(info.Env(), false);
368
+ }
369
+ }
370
+
371
+ Napi::Value WebSocketWrapper::isOpen(const Napi::CallbackInfo &info)
372
+ {
373
+ PLOG_DEBUG << "isOpen() called";
374
+ Napi::Env env = info.Env();
375
+
376
+ if (!mWebSocketPtr)
377
+ {
378
+ return Napi::Boolean::New(info.Env(), false);
379
+ }
380
+
381
+ try
382
+ {
383
+ return Napi::Boolean::New(info.Env(), mWebSocketPtr->isOpen());
384
+ }
385
+ catch (std::exception &ex)
386
+ {
387
+ Napi::Error::New(env, std::string("libWebSocket error: ") + ex.what()).ThrowAsJavaScriptException();
388
+ return Napi::Boolean::New(info.Env(), false);
389
+ }
390
+ }
391
+
392
+ Napi::Value WebSocketWrapper::bufferedAmount(const Napi::CallbackInfo &info)
393
+ {
394
+ PLOG_DEBUG << "bufferedAmount() called";
395
+ Napi::Env env = info.Env();
396
+
397
+ if (!mWebSocketPtr)
398
+ {
399
+ return Napi::Number::New(info.Env(), 0);
400
+ }
401
+
402
+ try
403
+ {
404
+ return Napi::Number::New(info.Env(), mWebSocketPtr->bufferedAmount());
405
+ }
406
+ catch (std::exception &ex)
407
+ {
408
+ Napi::Error::New(env, std::string("libWebSocket error: ") + ex.what()).ThrowAsJavaScriptException();
409
+ return Napi::Number::New(info.Env(), 0);
410
+ }
411
+ }
412
+
413
+ Napi::Value WebSocketWrapper::maxMessageSize(const Napi::CallbackInfo &info)
414
+ {
415
+ PLOG_DEBUG << "maxMessageSize() called";
416
+ Napi::Env env = info.Env();
417
+
418
+ if (!mWebSocketPtr)
419
+ {
420
+ return Napi::Number::New(info.Env(), 0);
421
+ }
422
+
423
+ try
424
+ {
425
+ return Napi::Number::New(info.Env(), mWebSocketPtr->maxMessageSize());
426
+ }
427
+ catch (std::exception &ex)
428
+ {
429
+ Napi::Error::New(env, std::string("libWebSocket error: ") + ex.what()).ThrowAsJavaScriptException();
430
+ return Napi::Number::New(info.Env(), 0);
431
+ }
432
+ }
433
+
434
+ void WebSocketWrapper::setBufferedAmountLowThreshold(const Napi::CallbackInfo &info)
435
+ {
436
+ PLOG_DEBUG << "setBufferedAmountLowThreshold() called";
437
+ if (!mWebSocketPtr)
438
+ {
439
+ Napi::Error::New(info.Env(), "setBufferedAmountLowThreshold() called on destroyed channel").ThrowAsJavaScriptException();
440
+ return;
441
+ }
442
+
443
+ Napi::Env env = info.Env();
444
+ int length = info.Length();
445
+
446
+ if (length < 1 || !info[0].IsNumber())
447
+ {
448
+ Napi::TypeError::New(env, "Number expected").ThrowAsJavaScriptException();
449
+ return;
450
+ }
451
+
452
+ try
453
+ {
454
+ mWebSocketPtr->setBufferedAmountLowThreshold(info[0].ToNumber().Uint32Value());
455
+ }
456
+ catch (std::exception &ex)
457
+ {
458
+ Napi::Error::New(env, std::string("libWebSocket error: ") + ex.what()).ThrowAsJavaScriptException();
459
+ return;
460
+ }
461
+ }
462
+
463
+ void WebSocketWrapper::onOpen(const Napi::CallbackInfo &info)
464
+ {
465
+ PLOG_DEBUG << "new onOpen() called";
466
+ if (!mWebSocketPtr)
467
+ {
468
+ Napi::Error::New(info.Env(), "onOpen() called on destroyed channel").ThrowAsJavaScriptException();
469
+ return;
470
+ }
471
+
472
+ Napi::Env env = info.Env();
473
+ int length = info.Length();
474
+
475
+ if (length < 1 || !info[0].IsFunction())
476
+ {
477
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
478
+ return;
479
+ }
480
+
481
+ // Callback
482
+ mOnOpenCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
483
+
484
+ mWebSocketPtr->onOpen([&]()
485
+ {
486
+ PLOG_DEBUG << "onOpen cb received from rtc";
487
+
488
+ if (mOnOpenCallback)
489
+ mOnOpenCallback->call([this](Napi::Env env, std::vector<napi_value> &args)
490
+ {
491
+ PLOG_DEBUG << "mOnOpenCallback call(1)";
492
+ // Check the WebSocket is not closed
493
+ if(instances.find(this) == instances.end())
494
+ {
495
+ PLOG_DEBUG << "WebSocket not found in instances";
496
+ throw ThreadSafeCallback::CancelException();
497
+ }
498
+
499
+
500
+ // This will run in main thread and needs to construct the
501
+ // arguments for the call
502
+ args = {};
503
+ PLOG_DEBUG << "mOnOpenCallback call(2)"; }); });
504
+ }
505
+
506
+ void WebSocketWrapper::onClosed(const Napi::CallbackInfo &info)
507
+ {
508
+ PLOG_DEBUG << "onClosed() called";
509
+ if (!mWebSocketPtr)
510
+ {
511
+ Napi::Error::New(info.Env(), "onClosed() called on destroyed WebSocket").ThrowAsJavaScriptException();
512
+ return;
513
+ }
514
+
515
+ Napi::Env env = info.Env();
516
+ int length = info.Length();
517
+
518
+ if (length < 1 || !info[0].IsFunction())
519
+ {
520
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
521
+ return;
522
+ }
523
+
524
+ // Callback
525
+ mOnClosedCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
526
+
527
+ mWebSocketPtr->onClosed([&]()
528
+ {
529
+ PLOG_DEBUG << "onClosed cb received from rtc";
530
+ if (mOnClosedCallback)
531
+ mOnClosedCallback->call([this](Napi::Env env, std::vector<napi_value> &args) {
532
+ PLOG_DEBUG << "mOnClosedCallback call";
533
+ // Do not check if the data channel has been closed here
534
+
535
+ // This will run in main thread and needs to construct the
536
+ // arguments for the call
537
+ args = {};
538
+ },[this]{
539
+ doCleanup();
540
+ }); });
541
+ }
542
+
543
+ void WebSocketWrapper::onError(const Napi::CallbackInfo &info)
544
+ {
545
+ PLOG_DEBUG << "onError() called";
546
+ if (!mWebSocketPtr)
547
+ {
548
+ Napi::Error::New(info.Env(), "onError() called on destroyed channel").ThrowAsJavaScriptException();
549
+ return;
550
+ }
551
+
552
+ Napi::Env env = info.Env();
553
+ int length = info.Length();
554
+
555
+ if (length < 1 || !info[0].IsFunction())
556
+ {
557
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
558
+ return;
559
+ }
560
+
561
+ // Callback
562
+ mOnErrorCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
563
+
564
+ mWebSocketPtr->onError([&](std::string error)
565
+ {
566
+ PLOG_DEBUG << "onError cb received from rtc";
567
+ if (mOnErrorCallback)
568
+ mOnErrorCallback->call([this, error = std::move(error)](Napi::Env env, std::vector<napi_value> &args) {
569
+ PLOG_DEBUG << "mOnErrorCallback call(1)";
570
+ // Check the data channel is not closed
571
+ if(instances.find(this) == instances.end())
572
+ throw ThreadSafeCallback::CancelException();
573
+
574
+ // This will run in main thread and needs to construct the
575
+ // arguments for the call
576
+ args = {Napi::String::New(env, error)};
577
+ PLOG_DEBUG << "mOnErrorCallback call(2)";
578
+ }); });
579
+ }
580
+
581
+ void WebSocketWrapper::onBufferedAmountLow(const Napi::CallbackInfo &info)
582
+ {
583
+ PLOG_DEBUG << "onBufferedAmountLow() called";
584
+ if (!mWebSocketPtr)
585
+ {
586
+ Napi::Error::New(info.Env(), "onBufferedAmountLow() called on destroyed channel").ThrowAsJavaScriptException();
587
+ return;
588
+ }
589
+
590
+ Napi::Env env = info.Env();
591
+ int length = info.Length();
592
+
593
+ if (length < 1 || !info[0].IsFunction())
594
+ {
595
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
596
+ return;
597
+ }
598
+
599
+ // Callback
600
+ mOnBufferedAmountLowCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
601
+
602
+ mWebSocketPtr->onBufferedAmountLow([&]()
603
+ {
604
+ PLOG_DEBUG << "onBufferedAmountLow cb received from rtc";
605
+ if (mOnBufferedAmountLowCallback)
606
+ mOnBufferedAmountLowCallback->call([this](Napi::Env env, std::vector<napi_value> &args) {
607
+ PLOG_DEBUG << "mOnBufferedAmountLowCallback call(1)";
608
+ // Check the data channel is not closed
609
+ if(instances.find(this) == instances.end())
610
+ throw ThreadSafeCallback::CancelException();
611
+
612
+ // This will run in main thread and needs to construct the
613
+ // arguments for the call
614
+ args = {};
615
+ PLOG_DEBUG << "mOnBufferedAmountLowCallback call(2)";
616
+ }); });
617
+ }
618
+
619
+ void WebSocketWrapper::onMessage(const Napi::CallbackInfo &info)
620
+ {
621
+ PLOG_DEBUG << "onMessage() called";
622
+ if (!mWebSocketPtr)
623
+ {
624
+ Napi::Error::New(info.Env(), "onMessage() called on destroyed channel").ThrowAsJavaScriptException();
625
+ return;
626
+ }
627
+
628
+ Napi::Env env = info.Env();
629
+ int length = info.Length();
630
+
631
+ if (length < 1 || !info[0].IsFunction())
632
+ {
633
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
634
+ return;
635
+ }
636
+
637
+ // Callback
638
+ mOnMessageCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
639
+
640
+ PLOG_DEBUG << "setting onMessage cb on mWebSocketPtr";
641
+ mWebSocketPtr->onMessage([&](std::variant<rtc::binary, std::string> message)
642
+ {
643
+ PLOG_DEBUG << "onMessage cb received from rtc";
644
+ if (mOnMessageCallback)
645
+ mOnMessageCallback->call([this, message = std::move(message)](Napi::Env env, std::vector<napi_value> &args) {
646
+ PLOG_DEBUG << "mOnMessageCallback call(1)";
647
+ // Check the data channel is not closed
648
+ if(instances.find(this) == instances.end())
649
+ throw ThreadSafeCallback::CancelException();
650
+
651
+ // This will run in main thread and needs to construct the
652
+ // arguments for the call
653
+ if (std::holds_alternative<std::string>(message))
654
+ {
655
+ args = {Napi::String::New(env, std::get<std::string>(message))};
656
+ }
657
+ else
658
+ {
659
+ auto bin = std::get<rtc::binary>(std::move(message));
660
+ args = {Napi::Buffer<std::byte>::Copy(env, bin.data(), bin.size())};
661
+ }
662
+ PLOG_DEBUG << "mOnMessageCallback call(2)";
663
+ }); });
664
+ }