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.
- package/.github/workflows/build-linux.yml +2 -2
- package/CMakeLists.txt +6 -10
- package/examples/electron-demo/package-lock.json +6 -6
- package/examples/media/README.md +12 -0
- package/jest.config.cjs +7 -0
- package/lib/index.cjs +142 -0
- package/lib/index.d.ts +66 -2
- package/lib/index.js +4 -0
- package/package.json +15 -6
- package/polyfill/RTCPeerConnection.js +0 -1
- package/polyfill/polyfill.cjs +1069 -0
- package/src/data-channel-wrapper.cpp +15 -1
- package/src/data-channel-wrapper.h +5 -1
- package/src/main.cpp +4 -0
- package/src/media-track-wrapper.cpp +25 -5
- package/src/media-track-wrapper.h +7 -3
- package/src/peer-connection-wrapper.cpp +21 -27
- package/src/peer-connection-wrapper.h +2 -5
- package/src/rtc-wrapper.cpp +9 -2
- package/src/thread-safe-callback.cpp +6 -2
- package/src/thread-safe-callback.h +3 -1
- package/src/web-socket-server-wrapper.cpp +265 -0
- package/src/web-socket-server-wrapper.h +43 -0
- package/src/web-socket-wrapper.cpp +664 -0
- package/src/web-socket-wrapper.h +62 -0
- package/test/jest-tests/basic.test.js +37 -0
- package/test/jest-tests/multiple-run.test.js +73 -0
- package/test/jest-tests/p2p.test.js +99 -0
- package/test/jest-tests/streams.test.js +48 -0
- package/test/jest-tests/websocket.test.js +69 -0
- package/test/websockets.js +48 -0
- package/test/test.js +0 -185
|
@@ -13,6 +13,14 @@ void DataChannelWrapper::CloseAll()
|
|
|
13
13
|
inst->doClose();
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
void DataChannelWrapper::CleanupAll()
|
|
17
|
+
{
|
|
18
|
+
PLOG_DEBUG << "CleanupAll() called";
|
|
19
|
+
auto copy(instances);
|
|
20
|
+
for (auto inst : copy)
|
|
21
|
+
inst->doCleanup();
|
|
22
|
+
}
|
|
23
|
+
|
|
16
24
|
Napi::Object DataChannelWrapper::Init(Napi::Env env, Napi::Object exports)
|
|
17
25
|
{
|
|
18
26
|
Napi::HandleScope scope(env);
|
|
@@ -78,11 +86,15 @@ void DataChannelWrapper::doClose()
|
|
|
78
86
|
}
|
|
79
87
|
|
|
80
88
|
mOnOpenCallback.reset();
|
|
81
|
-
mOnClosedCallback.reset();
|
|
82
89
|
mOnErrorCallback.reset();
|
|
83
90
|
mOnBufferedAmountLowCallback.reset();
|
|
84
91
|
mOnMessageCallback.reset();
|
|
92
|
+
}
|
|
85
93
|
|
|
94
|
+
void DataChannelWrapper::doCleanup()
|
|
95
|
+
{
|
|
96
|
+
PLOG_DEBUG << "doCleanup() called";
|
|
97
|
+
mOnClosedCallback.reset();
|
|
86
98
|
instances.erase(this);
|
|
87
99
|
}
|
|
88
100
|
|
|
@@ -350,6 +362,8 @@ void DataChannelWrapper::onClosed(const Napi::CallbackInfo &info)
|
|
|
350
362
|
// This will run in main thread and needs to construct the
|
|
351
363
|
// arguments for the call
|
|
352
364
|
args = {};
|
|
365
|
+
},[this]{
|
|
366
|
+
doCleanup();
|
|
353
367
|
}); });
|
|
354
368
|
}
|
|
355
369
|
|
|
@@ -42,10 +42,14 @@ public:
|
|
|
42
42
|
// Close all existing DataChannels
|
|
43
43
|
static void CloseAll();
|
|
44
44
|
|
|
45
|
+
// Reset all Callbacks for existing DataChannels
|
|
46
|
+
static void CleanupAll();
|
|
47
|
+
|
|
45
48
|
private:
|
|
46
|
-
static std::unordered_set<DataChannelWrapper*> instances;
|
|
49
|
+
static std::unordered_set<DataChannelWrapper *> instances;
|
|
47
50
|
|
|
48
51
|
void doClose();
|
|
52
|
+
void doCleanup();
|
|
49
53
|
|
|
50
54
|
std::string mLabel;
|
|
51
55
|
std::shared_ptr<rtc::DataChannel> mDataChannelPtr = nullptr;
|
package/src/main.cpp
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
#include "media-track-wrapper.h"
|
|
7
7
|
#include "media-video-wrapper.h"
|
|
8
8
|
#include "media-audio-wrapper.h"
|
|
9
|
+
#include "web-socket-wrapper.h"
|
|
10
|
+
#include "web-socket-server-wrapper.h"
|
|
9
11
|
|
|
10
12
|
Napi::Object InitAll(Napi::Env env, Napi::Object exports)
|
|
11
13
|
{
|
|
@@ -16,6 +18,8 @@ Napi::Object InitAll(Napi::Env env, Napi::Object exports)
|
|
|
16
18
|
AudioWrapper::Init(env, exports);
|
|
17
19
|
DataChannelWrapper::Init(env, exports);
|
|
18
20
|
PeerConnectionWrapper::Init(env, exports);
|
|
21
|
+
WebSocketWrapper::Init(env, exports);
|
|
22
|
+
WebSocketServerWrapper::Init(env, exports);
|
|
19
23
|
return exports;
|
|
20
24
|
}
|
|
21
25
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
#include "media-direction.h"
|
|
3
3
|
#include "media-rtcpreceivingsession-wrapper.h"
|
|
4
4
|
|
|
5
|
+
#include "plog/Log.h"
|
|
6
|
+
|
|
5
7
|
Napi::FunctionReference TrackWrapper::constructor;
|
|
6
8
|
std::unordered_set<TrackWrapper *> TrackWrapper::instances;
|
|
7
9
|
|
|
@@ -12,6 +14,14 @@ void TrackWrapper::CloseAll()
|
|
|
12
14
|
inst->doClose();
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
void TrackWrapper::CleanupAll()
|
|
18
|
+
{
|
|
19
|
+
PLOG_DEBUG << "CleanupAll() called";
|
|
20
|
+
auto copy(instances);
|
|
21
|
+
for (auto inst : copy)
|
|
22
|
+
inst->doCleanup();
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
Napi::Object TrackWrapper::Init(Napi::Env env, Napi::Object exports)
|
|
16
26
|
{
|
|
17
27
|
Napi::HandleScope scope(env);
|
|
@@ -73,10 +83,14 @@ void TrackWrapper::doClose()
|
|
|
73
83
|
}
|
|
74
84
|
|
|
75
85
|
mOnOpenCallback.reset();
|
|
76
|
-
mOnClosedCallback.reset();
|
|
77
86
|
mOnErrorCallback.reset();
|
|
78
87
|
mOnMessageCallback.reset();
|
|
88
|
+
}
|
|
79
89
|
|
|
90
|
+
void TrackWrapper::doCleanup()
|
|
91
|
+
{
|
|
92
|
+
PLOG_DEBUG << "doCleanup() called";
|
|
93
|
+
mOnClosedCallback.reset();
|
|
80
94
|
instances.erase(this);
|
|
81
95
|
}
|
|
82
96
|
|
|
@@ -276,7 +290,8 @@ void TrackWrapper::onOpen(const Napi::CallbackInfo &info)
|
|
|
276
290
|
// Callback
|
|
277
291
|
mOnOpenCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
|
|
278
292
|
|
|
279
|
-
mTrackPtr->onOpen([&]()
|
|
293
|
+
mTrackPtr->onOpen([&]()
|
|
294
|
+
{
|
|
280
295
|
if (mOnOpenCallback)
|
|
281
296
|
mOnOpenCallback->call([this](Napi::Env env, std::vector<napi_value> &args) {
|
|
282
297
|
// Check the track is not closed
|
|
@@ -309,7 +324,8 @@ void TrackWrapper::onClosed(const Napi::CallbackInfo &info)
|
|
|
309
324
|
// Callback
|
|
310
325
|
mOnClosedCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
|
|
311
326
|
|
|
312
|
-
mTrackPtr->onClosed([&]()
|
|
327
|
+
mTrackPtr->onClosed([&]()
|
|
328
|
+
{
|
|
313
329
|
if (mOnClosedCallback)
|
|
314
330
|
mOnClosedCallback->call([this](Napi::Env env, std::vector<napi_value> &args) {
|
|
315
331
|
// Do not check if the data channel has been closed here
|
|
@@ -317,6 +333,8 @@ void TrackWrapper::onClosed(const Napi::CallbackInfo &info)
|
|
|
317
333
|
// This will run in main thread and needs to construct the
|
|
318
334
|
// arguments for the call
|
|
319
335
|
args = {};
|
|
336
|
+
},[this]{
|
|
337
|
+
doCleanup();
|
|
320
338
|
}); });
|
|
321
339
|
}
|
|
322
340
|
|
|
@@ -340,7 +358,8 @@ void TrackWrapper::onError(const Napi::CallbackInfo &info)
|
|
|
340
358
|
// Callback
|
|
341
359
|
mOnErrorCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
|
|
342
360
|
|
|
343
|
-
mTrackPtr->onError([&](const std::string &error)
|
|
361
|
+
mTrackPtr->onError([&](const std::string &error)
|
|
362
|
+
{
|
|
344
363
|
if (mOnErrorCallback)
|
|
345
364
|
mOnErrorCallback->call([this, error](Napi::Env env, std::vector<napi_value> &args) {
|
|
346
365
|
// Check the track is not closed
|
|
@@ -373,7 +392,8 @@ void TrackWrapper::onMessage(const Napi::CallbackInfo &info)
|
|
|
373
392
|
// Callback
|
|
374
393
|
mOnMessageCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
|
|
375
394
|
|
|
376
|
-
mTrackPtr->onMessage([&](std::variant<rtc::binary, std::string> message)
|
|
395
|
+
mTrackPtr->onMessage([&](std::variant<rtc::binary, std::string> message)
|
|
396
|
+
{
|
|
377
397
|
if (mOnMessageCallback)
|
|
378
398
|
mOnMessageCallback->call([this, message = std::move(message)](Napi::Env env, std::vector<napi_value> &args) {
|
|
379
399
|
// Check the track is not closed
|
|
@@ -39,21 +39,25 @@ public:
|
|
|
39
39
|
void onError(const Napi::CallbackInfo &info);
|
|
40
40
|
void onMessage(const Napi::CallbackInfo &info);
|
|
41
41
|
|
|
42
|
-
// Close all existing
|
|
42
|
+
// Close all existing tracks
|
|
43
43
|
static void CloseAll();
|
|
44
44
|
|
|
45
|
+
// Reset all Callbacks for existing tracks
|
|
46
|
+
static void CleanupAll();
|
|
47
|
+
|
|
45
48
|
private:
|
|
46
49
|
static std::unordered_set<TrackWrapper *> instances;
|
|
47
50
|
|
|
48
51
|
void doClose();
|
|
52
|
+
void doCleanup();
|
|
49
53
|
|
|
50
54
|
std::shared_ptr<rtc::Track> mTrackPtr = nullptr;
|
|
51
55
|
|
|
52
|
-
|
|
56
|
+
// Callback Ptrs
|
|
53
57
|
std::unique_ptr<ThreadSafeCallback> mOnOpenCallback = nullptr;
|
|
54
58
|
std::unique_ptr<ThreadSafeCallback> mOnClosedCallback = nullptr;
|
|
55
59
|
std::unique_ptr<ThreadSafeCallback> mOnErrorCallback = nullptr;
|
|
56
60
|
std::unique_ptr<ThreadSafeCallback> mOnMessageCallback = nullptr;
|
|
57
61
|
};
|
|
58
62
|
|
|
59
|
-
#endif // MEDIA_TRACK_WRAPPER_H
|
|
63
|
+
#endif // MEDIA_TRACK_WRAPPER_H
|
|
@@ -20,12 +20,12 @@ void PeerConnectionWrapper::CloseAll()
|
|
|
20
20
|
inst->doClose();
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
void PeerConnectionWrapper::
|
|
23
|
+
void PeerConnectionWrapper::CleanupAll()
|
|
24
24
|
{
|
|
25
|
-
PLOG_DEBUG << "
|
|
25
|
+
PLOG_DEBUG << "CleanupAll() called";
|
|
26
26
|
auto copy(instances);
|
|
27
27
|
for (auto inst : copy)
|
|
28
|
-
inst->
|
|
28
|
+
inst->doCleanup();
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
Napi::Object PeerConnectionWrapper::Init(Napi::Env env, Napi::Object exports)
|
|
@@ -37,7 +37,6 @@ Napi::Object PeerConnectionWrapper::Init(Napi::Env env, Napi::Object exports)
|
|
|
37
37
|
"PeerConnection",
|
|
38
38
|
{
|
|
39
39
|
InstanceMethod("close", &PeerConnectionWrapper::close),
|
|
40
|
-
InstanceMethod("destroy", &PeerConnectionWrapper::destroy),
|
|
41
40
|
InstanceMethod("setLocalDescription", &PeerConnectionWrapper::setLocalDescription),
|
|
42
41
|
InstanceMethod("setRemoteDescription", &PeerConnectionWrapper::setRemoteDescription),
|
|
43
42
|
InstanceMethod("localDescription", &PeerConnectionWrapper::localDescription),
|
|
@@ -253,7 +252,8 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N
|
|
|
253
252
|
PeerConnectionWrapper::~PeerConnectionWrapper()
|
|
254
253
|
{
|
|
255
254
|
PLOG_DEBUG << "Destructor called";
|
|
256
|
-
|
|
255
|
+
doCleanup();
|
|
256
|
+
doClose();
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
void PeerConnectionWrapper::doClose()
|
|
@@ -273,40 +273,27 @@ void PeerConnectionWrapper::doClose()
|
|
|
273
273
|
return;
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
void PeerConnectionWrapper::close(const Napi::CallbackInfo &info)
|
|
279
|
-
{
|
|
280
|
-
PLOG_DEBUG << "close() called";
|
|
281
|
-
doClose();
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
void PeerConnectionWrapper::doDestroy()
|
|
285
|
-
{
|
|
286
|
-
PLOG_DEBUG << "doDestroy() called";
|
|
287
|
-
doClose();
|
|
288
|
-
doResetCallbacks();
|
|
289
|
-
}
|
|
290
276
|
|
|
291
|
-
void PeerConnectionWrapper::doResetCallbacks()
|
|
292
|
-
{
|
|
293
|
-
PLOG_DEBUG << "doResetCallbacks() called";
|
|
294
277
|
mOnLocalDescriptionCallback.reset();
|
|
295
278
|
mOnLocalCandidateCallback.reset();
|
|
296
|
-
mOnStateChangeCallback.reset();
|
|
297
279
|
mOnIceStateChangeCallback.reset();
|
|
298
280
|
mOnSignalingStateChangeCallback.reset();
|
|
299
281
|
mOnGatheringStateChangeCallback.reset();
|
|
300
282
|
mOnDataChannelCallback.reset();
|
|
301
283
|
mOnTrackCallback.reset();
|
|
284
|
+
}
|
|
302
285
|
|
|
303
|
-
|
|
286
|
+
void PeerConnectionWrapper::close(const Napi::CallbackInfo &info)
|
|
287
|
+
{
|
|
288
|
+
PLOG_DEBUG << "close() called";
|
|
289
|
+
doClose();
|
|
304
290
|
}
|
|
305
291
|
|
|
306
|
-
void PeerConnectionWrapper::
|
|
292
|
+
void PeerConnectionWrapper::doCleanup()
|
|
307
293
|
{
|
|
308
|
-
PLOG_DEBUG << "
|
|
309
|
-
|
|
294
|
+
PLOG_DEBUG << "doCleanup() called";
|
|
295
|
+
mOnStateChangeCallback.reset();
|
|
296
|
+
instances.erase(this);
|
|
310
297
|
}
|
|
311
298
|
|
|
312
299
|
void PeerConnectionWrapper::setLocalDescription(const Napi::CallbackInfo &info)
|
|
@@ -738,6 +725,13 @@ void PeerConnectionWrapper::onStateChange(const Napi::CallbackInfo &info)
|
|
|
738
725
|
stream << state;
|
|
739
726
|
args = {Napi::String::New(env, stream.str())};
|
|
740
727
|
PLOG_DEBUG << "mOnStateChangeCallback call(2)";
|
|
728
|
+
},[this,state](){
|
|
729
|
+
PLOG_DEBUG << "mOnStateChangeCallback cleanup";
|
|
730
|
+
// Special case for closed state, we need to reset all callbacks
|
|
731
|
+
if(state == rtc::PeerConnection::State::Closed)
|
|
732
|
+
{
|
|
733
|
+
doCleanup();
|
|
734
|
+
}
|
|
741
735
|
}); });
|
|
742
736
|
}
|
|
743
737
|
|
|
@@ -19,8 +19,6 @@ public:
|
|
|
19
19
|
PeerConnectionWrapper(const Napi::CallbackInfo &info);
|
|
20
20
|
~PeerConnectionWrapper();
|
|
21
21
|
|
|
22
|
-
void destroy(const Napi::CallbackInfo &info);
|
|
23
|
-
|
|
24
22
|
// Functions
|
|
25
23
|
void close(const Napi::CallbackInfo &info);
|
|
26
24
|
void setLocalDescription(const Napi::CallbackInfo &info);
|
|
@@ -58,15 +56,14 @@ public:
|
|
|
58
56
|
static void CloseAll();
|
|
59
57
|
|
|
60
58
|
// Reset all Callbacks for existing Peer Connections
|
|
61
|
-
static void
|
|
59
|
+
static void CleanupAll();
|
|
62
60
|
|
|
63
61
|
private:
|
|
64
62
|
static Napi::FunctionReference constructor;
|
|
65
63
|
static std::unordered_set<PeerConnectionWrapper *> instances;
|
|
66
64
|
|
|
67
65
|
void doClose();
|
|
68
|
-
void
|
|
69
|
-
void doResetCallbacks();
|
|
66
|
+
void doCleanup();
|
|
70
67
|
|
|
71
68
|
std::string mPeerName;
|
|
72
69
|
std::unique_ptr<rtc::PeerConnection> mRtcPeerConnPtr = nullptr;
|
package/src/rtc-wrapper.cpp
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
#include "peer-connection-wrapper.h"
|
|
3
3
|
#include "data-channel-wrapper.h"
|
|
4
4
|
#include "media-track-wrapper.h"
|
|
5
|
+
#include "web-socket-wrapper.h"
|
|
6
|
+
#include "web-socket-server-wrapper.h"
|
|
5
7
|
|
|
6
8
|
#include "plog/Log.h"
|
|
7
9
|
|
|
@@ -116,13 +118,18 @@ void RtcWrapper::cleanup(const Napi::CallbackInfo &info)
|
|
|
116
118
|
PeerConnectionWrapper::CloseAll();
|
|
117
119
|
DataChannelWrapper::CloseAll();
|
|
118
120
|
TrackWrapper::CloseAll();
|
|
121
|
+
WebSocketWrapper::CloseAll();
|
|
122
|
+
WebSocketServerWrapper::StopAll();
|
|
119
123
|
|
|
120
124
|
const auto timeout = std::chrono::seconds(10);
|
|
121
125
|
if (rtc::Cleanup().wait_for(std::chrono::seconds(timeout)) == std::future_status::timeout)
|
|
122
126
|
throw std::runtime_error("cleanup timeout (possible deadlock)");
|
|
123
127
|
|
|
124
|
-
//
|
|
125
|
-
PeerConnectionWrapper::
|
|
128
|
+
// Cleanup the instances
|
|
129
|
+
PeerConnectionWrapper::CleanupAll();
|
|
130
|
+
DataChannelWrapper::CleanupAll();
|
|
131
|
+
TrackWrapper::CleanupAll();
|
|
132
|
+
WebSocketWrapper::CleanupAll();
|
|
126
133
|
|
|
127
134
|
if (logCallback)
|
|
128
135
|
logCallback.reset();
|
|
@@ -26,9 +26,9 @@ ThreadSafeCallback::~ThreadSafeCallback()
|
|
|
26
26
|
tsfn.Abort();
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
void ThreadSafeCallback::call(arg_func_t argFunc)
|
|
29
|
+
void ThreadSafeCallback::call(arg_func_t argFunc, cleanup_func_t cleanupFunc)
|
|
30
30
|
{
|
|
31
|
-
CallbackData *data = new CallbackData{std::move(argFunc)};
|
|
31
|
+
CallbackData *data = new CallbackData{std::move(argFunc), std::move(cleanupFunc)};
|
|
32
32
|
if (tsfn.BlockingCall(data) != napi_ok)
|
|
33
33
|
{
|
|
34
34
|
delete data;
|
|
@@ -47,6 +47,7 @@ void ThreadSafeCallback::callbackFunc(Napi::Env env,
|
|
|
47
47
|
|
|
48
48
|
arg_vector_t args;
|
|
49
49
|
arg_func_t argFunc(std::move(data->argFunc));
|
|
50
|
+
cleanup_func_t cleanup(std::move(data->cleanupFunc));
|
|
50
51
|
delete data;
|
|
51
52
|
|
|
52
53
|
try
|
|
@@ -59,5 +60,8 @@ void ThreadSafeCallback::callbackFunc(Napi::Env env,
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
if (env && callback)
|
|
63
|
+
{
|
|
62
64
|
callback.Call(context->Value(), args);
|
|
65
|
+
cleanup();
|
|
66
|
+
}
|
|
63
67
|
}
|
|
@@ -11,6 +11,7 @@ class ThreadSafeCallback
|
|
|
11
11
|
public:
|
|
12
12
|
using arg_vector_t = std::vector<napi_value>;
|
|
13
13
|
using arg_func_t = std::function<void(napi_env, arg_vector_t &)>;
|
|
14
|
+
using cleanup_func_t = std::function<void()>;
|
|
14
15
|
|
|
15
16
|
ThreadSafeCallback(Napi::Function callback);
|
|
16
17
|
~ThreadSafeCallback();
|
|
@@ -21,7 +22,7 @@ public:
|
|
|
21
22
|
ThreadSafeCallback &operator=(const ThreadSafeCallback &) = delete;
|
|
22
23
|
ThreadSafeCallback &operator=(ThreadSafeCallback &&) = delete;
|
|
23
24
|
|
|
24
|
-
void call(arg_func_t argFunc);
|
|
25
|
+
void call(arg_func_t argFunc, cleanup_func_t cleanupFunc = []() {});
|
|
25
26
|
|
|
26
27
|
class CancelException : public std::exception
|
|
27
28
|
{
|
|
@@ -32,6 +33,7 @@ private:
|
|
|
32
33
|
struct CallbackData
|
|
33
34
|
{
|
|
34
35
|
arg_func_t argFunc;
|
|
36
|
+
cleanup_func_t cleanupFunc;
|
|
35
37
|
};
|
|
36
38
|
|
|
37
39
|
static void callbackFunc(Napi::Env env,
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
#include "web-socket-server-wrapper.h"
|
|
2
|
+
|
|
3
|
+
#include "plog/Log.h"
|
|
4
|
+
|
|
5
|
+
void WebSocketServerWrapper::StopAll()
|
|
6
|
+
{
|
|
7
|
+
PLOG_DEBUG << "StopAll() called";
|
|
8
|
+
auto copy(instances);
|
|
9
|
+
for (auto inst : copy)
|
|
10
|
+
inst->doStop();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
Napi::FunctionReference WebSocketServerWrapper::constructor;
|
|
14
|
+
std::unordered_set<WebSocketServerWrapper *> WebSocketServerWrapper::instances;
|
|
15
|
+
|
|
16
|
+
Napi::Object WebSocketServerWrapper::Init(Napi::Env env, Napi::Object exports)
|
|
17
|
+
{
|
|
18
|
+
Napi::HandleScope scope(env);
|
|
19
|
+
|
|
20
|
+
Napi::Function func = DefineClass(
|
|
21
|
+
env,
|
|
22
|
+
"WebSocketServer",
|
|
23
|
+
{
|
|
24
|
+
InstanceMethod("stop", &WebSocketServerWrapper::stop),
|
|
25
|
+
InstanceMethod("port", &WebSocketServerWrapper::port),
|
|
26
|
+
InstanceMethod("onClient", &WebSocketServerWrapper::onClient)
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
constructor = Napi::Persistent(func);
|
|
30
|
+
constructor.SuppressDestruct();
|
|
31
|
+
|
|
32
|
+
exports.Set("WebSocketServer", func);
|
|
33
|
+
return exports;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
WebSocketServerWrapper::WebSocketServerWrapper(const Napi::CallbackInfo &info) : Napi::ObjectWrap<WebSocketServerWrapper>(info)
|
|
37
|
+
{
|
|
38
|
+
PLOG_DEBUG << "Constructor called";
|
|
39
|
+
|
|
40
|
+
Napi::Env env = info.Env();
|
|
41
|
+
|
|
42
|
+
// Create WebSocketServer without config
|
|
43
|
+
if (info.Length() == 0)
|
|
44
|
+
{
|
|
45
|
+
try
|
|
46
|
+
{
|
|
47
|
+
PLOG_DEBUG << "Creating a new WebSocketServer without config";
|
|
48
|
+
mWebSocketServerPtr = std::make_unique<rtc::WebSocketServer>();
|
|
49
|
+
}
|
|
50
|
+
catch (std::exception &ex)
|
|
51
|
+
{
|
|
52
|
+
Napi::Error::New(env, std::string("libdatachannel error while creating WebSocketServer without config: ") + ex.what()).ThrowAsJavaScriptException();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
PLOG_DEBUG << "WebSocketServer created without config";
|
|
57
|
+
|
|
58
|
+
instances.insert(this);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Create WebSocketServer with config
|
|
63
|
+
|
|
64
|
+
Napi::Object config = info[0].As<Napi::Object>();
|
|
65
|
+
rtc::WebSocketServerConfiguration webSocketServerConfig;
|
|
66
|
+
|
|
67
|
+
// Port
|
|
68
|
+
if (config.Has("port"))
|
|
69
|
+
{
|
|
70
|
+
if (!config.Get("port").IsNumber())
|
|
71
|
+
{
|
|
72
|
+
Napi::TypeError::New(info.Env(), "port must be a number").ThrowAsJavaScriptException();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
webSocketServerConfig.port = config.Get("port").ToNumber().Uint32Value();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Enable TLS
|
|
79
|
+
if (config.Has("enableTls"))
|
|
80
|
+
{
|
|
81
|
+
if (!config.Get("enableTls").IsBoolean())
|
|
82
|
+
{
|
|
83
|
+
Napi::TypeError::New(info.Env(), "enableTls must be boolean").ThrowAsJavaScriptException();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
webSocketServerConfig.enableTls = config.Get("enableTls").ToBoolean();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Certificate PEM File
|
|
90
|
+
if (config.Has("certificatePemFile"))
|
|
91
|
+
{
|
|
92
|
+
if (!config.Get("certificatePemFile").IsString())
|
|
93
|
+
{
|
|
94
|
+
Napi::TypeError::New(info.Env(), "certificatePemFile must be a string").ThrowAsJavaScriptException();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
webSocketServerConfig.certificatePemFile = config.Get("certificatePemFile").ToString();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Key PEM File
|
|
101
|
+
if (config.Has("keyPemFile"))
|
|
102
|
+
{
|
|
103
|
+
if (!config.Get("keyPemFile").IsString())
|
|
104
|
+
{
|
|
105
|
+
Napi::TypeError::New(info.Env(), "keyPemFile must be a string").ThrowAsJavaScriptException();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
webSocketServerConfig.keyPemFile = config.Get("keyPemFile").ToString();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Key PEM Pass
|
|
112
|
+
if (config.Has("keyPemPass"))
|
|
113
|
+
{
|
|
114
|
+
if (!config.Get("keyPemPass").IsString())
|
|
115
|
+
{
|
|
116
|
+
Napi::TypeError::New(info.Env(), "keyPemPass must be a string").ThrowAsJavaScriptException();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
webSocketServerConfig.keyPemPass = config.Get("keyPemPass").ToString();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Bind Address
|
|
123
|
+
if (config.Has("bindAddress"))
|
|
124
|
+
{
|
|
125
|
+
if (!config.Get("bindAddress").IsString())
|
|
126
|
+
{
|
|
127
|
+
Napi::TypeError::New(info.Env(), "bindAddress must be a string").ThrowAsJavaScriptException();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
webSocketServerConfig.bindAddress = config.Get("bindAddress").ToString();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Connection Timeout
|
|
134
|
+
if (config.Has("connectionTimeout"))
|
|
135
|
+
{
|
|
136
|
+
if (!config.Get("connectionTimeout").IsNumber())
|
|
137
|
+
{
|
|
138
|
+
Napi::TypeError::New(info.Env(), "connectionTimeout must be a number").ThrowAsJavaScriptException();
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
webSocketServerConfig.connectionTimeout = std::chrono::milliseconds(config.Get("connectionTimeout").ToNumber().Int64Value());
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Max Message Size
|
|
145
|
+
if (config.Has("maxMessageSize"))
|
|
146
|
+
{
|
|
147
|
+
if (!config.Get("maxMessageSize").IsNumber())
|
|
148
|
+
{
|
|
149
|
+
Napi::TypeError::New(info.Env(), "maxMessageSize must be a number").ThrowAsJavaScriptException();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
webSocketServerConfig.maxMessageSize = config.Get("maxMessageSize").ToNumber().Int32Value();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Create WebSocketServer with config
|
|
156
|
+
try
|
|
157
|
+
{
|
|
158
|
+
PLOG_DEBUG << "Creating a new WebSocketServer";
|
|
159
|
+
mWebSocketServerPtr = std::make_unique<rtc::WebSocketServer>(webSocketServerConfig);
|
|
160
|
+
}
|
|
161
|
+
catch (std::exception &ex)
|
|
162
|
+
{
|
|
163
|
+
Napi::Error::New(env, std::string("libdatachannel error while creating WebSocketServer: ") + ex.what()).ThrowAsJavaScriptException();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
PLOG_DEBUG << "WebSocketServer created";
|
|
168
|
+
instances.insert(this);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
WebSocketServerWrapper::~WebSocketServerWrapper()
|
|
172
|
+
{
|
|
173
|
+
PLOG_DEBUG << "Destructor called";
|
|
174
|
+
doStop();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
void WebSocketServerWrapper::doStop()
|
|
178
|
+
{
|
|
179
|
+
PLOG_DEBUG << "doStop() called";
|
|
180
|
+
if (mWebSocketServerPtr)
|
|
181
|
+
{
|
|
182
|
+
PLOG_DEBUG << "Closing...";
|
|
183
|
+
try
|
|
184
|
+
{
|
|
185
|
+
mWebSocketServerPtr->stop();
|
|
186
|
+
mWebSocketServerPtr.reset();
|
|
187
|
+
}
|
|
188
|
+
catch (std::exception &ex)
|
|
189
|
+
{
|
|
190
|
+
std::cerr << std::string("libWebRtc error while closing WebSocketServer: ") + ex.what() << std::endl;
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
mOnClientCallback.reset();
|
|
196
|
+
|
|
197
|
+
instances.erase(this);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
void WebSocketServerWrapper::stop(const Napi::CallbackInfo &info)
|
|
201
|
+
{
|
|
202
|
+
PLOG_DEBUG << "stop() called";
|
|
203
|
+
doStop();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
Napi::Value WebSocketServerWrapper::port(const Napi::CallbackInfo &info)
|
|
207
|
+
{
|
|
208
|
+
PLOG_DEBUG << "port() called";
|
|
209
|
+
Napi::Env env = info.Env();
|
|
210
|
+
|
|
211
|
+
if (!mWebSocketServerPtr)
|
|
212
|
+
{
|
|
213
|
+
return Napi::Number::New(info.Env(), 0);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
try
|
|
217
|
+
{
|
|
218
|
+
return Napi::Number::New(info.Env(), mWebSocketServerPtr->port());
|
|
219
|
+
}
|
|
220
|
+
catch (std::exception &ex)
|
|
221
|
+
{
|
|
222
|
+
Napi::Error::New(env, std::string("WebSocketServer error: ") + ex.what()).ThrowAsJavaScriptException();
|
|
223
|
+
return Napi::Number::New(info.Env(), 0);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
void WebSocketServerWrapper::onClient(const Napi::CallbackInfo &info)
|
|
228
|
+
{
|
|
229
|
+
PLOG_DEBUG << "onClient() called";
|
|
230
|
+
Napi::Env env = info.Env();
|
|
231
|
+
int length = info.Length();
|
|
232
|
+
|
|
233
|
+
if (!mWebSocketServerPtr)
|
|
234
|
+
{
|
|
235
|
+
Napi::Error::New(env, "onClient() called on destroyed WebSocketServer").ThrowAsJavaScriptException();
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (length < 1 || !info[0].IsFunction())
|
|
240
|
+
{
|
|
241
|
+
Napi::TypeError::New(env, "Function expected as onClient calback").ThrowAsJavaScriptException();
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Callback
|
|
246
|
+
mOnClientCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
|
|
247
|
+
|
|
248
|
+
mWebSocketServerPtr->onClient([&](std::shared_ptr<rtc::WebSocket> ws)
|
|
249
|
+
{
|
|
250
|
+
PLOG_DEBUG << "onClient ws received from WebSocketServer";
|
|
251
|
+
if (mOnClientCallback)
|
|
252
|
+
mOnClientCallback->call([this, ws](Napi::Env env, std::vector<napi_value> &args) {
|
|
253
|
+
PLOG_DEBUG << "mOnClientCallback call(1)";
|
|
254
|
+
// Check the WebSocketServer is not stopped
|
|
255
|
+
if(instances.find(this) == instances.end())
|
|
256
|
+
throw ThreadSafeCallback::CancelException();
|
|
257
|
+
|
|
258
|
+
// This will run in main thread and needs to construct the
|
|
259
|
+
// arguments for the call
|
|
260
|
+
std::shared_ptr<rtc::WebSocket> webSocket = ws;
|
|
261
|
+
auto instance = WebSocketWrapper::constructor.New({Napi::External<std::shared_ptr<rtc::WebSocket>>::New(env, nullptr), Napi::External<std::shared_ptr<rtc::WebSocket>>::New(env, &webSocket)});
|
|
262
|
+
args = {instance};
|
|
263
|
+
PLOG_DEBUG << "mOnClientCallback call(2)";
|
|
264
|
+
}); });
|
|
265
|
+
}
|