@lng2004/node-datachannel 0.31.0-20251228

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.
Files changed (171) hide show
  1. package/.clang-format +17 -0
  2. package/.editorconfig +12 -0
  3. package/.eslintignore +8 -0
  4. package/.eslintrc.json +27 -0
  5. package/.gitmodules +3 -0
  6. package/.prettierignore +7 -0
  7. package/.prettierrc +13 -0
  8. package/API.md +247 -0
  9. package/BULDING.md +33 -0
  10. package/CMakeLists.txt +134 -0
  11. package/LICENSE +373 -0
  12. package/README.md +130 -0
  13. package/dist/cjs/index.cjs +11 -0
  14. package/dist/cjs/index.cjs.map +1 -0
  15. package/dist/cjs/lib/datachannel-stream.cjs +101 -0
  16. package/dist/cjs/lib/datachannel-stream.cjs.map +1 -0
  17. package/dist/cjs/lib/index.cjs +88 -0
  18. package/dist/cjs/lib/index.cjs.map +1 -0
  19. package/dist/cjs/lib/node-datachannel.cjs +8 -0
  20. package/dist/cjs/lib/node-datachannel.cjs.map +1 -0
  21. package/dist/cjs/lib/websocket-server.cjs +45 -0
  22. package/dist/cjs/lib/websocket-server.cjs.map +1 -0
  23. package/dist/cjs/lib/websocket.cjs +8 -0
  24. package/dist/cjs/lib/websocket.cjs.map +1 -0
  25. package/dist/cjs/polyfill/Events.cjs +86 -0
  26. package/dist/cjs/polyfill/Events.cjs.map +1 -0
  27. package/dist/cjs/polyfill/Exception.cjs +34 -0
  28. package/dist/cjs/polyfill/Exception.cjs.map +1 -0
  29. package/dist/cjs/polyfill/RTCCertificate.cjs +34 -0
  30. package/dist/cjs/polyfill/RTCCertificate.cjs.map +1 -0
  31. package/dist/cjs/polyfill/RTCDataChannel.cjs +214 -0
  32. package/dist/cjs/polyfill/RTCDataChannel.cjs.map +1 -0
  33. package/dist/cjs/polyfill/RTCDtlsTransport.cjs +53 -0
  34. package/dist/cjs/polyfill/RTCDtlsTransport.cjs.map +1 -0
  35. package/dist/cjs/polyfill/RTCError.cjs +83 -0
  36. package/dist/cjs/polyfill/RTCError.cjs.map +1 -0
  37. package/dist/cjs/polyfill/RTCIceCandidate.cjs +132 -0
  38. package/dist/cjs/polyfill/RTCIceCandidate.cjs.map +1 -0
  39. package/dist/cjs/polyfill/RTCIceTransport.cjs +94 -0
  40. package/dist/cjs/polyfill/RTCIceTransport.cjs.map +1 -0
  41. package/dist/cjs/polyfill/RTCPeerConnection.cjs +434 -0
  42. package/dist/cjs/polyfill/RTCPeerConnection.cjs.map +1 -0
  43. package/dist/cjs/polyfill/RTCSctpTransport.cjs +59 -0
  44. package/dist/cjs/polyfill/RTCSctpTransport.cjs.map +1 -0
  45. package/dist/cjs/polyfill/RTCSessionDescription.cjs +45 -0
  46. package/dist/cjs/polyfill/RTCSessionDescription.cjs.map +1 -0
  47. package/dist/cjs/polyfill/index.cjs +42 -0
  48. package/dist/cjs/polyfill/index.cjs.map +1 -0
  49. package/dist/esm/index.mjs +8 -0
  50. package/dist/esm/index.mjs.map +1 -0
  51. package/dist/esm/lib/datachannel-stream.mjs +78 -0
  52. package/dist/esm/lib/datachannel-stream.mjs.map +1 -0
  53. package/dist/esm/lib/index.mjs +62 -0
  54. package/dist/esm/lib/index.mjs.map +1 -0
  55. package/dist/esm/lib/node-datachannel.mjs +12 -0
  56. package/dist/esm/lib/node-datachannel.mjs.map +1 -0
  57. package/dist/esm/lib/websocket-server.mjs +43 -0
  58. package/dist/esm/lib/websocket-server.mjs.map +1 -0
  59. package/dist/esm/lib/websocket.mjs +6 -0
  60. package/dist/esm/lib/websocket.mjs.map +1 -0
  61. package/dist/esm/polyfill/Events.mjs +82 -0
  62. package/dist/esm/polyfill/Events.mjs.map +1 -0
  63. package/dist/esm/polyfill/Exception.mjs +28 -0
  64. package/dist/esm/polyfill/Exception.mjs.map +1 -0
  65. package/dist/esm/polyfill/RTCCertificate.mjs +30 -0
  66. package/dist/esm/polyfill/RTCCertificate.mjs.map +1 -0
  67. package/dist/esm/polyfill/RTCDataChannel.mjs +210 -0
  68. package/dist/esm/polyfill/RTCDataChannel.mjs.map +1 -0
  69. package/dist/esm/polyfill/RTCDtlsTransport.mjs +49 -0
  70. package/dist/esm/polyfill/RTCDtlsTransport.mjs.map +1 -0
  71. package/dist/esm/polyfill/RTCError.mjs +79 -0
  72. package/dist/esm/polyfill/RTCError.mjs.map +1 -0
  73. package/dist/esm/polyfill/RTCIceCandidate.mjs +128 -0
  74. package/dist/esm/polyfill/RTCIceCandidate.mjs.map +1 -0
  75. package/dist/esm/polyfill/RTCIceTransport.mjs +89 -0
  76. package/dist/esm/polyfill/RTCIceTransport.mjs.map +1 -0
  77. package/dist/esm/polyfill/RTCPeerConnection.mjs +430 -0
  78. package/dist/esm/polyfill/RTCPeerConnection.mjs.map +1 -0
  79. package/dist/esm/polyfill/RTCSctpTransport.mjs +55 -0
  80. package/dist/esm/polyfill/RTCSctpTransport.mjs.map +1 -0
  81. package/dist/esm/polyfill/RTCSessionDescription.mjs +41 -0
  82. package/dist/esm/polyfill/RTCSessionDescription.mjs.map +1 -0
  83. package/dist/esm/polyfill/index.mjs +27 -0
  84. package/dist/esm/polyfill/index.mjs.map +1 -0
  85. package/dist/types/lib/datachannel-stream.d.ts +24 -0
  86. package/dist/types/lib/index.d.ts +235 -0
  87. package/dist/types/lib/types.d.ts +118 -0
  88. package/dist/types/lib/websocket-server.d.ts +13 -0
  89. package/dist/types/lib/websocket.d.ts +25 -0
  90. package/dist/types/polyfill/Events.d.ts +15 -0
  91. package/dist/types/polyfill/RTCCertificate.d.ts +9 -0
  92. package/dist/types/polyfill/RTCDataChannel.d.ts +29 -0
  93. package/dist/types/polyfill/RTCDtlsTransport.d.ts +15 -0
  94. package/dist/types/polyfill/RTCError.d.ts +17 -0
  95. package/dist/types/polyfill/RTCIceCandidate.d.ts +21 -0
  96. package/dist/types/polyfill/RTCIceTransport.d.ts +30 -0
  97. package/dist/types/polyfill/RTCPeerConnection.d.ts +64 -0
  98. package/dist/types/polyfill/RTCSctpTransport.d.ts +15 -0
  99. package/dist/types/polyfill/RTCSessionDescription.d.ts +10 -0
  100. package/dist/types/polyfill/index.d.ts +26 -0
  101. package/jest.config.ts +14 -0
  102. package/package.json +121 -0
  103. package/rollup.config.mjs +72 -0
  104. package/src/cpp/data-channel-wrapper.cpp +530 -0
  105. package/src/cpp/data-channel-wrapper.h +63 -0
  106. package/src/cpp/ice-udp-mux-listener-wrapper.cpp +157 -0
  107. package/src/cpp/ice-udp-mux-listener-wrapper.h +43 -0
  108. package/src/cpp/main.cpp +58 -0
  109. package/src/cpp/media-audio-wrapper.cpp +457 -0
  110. package/src/cpp/media-audio-wrapper.h +52 -0
  111. package/src/cpp/media-av1packetization.cpp +24 -0
  112. package/src/cpp/media-av1packetization.h +11 -0
  113. package/src/cpp/media-av1rtppacketizer-wrapper.cpp +126 -0
  114. package/src/cpp/media-av1rtppacketizer-wrapper.h +29 -0
  115. package/src/cpp/media-direction.cpp +43 -0
  116. package/src/cpp/media-direction.h +10 -0
  117. package/src/cpp/media-h264rtppacketizer-wrapper.cpp +126 -0
  118. package/src/cpp/media-h264rtppacketizer-wrapper.h +30 -0
  119. package/src/cpp/media-h265rtppacketizer-wrapper.cpp +126 -0
  120. package/src/cpp/media-h265rtppacketizer-wrapper.h +30 -0
  121. package/src/cpp/media-h26xseparator.cpp +32 -0
  122. package/src/cpp/media-h26xseparator.h +11 -0
  123. package/src/cpp/media-mediahandler-helper.cpp +31 -0
  124. package/src/cpp/media-mediahandler-helper.h +11 -0
  125. package/src/cpp/media-pacinghandler-wrapper.cpp +79 -0
  126. package/src/cpp/media-pacinghandler-wrapper.h +28 -0
  127. package/src/cpp/media-rtcpnackresponder-wrapper.cpp +68 -0
  128. package/src/cpp/media-rtcpnackresponder-wrapper.h +28 -0
  129. package/src/cpp/media-rtcpreceivingsession-wrapper.cpp +57 -0
  130. package/src/cpp/media-rtcpreceivingsession-wrapper.h +28 -0
  131. package/src/cpp/media-rtcpsrreporter-wrapper.cpp +93 -0
  132. package/src/cpp/media-rtcpsrreporter-wrapper.h +30 -0
  133. package/src/cpp/media-rtppacketizationconfig-wrapper.cpp +167 -0
  134. package/src/cpp/media-rtppacketizationconfig-wrapper.h +35 -0
  135. package/src/cpp/media-rtppacketizer-wrapper.cpp +95 -0
  136. package/src/cpp/media-rtppacketizer-wrapper.h +30 -0
  137. package/src/cpp/media-track-wrapper.cpp +458 -0
  138. package/src/cpp/media-track-wrapper.h +61 -0
  139. package/src/cpp/media-video-wrapper.cpp +526 -0
  140. package/src/cpp/media-video-wrapper.h +56 -0
  141. package/src/cpp/peer-connection-wrapper.cpp +1298 -0
  142. package/src/cpp/peer-connection-wrapper.h +89 -0
  143. package/src/cpp/rtc-wrapper.cpp +205 -0
  144. package/src/cpp/rtc-wrapper.h +24 -0
  145. package/src/cpp/thread-safe-callback.cpp +57 -0
  146. package/src/cpp/thread-safe-callback.h +47 -0
  147. package/src/cpp/web-socket-server-wrapper.cpp +275 -0
  148. package/src/cpp/web-socket-server-wrapper.h +41 -0
  149. package/src/cpp/web-socket-wrapper.cpp +796 -0
  150. package/src/cpp/web-socket-wrapper.h +63 -0
  151. package/src/index.ts +9 -0
  152. package/src/lib/datachannel-stream.ts +100 -0
  153. package/src/lib/index.ts +283 -0
  154. package/src/lib/node-datachannel.ts +3 -0
  155. package/src/lib/types.ts +168 -0
  156. package/src/lib/websocket-server.ts +37 -0
  157. package/src/lib/websocket.ts +26 -0
  158. package/src/polyfill/Events.ts +82 -0
  159. package/src/polyfill/Exception.ts +37 -0
  160. package/src/polyfill/README.md +41 -0
  161. package/src/polyfill/RTCCertificate.ts +21 -0
  162. package/src/polyfill/RTCDataChannel.ts +225 -0
  163. package/src/polyfill/RTCDtlsTransport.ts +46 -0
  164. package/src/polyfill/RTCError.ts +78 -0
  165. package/src/polyfill/RTCIceCandidate.ts +128 -0
  166. package/src/polyfill/RTCIceTransport.ts +90 -0
  167. package/src/polyfill/RTCPeerConnection.ts +527 -0
  168. package/src/polyfill/RTCSctpTransport.ts +51 -0
  169. package/src/polyfill/RTCSessionDescription.ts +41 -0
  170. package/src/polyfill/index.ts +38 -0
  171. package/tsconfig.json +21 -0
@@ -0,0 +1,89 @@
1
+ #ifndef PEER_CONNECTION_WRAPPER_H
2
+ #define PEER_CONNECTION_WRAPPER_H
3
+
4
+ #include <string>
5
+ #include <memory>
6
+ #include <unordered_set>
7
+
8
+ #include <napi.h>
9
+ #include <rtc/rtc.hpp>
10
+
11
+ #include "thread-safe-callback.h"
12
+
13
+ class PeerConnectionWrapper : public Napi::ObjectWrap<PeerConnectionWrapper>
14
+ {
15
+ public:
16
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
17
+ PeerConnectionWrapper(const Napi::CallbackInfo &info);
18
+ ~PeerConnectionWrapper();
19
+
20
+ // Functions
21
+ void close(const Napi::CallbackInfo &info);
22
+ void setLocalDescription(const Napi::CallbackInfo &info);
23
+ void setRemoteDescription(const Napi::CallbackInfo &info);
24
+ Napi::Value localDescription(const Napi::CallbackInfo &info);
25
+ Napi::Value remoteDescription(const Napi::CallbackInfo &info);
26
+ void addRemoteCandidate(const Napi::CallbackInfo &info);
27
+ Napi::Value createDataChannel(const Napi::CallbackInfo &info);
28
+
29
+ #if RTC_ENABLE_MEDIA == 1
30
+ Napi::Value addTrack(const Napi::CallbackInfo &info);
31
+ void onTrack(const Napi::CallbackInfo &info);
32
+ #endif
33
+
34
+ Napi::Value hasMedia(const Napi::CallbackInfo &info);
35
+ Napi::Value state(const Napi::CallbackInfo &info);
36
+ Napi::Value iceState(const Napi::CallbackInfo &info);
37
+ Napi::Value signalingState(const Napi::CallbackInfo &info);
38
+ Napi::Value gatheringState(const Napi::CallbackInfo &info);
39
+ Napi::Value remoteFingerprint(const Napi::CallbackInfo &info);
40
+
41
+ // Callbacks
42
+ void onLocalDescription(const Napi::CallbackInfo &info);
43
+ void onLocalCandidate(const Napi::CallbackInfo &info);
44
+ void onStateChange(const Napi::CallbackInfo &info);
45
+ void onIceStateChange(const Napi::CallbackInfo &info);
46
+ void onSignalingStateChange(const Napi::CallbackInfo &info);
47
+ void onGatheringStateChange(const Napi::CallbackInfo &info);
48
+ void onDataChannel(const Napi::CallbackInfo &info);
49
+
50
+ // Stats
51
+ Napi::Value bytesSent(const Napi::CallbackInfo &info);
52
+ Napi::Value bytesReceived(const Napi::CallbackInfo &info);
53
+ Napi::Value rtt(const Napi::CallbackInfo &info);
54
+ Napi::Value getSelectedCandidatePair(const Napi::CallbackInfo &info);
55
+ Napi::Value maxDataChannelId(const Napi::CallbackInfo &info);
56
+ Napi::Value maxMessageSize(const Napi::CallbackInfo &info);
57
+
58
+ // Close all existing Peer Connections
59
+ static void CloseAll();
60
+
61
+ // Reset all Callbacks for existing Peer Connections
62
+ static void CleanupAll();
63
+
64
+ private:
65
+ static Napi::FunctionReference constructor;
66
+ static std::unordered_set<PeerConnectionWrapper *> instances;
67
+
68
+ void doClose();
69
+ void doCleanup();
70
+
71
+ std::string mPeerName;
72
+ std::unique_ptr<rtc::PeerConnection> mRtcPeerConnPtr = nullptr;
73
+
74
+ // Callback Ptrs
75
+ std::unique_ptr<ThreadSafeCallback> mOnLocalDescriptionCallback = nullptr;
76
+ std::unique_ptr<ThreadSafeCallback> mOnLocalCandidateCallback = nullptr;
77
+ std::unique_ptr<ThreadSafeCallback> mOnStateChangeCallback = nullptr;
78
+ std::unique_ptr<ThreadSafeCallback> mOnIceStateChangeCallback = nullptr;
79
+ std::unique_ptr<ThreadSafeCallback> mOnSignalingStateChangeCallback = nullptr;
80
+ std::unique_ptr<ThreadSafeCallback> mOnGatheringStateChangeCallback = nullptr;
81
+ std::unique_ptr<ThreadSafeCallback> mOnDataChannelCallback = nullptr;
82
+ std::unique_ptr<ThreadSafeCallback> mOnTrackCallback = nullptr;
83
+
84
+ // Helpers
85
+ std::string candidateTypeToString(const rtc::Candidate::Type &type);
86
+ std::string candidateTransportTypeToString(const rtc::Candidate::TransportType &transportType);
87
+ };
88
+
89
+ #endif // PEER_CONNECTION_WRAPPER_H
@@ -0,0 +1,205 @@
1
+ #include "rtc-wrapper.h"
2
+ #include "peer-connection-wrapper.h"
3
+ #include "data-channel-wrapper.h"
4
+
5
+ #if RTC_ENABLE_MEDIA == 1
6
+ #include "media-track-wrapper.h"
7
+ #endif
8
+
9
+ #if RTC_ENABLE_WEBSOCKET == 1
10
+ #include "web-socket-wrapper.h"
11
+ #include "web-socket-server-wrapper.h"
12
+ #endif
13
+
14
+ #include "plog/Log.h"
15
+
16
+ #include <chrono>
17
+ #include <future>
18
+
19
+ Napi::Object RtcWrapper::Init(Napi::Env env, Napi::Object exports)
20
+ {
21
+ Napi::HandleScope scope(env);
22
+
23
+ exports.Set("initLogger", Napi::Function::New(env, &RtcWrapper::initLogger));
24
+ exports.Set("cleanup", Napi::Function::New(env, &RtcWrapper::cleanup));
25
+ exports.Set("preload", Napi::Function::New(env, &RtcWrapper::preload));
26
+ exports.Set("setSctpSettings", Napi::Function::New(env, &RtcWrapper::setSctpSettings));
27
+ exports.Set("getLibraryVersion", Napi::Function::New(env, &RtcWrapper::getLibraryVersion));
28
+
29
+ return exports;
30
+ }
31
+
32
+ void RtcWrapper::preload(const Napi::CallbackInfo &info)
33
+ {
34
+ PLOG_DEBUG << "preload() called";
35
+ Napi::Env env = info.Env();
36
+ try
37
+ {
38
+ rtc::Preload();
39
+ }
40
+ catch (std::exception &ex)
41
+ {
42
+ Napi::Error::New(env, std::string("libdatachannel error# ") + ex.what()).ThrowAsJavaScriptException();
43
+ }
44
+ }
45
+
46
+ void RtcWrapper::initLogger(const Napi::CallbackInfo &info)
47
+ {
48
+ Napi::Env env = info.Env();
49
+ int length = info.Length();
50
+
51
+ // We expect (String, Object, Function) as param
52
+ if (length < 1 || !info[0].IsString())
53
+ {
54
+ Napi::TypeError::New(env, "LogLevel(String) expected").ThrowAsJavaScriptException();
55
+ return;
56
+ }
57
+
58
+ std::string logLevelStr = info[0].As<Napi::String>().ToString();
59
+ rtc::LogLevel logLevel = rtc::LogLevel::None;
60
+
61
+ if (logLevelStr == "Verbose")
62
+ logLevel = rtc::LogLevel::Verbose;
63
+ if (logLevelStr == "Debug")
64
+ logLevel = rtc::LogLevel::Debug;
65
+ if (logLevelStr == "Info")
66
+ logLevel = rtc::LogLevel::Info;
67
+ if (logLevelStr == "Warning")
68
+ logLevel = rtc::LogLevel::Warning;
69
+ if (logLevelStr == "Error")
70
+ logLevel = rtc::LogLevel::Error;
71
+ if (logLevelStr == "Fatal")
72
+ logLevel = rtc::LogLevel::Fatal;
73
+
74
+ try
75
+ {
76
+ if (length < 2)
77
+ {
78
+ rtc::InitLogger(logLevel);
79
+ }
80
+ else
81
+ {
82
+ if (!info[1].IsFunction())
83
+ {
84
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
85
+ return;
86
+ }
87
+ logCallback = std::make_unique<ThreadSafeCallback>(info[1].As<Napi::Function>());
88
+ rtc::InitLogger(logLevel,
89
+ [&](rtc::LogLevel level, std::string message)
90
+ {
91
+ if (logCallback)
92
+ logCallback->call(
93
+ [level, message = std::move(message)](Napi::Env env, std::vector<napi_value> &args)
94
+ {
95
+ // This will run in main thread and needs to construct the
96
+ // arguments for the call
97
+
98
+ std::string logLevel;
99
+ if (level == rtc::LogLevel::Verbose)
100
+ logLevel = "Verbose";
101
+ if (level == rtc::LogLevel::Debug)
102
+ logLevel = "Debug";
103
+ if (level == rtc::LogLevel::Info)
104
+ logLevel = "Info";
105
+ if (level == rtc::LogLevel::Warning)
106
+ logLevel = "Warning";
107
+ if (level == rtc::LogLevel::Error)
108
+ logLevel = "Error";
109
+ if (level == rtc::LogLevel::Fatal)
110
+ logLevel = "Fatal";
111
+ args = {Napi::String::New(env, logLevel), Napi::String::New(env, message)};
112
+ });
113
+ });
114
+ }
115
+ }
116
+ catch (std::exception &ex)
117
+ {
118
+ Napi::Error::New(env, std::string("libdatachannel error# ") + ex.what()).ThrowAsJavaScriptException();
119
+ return;
120
+ }
121
+ }
122
+
123
+ void RtcWrapper::cleanup(const Napi::CallbackInfo &info)
124
+ {
125
+ PLOG_DEBUG << "cleanup() called";
126
+ Napi::Env env = info.Env();
127
+ try
128
+ {
129
+ PeerConnectionWrapper::CloseAll();
130
+ DataChannelWrapper::CloseAll();
131
+
132
+ #if RTC_ENABLE_MEDIA == 1
133
+ TrackWrapper::CloseAll();
134
+ #endif
135
+
136
+ #if RTC_ENABLE_WEBSOCKET == 1
137
+ WebSocketWrapper::CloseAll();
138
+ WebSocketServerWrapper::StopAll();
139
+ #endif
140
+
141
+ const auto timeout = std::chrono::seconds(10);
142
+ if (rtc::Cleanup().wait_for(std::chrono::seconds(timeout)) == std::future_status::timeout)
143
+ throw std::runtime_error("cleanup timeout (possible deadlock)");
144
+
145
+ // Cleanup the instances
146
+ PeerConnectionWrapper::CleanupAll();
147
+ DataChannelWrapper::CleanupAll();
148
+
149
+ #if RTC_ENABLE_MEDIA == 1
150
+ TrackWrapper::CleanupAll();
151
+ #endif
152
+
153
+ #if RTC_ENABLE_WEBSOCKET == 1
154
+ WebSocketWrapper::CleanupAll();
155
+ #endif
156
+
157
+ if (logCallback)
158
+ logCallback.reset();
159
+ }
160
+ catch (std::exception &ex)
161
+ {
162
+ Napi::Error::New(env, std::string("libdatachannel error# ") + ex.what()).ThrowAsJavaScriptException();
163
+ return;
164
+ }
165
+ }
166
+
167
+ void RtcWrapper::setSctpSettings(const Napi::CallbackInfo &info)
168
+ {
169
+ PLOG_DEBUG << "setSctpSettings() called";
170
+ Napi::Env env = info.Env();
171
+ int length = info.Length();
172
+
173
+ // We expect (Object) as param
174
+ if (length < 1 || !info[0].IsObject())
175
+ {
176
+ Napi::TypeError::New(env, "Configuration (Object) expected").ThrowAsJavaScriptException();
177
+ return;
178
+ }
179
+
180
+ rtc::SctpSettings settings;
181
+ Napi::Object config = info[0].As<Napi::Object>();
182
+
183
+ if (config.Get("recvBufferSize").IsNumber())
184
+ settings.recvBufferSize = config.Get("recvBufferSize").As<Napi::Number>().Uint32Value();
185
+ if (config.Get("sendBufferSize").IsNumber())
186
+ settings.sendBufferSize = config.Get("sendBufferSize").As<Napi::Number>().Uint32Value();
187
+ if (config.Get("maxChunksOnQueue").IsNumber())
188
+ settings.maxChunksOnQueue = config.Get("maxChunksOnQueue").As<Napi::Number>().Uint32Value();
189
+ if (config.Get("initialCongestionWindow").IsNumber())
190
+ settings.initialCongestionWindow = config.Get("initialCongestionWindow").As<Napi::Number>().Uint32Value();
191
+ if (config.Get("congestionControlModule").IsNumber())
192
+ settings.congestionControlModule = config.Get("congestionControlModule").As<Napi::Number>().Uint32Value();
193
+ if (config.Get("delayedSackTime").IsNumber())
194
+ settings.delayedSackTime =
195
+ std::chrono::milliseconds(config.Get("delayedSackTime").As<Napi::Number>().Uint32Value());
196
+
197
+ rtc::SetSctpSettings(settings);
198
+ }
199
+
200
+ Napi::Value RtcWrapper::getLibraryVersion(const Napi::CallbackInfo &info)
201
+ {
202
+ PLOG_DEBUG << "getLibraryVersion() called";
203
+ Napi::Env env = info.Env();
204
+ return Napi::String::New(info.Env(), RTC_VERSION);
205
+ }
@@ -0,0 +1,24 @@
1
+ #ifndef RTC_WRAPPER_H
2
+ #define RTC_WRAPPER_H
3
+
4
+ #include <napi.h>
5
+
6
+ #include "rtc/rtc.hpp"
7
+
8
+ #include "thread-safe-callback.h"
9
+
10
+ class RtcWrapper
11
+ {
12
+ public:
13
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
14
+ static void preload(const Napi::CallbackInfo &info);
15
+ static void initLogger(const Napi::CallbackInfo &info);
16
+ static void cleanup(const Napi::CallbackInfo &info);
17
+ static void setSctpSettings(const Napi::CallbackInfo &info);
18
+ static Napi::Value getLibraryVersion(const Napi::CallbackInfo &info);
19
+
20
+ private:
21
+ static inline std::unique_ptr<ThreadSafeCallback> logCallback = nullptr;
22
+ };
23
+
24
+ #endif // RTC_WRAPPER_H
@@ -0,0 +1,57 @@
1
+ #include "thread-safe-callback.h"
2
+
3
+ #include <stdexcept>
4
+
5
+ const char *ThreadSafeCallback::CancelException::what() const throw() { return "ThreadSafeCallback cancelled"; }
6
+
7
+ ThreadSafeCallback::ThreadSafeCallback(Napi::Function callback)
8
+ {
9
+ Napi::Env env = callback.Env();
10
+
11
+ if (!callback.IsFunction())
12
+ throw Napi::Error::New(env, "Callback must be a function");
13
+
14
+ tsfn = tsfn_t::New(env, std::move(callback), "ThreadSafeCallback callback",
15
+ 0, // unlimited queue
16
+ 1);
17
+ }
18
+
19
+ ThreadSafeCallback::~ThreadSafeCallback() { tsfn.Abort(); }
20
+
21
+ void ThreadSafeCallback::call(arg_func_t argFunc, cleanup_func_t cleanupFunc)
22
+ {
23
+ CallbackData *data = new CallbackData{std::move(argFunc), std::move(cleanupFunc)};
24
+ if (tsfn.BlockingCall(data) != napi_ok)
25
+ {
26
+ delete data;
27
+ throw std::runtime_error("Failed to call JavaScript callback");
28
+ }
29
+ }
30
+
31
+ void ThreadSafeCallback::callbackFunc(Napi::Env env, Napi::Function callback, ContextType *context, CallbackData *data)
32
+ {
33
+ // if env is gone, it could mean this cb was destroyed. See issue#176
34
+ if (!data || !env)
35
+ return;
36
+
37
+ arg_vector_t args;
38
+ arg_func_t argFunc(std::move(data->argFunc));
39
+ cleanup_func_t cleanup(std::move(data->cleanupFunc));
40
+ delete data;
41
+
42
+ try
43
+ {
44
+ argFunc(env, args);
45
+ }
46
+ catch (CancelException &)
47
+ {
48
+ return;
49
+ }
50
+
51
+ if (callback)
52
+ {
53
+ callback.Call(args);
54
+ }
55
+
56
+ cleanup();
57
+ }
@@ -0,0 +1,47 @@
1
+ #ifndef THREAD_SAFE_CALLBACK_H
2
+ #define THREAD_SAFE_CALLBACK_H
3
+
4
+ #include <napi.h>
5
+
6
+ #include <vector>
7
+ #include <functional>
8
+
9
+ class ThreadSafeCallback
10
+ {
11
+ public:
12
+ using arg_vector_t = std::vector<napi_value>;
13
+ using arg_func_t = std::function<void(napi_env, arg_vector_t &)>;
14
+ using cleanup_func_t = std::function<void()>;
15
+
16
+ ThreadSafeCallback(Napi::Function callback);
17
+ ~ThreadSafeCallback();
18
+
19
+ ThreadSafeCallback(const ThreadSafeCallback &) = delete;
20
+ ThreadSafeCallback(ThreadSafeCallback &&) = delete;
21
+
22
+ ThreadSafeCallback &operator=(const ThreadSafeCallback &) = delete;
23
+ ThreadSafeCallback &operator=(ThreadSafeCallback &&) = delete;
24
+
25
+ void call(
26
+ arg_func_t argFunc, cleanup_func_t cleanupFunc = []() {});
27
+
28
+ class CancelException : public std::exception
29
+ {
30
+ const char *what() const throw();
31
+ };
32
+
33
+ private:
34
+ using ContextType = std::nullptr_t;
35
+ struct CallbackData
36
+ {
37
+ arg_func_t argFunc;
38
+ cleanup_func_t cleanupFunc;
39
+ };
40
+
41
+ static void callbackFunc(Napi::Env env, Napi::Function callback, ContextType *context, CallbackData *data);
42
+
43
+ using tsfn_t = Napi::TypedThreadSafeFunction<ContextType, CallbackData, callbackFunc>;
44
+ tsfn_t tsfn;
45
+ };
46
+
47
+ #endif // THREAD_SAFE_CALLBACK_H
@@ -0,0 +1,275 @@
1
+ #include "web-socket-server-wrapper.h"
2
+ #include "web-socket-wrapper.h"
3
+
4
+ #include "plog/Log.h"
5
+
6
+ Napi::FunctionReference WebSocketServerWrapper::constructor = Napi::FunctionReference();
7
+ std::unordered_set<WebSocketServerWrapper *> WebSocketServerWrapper::instances;
8
+
9
+ void WebSocketServerWrapper::StopAll()
10
+ {
11
+ PLOG_DEBUG << "StopAll() called";
12
+ auto copy(instances);
13
+ for (auto inst : copy)
14
+ inst->doStop();
15
+ }
16
+
17
+ Napi::Object WebSocketServerWrapper::Init(Napi::Env env, Napi::Object exports)
18
+ {
19
+ Napi::HandleScope scope(env);
20
+
21
+ Napi::Function func = DefineClass(env, "WebSocketServer",
22
+ {InstanceMethod("stop", &WebSocketServerWrapper::stop),
23
+ InstanceMethod("port", &WebSocketServerWrapper::port),
24
+ InstanceMethod("onClient", &WebSocketServerWrapper::onClient)});
25
+
26
+ // If this is not the first call, we don't want to reassign the constructor (hot-reload problem)
27
+ if (constructor.IsEmpty())
28
+ {
29
+ constructor = Napi::Persistent(func);
30
+ constructor.SuppressDestruct();
31
+ }
32
+
33
+ exports.Set("WebSocketServer", func);
34
+ return exports;
35
+ }
36
+
37
+ WebSocketServerWrapper::WebSocketServerWrapper(const Napi::CallbackInfo &info)
38
+ : Napi::ObjectWrap<WebSocketServerWrapper>(info)
39
+ {
40
+ PLOG_DEBUG << "Constructor called";
41
+ Napi::Env env = info.Env();
42
+
43
+ // Create WebSocketServer without config
44
+ if (info.Length() == 0)
45
+ {
46
+ try
47
+ {
48
+ PLOG_DEBUG << "Creating a new WebSocketServer without config";
49
+ mWebSocketServerPtr = std::make_unique<rtc::WebSocketServer>();
50
+ }
51
+ catch (std::exception &ex)
52
+ {
53
+ Napi::Error::New(env,
54
+ std::string("libdatachannel error while creating WebSocketServer without config: ") + ex.what())
55
+ .ThrowAsJavaScriptException();
56
+ return;
57
+ }
58
+
59
+ PLOG_DEBUG << "WebSocketServer created without config";
60
+
61
+ instances.insert(this);
62
+ return;
63
+ }
64
+
65
+ // Create WebSocketServer with config
66
+
67
+ Napi::Object config = info[0].As<Napi::Object>();
68
+ rtc::WebSocketServerConfiguration webSocketServerConfig;
69
+
70
+ // Port
71
+ if (config.Has("port"))
72
+ {
73
+ if (!config.Get("port").IsNumber())
74
+ {
75
+ Napi::TypeError::New(info.Env(), "port must be a number").ThrowAsJavaScriptException();
76
+ return;
77
+ }
78
+ webSocketServerConfig.port = config.Get("port").ToNumber().Uint32Value();
79
+ }
80
+
81
+ // Enable TLS
82
+ if (config.Has("enableTls"))
83
+ {
84
+ if (!config.Get("enableTls").IsBoolean())
85
+ {
86
+ Napi::TypeError::New(info.Env(), "enableTls must be boolean").ThrowAsJavaScriptException();
87
+ return;
88
+ }
89
+ webSocketServerConfig.enableTls = config.Get("enableTls").ToBoolean();
90
+ }
91
+
92
+ // Certificate PEM File
93
+ if (config.Has("certificatePemFile"))
94
+ {
95
+ if (!config.Get("certificatePemFile").IsString())
96
+ {
97
+ Napi::TypeError::New(info.Env(), "certificatePemFile must be a string").ThrowAsJavaScriptException();
98
+ return;
99
+ }
100
+ webSocketServerConfig.certificatePemFile = config.Get("certificatePemFile").ToString();
101
+ }
102
+
103
+ // Key PEM File
104
+ if (config.Has("keyPemFile"))
105
+ {
106
+ if (!config.Get("keyPemFile").IsString())
107
+ {
108
+ Napi::TypeError::New(info.Env(), "keyPemFile must be a string").ThrowAsJavaScriptException();
109
+ return;
110
+ }
111
+ webSocketServerConfig.keyPemFile = config.Get("keyPemFile").ToString();
112
+ }
113
+
114
+ // Key PEM Pass
115
+ if (config.Has("keyPemPass"))
116
+ {
117
+ if (!config.Get("keyPemPass").IsString())
118
+ {
119
+ Napi::TypeError::New(info.Env(), "keyPemPass must be a string").ThrowAsJavaScriptException();
120
+ return;
121
+ }
122
+ webSocketServerConfig.keyPemPass = config.Get("keyPemPass").ToString();
123
+ }
124
+
125
+ // Bind Address
126
+ if (config.Has("bindAddress"))
127
+ {
128
+ if (!config.Get("bindAddress").IsString())
129
+ {
130
+ Napi::TypeError::New(info.Env(), "bindAddress must be a string").ThrowAsJavaScriptException();
131
+ return;
132
+ }
133
+ webSocketServerConfig.bindAddress = config.Get("bindAddress").ToString();
134
+ }
135
+
136
+ // Connection Timeout
137
+ if (config.Has("connectionTimeout"))
138
+ {
139
+ if (!config.Get("connectionTimeout").IsNumber())
140
+ {
141
+ Napi::TypeError::New(info.Env(), "connectionTimeout must be a number").ThrowAsJavaScriptException();
142
+ return;
143
+ }
144
+ webSocketServerConfig.connectionTimeout =
145
+ std::chrono::milliseconds(config.Get("connectionTimeout").ToNumber().Int64Value());
146
+ }
147
+
148
+ // Max Message Size
149
+ if (config.Has("maxMessageSize"))
150
+ {
151
+ if (!config.Get("maxMessageSize").IsNumber())
152
+ {
153
+ Napi::TypeError::New(info.Env(), "maxMessageSize must be a number").ThrowAsJavaScriptException();
154
+ return;
155
+ }
156
+ webSocketServerConfig.maxMessageSize = config.Get("maxMessageSize").ToNumber().Int32Value();
157
+ }
158
+
159
+ // Create WebSocketServer with config
160
+ try
161
+ {
162
+ PLOG_DEBUG << "Creating a new WebSocketServer";
163
+ mWebSocketServerPtr = std::make_unique<rtc::WebSocketServer>(webSocketServerConfig);
164
+ }
165
+ catch (std::exception &ex)
166
+ {
167
+ Napi::Error::New(env, std::string("libdatachannel error while creating WebSocketServer: ") + ex.what())
168
+ .ThrowAsJavaScriptException();
169
+ return;
170
+ }
171
+
172
+ PLOG_DEBUG << "WebSocketServer created";
173
+ instances.insert(this);
174
+ }
175
+
176
+ WebSocketServerWrapper::~WebSocketServerWrapper()
177
+ {
178
+ PLOG_DEBUG << "Destructor called";
179
+ doStop();
180
+ }
181
+
182
+ void WebSocketServerWrapper::doStop()
183
+ {
184
+ PLOG_DEBUG << "doStop() called";
185
+ if (mWebSocketServerPtr)
186
+ {
187
+ PLOG_DEBUG << "Stopping...";
188
+ try
189
+ {
190
+ mWebSocketServerPtr->stop();
191
+ mWebSocketServerPtr.reset();
192
+ }
193
+ catch (std::exception &ex)
194
+ {
195
+ std::cerr << std::string("libdatachannel error while closing WebSocketServer: ") + ex.what() << std::endl;
196
+ return;
197
+ }
198
+ }
199
+
200
+ mOnClientCallback.reset();
201
+ instances.erase(this);
202
+ }
203
+
204
+ void WebSocketServerWrapper::stop(const Napi::CallbackInfo &info)
205
+ {
206
+ PLOG_DEBUG << "stop() called";
207
+ doStop();
208
+ }
209
+
210
+ Napi::Value WebSocketServerWrapper::port(const Napi::CallbackInfo &info)
211
+ {
212
+ PLOG_DEBUG << "port() called";
213
+ Napi::Env env = info.Env();
214
+
215
+ if (!mWebSocketServerPtr)
216
+ {
217
+ return Napi::Number::New(info.Env(), 0);
218
+ }
219
+
220
+ try
221
+ {
222
+ return Napi::Number::New(info.Env(), mWebSocketServerPtr->port());
223
+ }
224
+ catch (std::exception &ex)
225
+ {
226
+ Napi::Error::New(env, std::string("WebSocketServer error: ") + ex.what()).ThrowAsJavaScriptException();
227
+ return Napi::Number::New(info.Env(), 0);
228
+ }
229
+ }
230
+
231
+ void WebSocketServerWrapper::onClient(const Napi::CallbackInfo &info)
232
+ {
233
+ PLOG_DEBUG << "onClient() called";
234
+ Napi::Env env = info.Env();
235
+ int length = info.Length();
236
+
237
+ if (!mWebSocketServerPtr)
238
+ {
239
+ Napi::Error::New(env, "onClient() called on destroyed WebSocketServer").ThrowAsJavaScriptException();
240
+ return;
241
+ }
242
+
243
+ if (length < 1 || !info[0].IsFunction())
244
+ {
245
+ Napi::TypeError::New(env, "Function expected as onClient callback").ThrowAsJavaScriptException();
246
+ return;
247
+ }
248
+
249
+ // Callback
250
+ mOnClientCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
251
+
252
+ mWebSocketServerPtr->onClient(
253
+ [&](std::shared_ptr<rtc::WebSocket> ws)
254
+ {
255
+ PLOG_DEBUG << "onClient ws received from WebSocketServer";
256
+ if (mOnClientCallback)
257
+ mOnClientCallback->call(
258
+ [this, ws](Napi::Env env, std::vector<napi_value> &args)
259
+ {
260
+ PLOG_DEBUG << "mOnClientCallback call(1)";
261
+ // Check the WebSocketServer is not stopped
262
+ if (instances.find(this) == instances.end())
263
+ throw ThreadSafeCallback::CancelException();
264
+
265
+ // This will run in main thread and needs to construct the
266
+ // arguments for the call
267
+ std::shared_ptr<rtc::WebSocket> webSocket = ws;
268
+ // First argument is just a placeholder
269
+ auto instance = WebSocketWrapper::constructor.New(
270
+ {Napi::Boolean::New(env, false), Napi::External<std::shared_ptr<rtc::WebSocket>>::New(env, &webSocket)});
271
+ args = {instance};
272
+ PLOG_DEBUG << "mOnClientCallback call(2)";
273
+ });
274
+ });
275
+ }