@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,796 @@
1
+ #include "web-socket-wrapper.h"
2
+
3
+ #include "plog/Log.h"
4
+
5
+ Napi::FunctionReference WebSocketWrapper::constructor = Napi::FunctionReference();
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 =
29
+ DefineClass(env, "WebSocket",
30
+ {
31
+ InstanceMethod("open", &WebSocketWrapper::open),
32
+ InstanceMethod("close", &WebSocketWrapper::close),
33
+ InstanceMethod("forceClose", &WebSocketWrapper::forceClose),
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
+ InstanceMethod("remoteAddress", &WebSocketWrapper::remoteAddress),
46
+ InstanceMethod("path", &WebSocketWrapper::path),
47
+ });
48
+
49
+ // If this is not the first call, we don't want to reassign the constructor (hot-reload problem)
50
+ if (constructor.IsEmpty())
51
+ {
52
+ constructor = Napi::Persistent(func);
53
+ constructor.SuppressDestruct();
54
+ }
55
+
56
+ exports.Set("WebSocket", func);
57
+ return exports;
58
+ }
59
+
60
+ WebSocketWrapper::WebSocketWrapper(const Napi::CallbackInfo &info) : Napi::ObjectWrap<WebSocketWrapper>(info)
61
+ {
62
+ PLOG_DEBUG << "Constructor called";
63
+ Napi::Env env = info.Env();
64
+
65
+ // Create WebSocket using rtc::WebSocket provided by WebSocketServer
66
+ if (info.Length() > 1)
67
+ {
68
+ mWebSocketPtr = *(info[1].As<Napi::External<std::shared_ptr<rtc::WebSocket>>>().Data());
69
+ PLOG_DEBUG << "Using WebSocket got from WebSocketServer";
70
+ instances.insert(this);
71
+
72
+ // Closed callback must be set to trigger cleanup
73
+ mOnClosedCallback =
74
+ std::make_unique<ThreadSafeCallback>(Napi::Function::New(info.Env(), [](const Napi::CallbackInfo &) {}));
75
+ return;
76
+ }
77
+
78
+ // Create WebSocket without config
79
+ if (info.Length() == 0)
80
+ {
81
+ try
82
+ {
83
+ PLOG_DEBUG << "Creating a new WebSocket without config";
84
+ mWebSocketPtr = std::make_unique<rtc::WebSocket>();
85
+ }
86
+ catch (std::exception &ex)
87
+ {
88
+ Napi::Error::New(env, std::string("libdatachannel error while creating WebSocket without config: ") + ex.what())
89
+ .ThrowAsJavaScriptException();
90
+ return;
91
+ }
92
+ instances.insert(this);
93
+
94
+ // Closed callback must be set to trigger cleanup
95
+ mOnClosedCallback =
96
+ std::make_unique<ThreadSafeCallback>(Napi::Function::New(info.Env(), [](const Napi::CallbackInfo &) {}));
97
+ return;
98
+ }
99
+
100
+ // Create WebSocket with config
101
+ PLOG_DEBUG << "Creating a new WebSocket with config";
102
+
103
+ Napi::Object config = info[0].As<Napi::Object>();
104
+ rtc::WebSocketConfiguration webSocketConfig;
105
+
106
+ if (config.Has("disableTlsVerification"))
107
+ {
108
+ if (!config.Get("disableTlsVerification").IsBoolean())
109
+ {
110
+ Napi::TypeError::New(info.Env(), "disableTlsVerification must be boolean").ThrowAsJavaScriptException();
111
+ return;
112
+ }
113
+ webSocketConfig.disableTlsVerification = config.Get("disableTlsVerification").ToBoolean();
114
+ }
115
+
116
+ // Proxy Server
117
+ if (config.Has("proxyServer") && config.Get("proxyServer").IsObject())
118
+ {
119
+ Napi::Object proxyServer = config.Get("proxyServer").As<Napi::Object>();
120
+
121
+ // IP
122
+ std::string ip = proxyServer.Get("ip").As<Napi::String>();
123
+
124
+ // Port
125
+ uint16_t port = proxyServer.Get("port").As<Napi::Number>().Uint32Value();
126
+
127
+ // Type
128
+ std::string strType = proxyServer.Get("type").As<Napi::String>().ToString();
129
+ rtc::ProxyServer::Type type = rtc::ProxyServer::Type::Http;
130
+
131
+ if (strType == "Socks5")
132
+ type = rtc::ProxyServer::Type::Socks5;
133
+
134
+ // Username & Password
135
+ std::string username = "";
136
+ std::string password = "";
137
+
138
+ if (proxyServer.Get("username").IsString())
139
+ username = proxyServer.Get("username").As<Napi::String>().ToString();
140
+ if (proxyServer.Get("password").IsString())
141
+ password = proxyServer.Get("password").As<Napi::String>().ToString();
142
+
143
+ webSocketConfig.proxyServer = rtc::ProxyServer(type, ip, port, username, password);
144
+ }
145
+
146
+ if (config.Has("protocols"))
147
+ {
148
+ if (!config.Get("protocols").IsArray())
149
+ {
150
+ Napi::TypeError::New(info.Env(), "protocols must be an array").ThrowAsJavaScriptException();
151
+ return;
152
+ }
153
+ Napi::Array protocols = config.Get("protocols").As<Napi::Array>();
154
+ for (uint32_t i = 0; i < protocols.Length(); i++)
155
+ {
156
+ webSocketConfig.protocols.push_back(protocols.Get(i).ToString());
157
+ }
158
+ }
159
+
160
+ if (config.Has("connectionTimeout"))
161
+ {
162
+ if (!config.Get("connectionTimeout").IsNumber())
163
+ {
164
+ Napi::TypeError::New(info.Env(), "connectionTimeout must be a number").ThrowAsJavaScriptException();
165
+ return;
166
+ }
167
+ webSocketConfig.connectionTimeout =
168
+ std::chrono::milliseconds(config.Get("connectionTimeout").ToNumber().Int64Value());
169
+ }
170
+
171
+ if (config.Has("pingInterval"))
172
+ {
173
+ if (!config.Get("pingInterval").IsNumber())
174
+ {
175
+ Napi::TypeError::New(info.Env(), "pingInterval must be a number").ThrowAsJavaScriptException();
176
+ return;
177
+ }
178
+ webSocketConfig.pingInterval = std::chrono::milliseconds(config.Get("pingInterval").ToNumber().Int64Value());
179
+ }
180
+
181
+ if (config.Has("maxOutstandingPings"))
182
+ {
183
+ if (!config.Get("maxOutstandingPings").IsNumber())
184
+ {
185
+ Napi::TypeError::New(info.Env(), "maxOutstandingPings must be a number").ThrowAsJavaScriptException();
186
+ return;
187
+ }
188
+ webSocketConfig.maxOutstandingPings = config.Get("maxOutstandingPings").ToNumber().Int32Value();
189
+ }
190
+
191
+ if (config.Has("caCertificatePemFile"))
192
+ {
193
+ if (!config.Get("caCertificatePemFile").IsString())
194
+ {
195
+ Napi::TypeError::New(info.Env(), "caCertificatePemFile must be a string").ThrowAsJavaScriptException();
196
+ return;
197
+ }
198
+ webSocketConfig.caCertificatePemFile = config.Get("caCertificatePemFile").ToString();
199
+ }
200
+
201
+ if (config.Has("certificatePemFile"))
202
+ {
203
+ if (!config.Get("certificatePemFile").IsString())
204
+ {
205
+ Napi::TypeError::New(info.Env(), "certificatePemFile must be a string").ThrowAsJavaScriptException();
206
+ return;
207
+ }
208
+ webSocketConfig.certificatePemFile = config.Get("certificatePemFile").ToString();
209
+ }
210
+
211
+ if (config.Has("keyPemFile"))
212
+ {
213
+ if (!config.Get("keyPemFile").IsString())
214
+ {
215
+ Napi::TypeError::New(info.Env(), "keyPemFile must be a string").ThrowAsJavaScriptException();
216
+ return;
217
+ }
218
+ webSocketConfig.keyPemFile = config.Get("keyPemFile").ToString();
219
+ }
220
+
221
+ if (config.Has("keyPemPass"))
222
+ {
223
+ if (!config.Get("keyPemPass").IsString())
224
+ {
225
+ Napi::TypeError::New(info.Env(), "keyPemPass must be a string").ThrowAsJavaScriptException();
226
+ return;
227
+ }
228
+ webSocketConfig.keyPemPass = config.Get("keyPemPass").ToString();
229
+ }
230
+
231
+ if (config.Has("maxMessageSize"))
232
+ {
233
+ if (!config.Get("maxMessageSize").IsNumber())
234
+ {
235
+ Napi::TypeError::New(info.Env(), "maxMessageSize must be a number").ThrowAsJavaScriptException();
236
+ return;
237
+ }
238
+ webSocketConfig.maxMessageSize = config.Get("maxMessageSize").ToNumber().Int32Value();
239
+ }
240
+
241
+ // Create WebSocket
242
+ try
243
+ {
244
+ PLOG_DEBUG << "Creating a new WebSocket";
245
+ mWebSocketPtr = std::make_unique<rtc::WebSocket>(webSocketConfig);
246
+ }
247
+ catch (std::exception &ex)
248
+ {
249
+ Napi::Error::New(env, std::string("libdatachannel error while creating WebSocket: ") + ex.what())
250
+ .ThrowAsJavaScriptException();
251
+ return;
252
+ }
253
+
254
+ PLOG_DEBUG << "WebSocket created";
255
+
256
+ // Closed callback must set to trigger cleanup
257
+ mOnClosedCallback =
258
+ std::make_unique<ThreadSafeCallback>(Napi::Function::New(info.Env(), [](const Napi::CallbackInfo &) {}));
259
+
260
+ instances.insert(this);
261
+ }
262
+
263
+ WebSocketWrapper::~WebSocketWrapper()
264
+ {
265
+ PLOG_DEBUG << "Destructor called";
266
+ doClose();
267
+ }
268
+
269
+ void WebSocketWrapper::doClose()
270
+ {
271
+ PLOG_DEBUG << "doClose() called";
272
+ if (mWebSocketPtr)
273
+ {
274
+ PLOG_DEBUG << "Closing...";
275
+ try
276
+ {
277
+ mWebSocketPtr->close();
278
+ mWebSocketPtr.reset();
279
+ }
280
+ catch (std::exception &ex)
281
+ {
282
+ std::cerr << std::string("libdatachannel error while closing WebSocket: ") + ex.what() << std::endl;
283
+ return;
284
+ }
285
+ }
286
+
287
+ mOnOpenCallback.reset();
288
+ mOnErrorCallback.reset();
289
+ mOnBufferedAmountLowCallback.reset();
290
+ mOnMessageCallback.reset();
291
+ }
292
+
293
+ void WebSocketWrapper::doForceClose()
294
+ {
295
+ PLOG_DEBUG << "doForceClose() called";
296
+ if (mWebSocketPtr)
297
+ {
298
+ PLOG_DEBUG << "Force closing...";
299
+ try
300
+ {
301
+ mWebSocketPtr->forceClose();
302
+ mWebSocketPtr.reset();
303
+ }
304
+ catch (std::exception &ex)
305
+ {
306
+ std::cerr << std::string("libdatachannel error while force closing WebSocket: ") + ex.what() << std::endl;
307
+ return;
308
+ }
309
+ }
310
+
311
+ mOnOpenCallback.reset();
312
+ mOnErrorCallback.reset();
313
+ mOnBufferedAmountLowCallback.reset();
314
+ mOnMessageCallback.reset();
315
+ }
316
+
317
+ void WebSocketWrapper::doCleanup()
318
+ {
319
+ PLOG_DEBUG << "doCleanup() called";
320
+ mOnClosedCallback.reset();
321
+ instances.erase(this);
322
+ }
323
+
324
+ void WebSocketWrapper::open(const Napi::CallbackInfo &info)
325
+ {
326
+ PLOG_DEBUG << "open() called";
327
+ Napi::Env env = info.Env();
328
+
329
+ if (!mWebSocketPtr)
330
+ {
331
+ Napi::Error::New(env, "open() called on destroyed WebSocket").ThrowAsJavaScriptException();
332
+ return;
333
+ }
334
+ if (info.Length() < 1 || !info[0].IsString())
335
+ {
336
+ Napi::TypeError::New(env, "url must be string").ThrowAsJavaScriptException();
337
+ return;
338
+ }
339
+
340
+ try
341
+ {
342
+ mWebSocketPtr->open(info[0].As<Napi::String>().ToString());
343
+ }
344
+ catch (std::exception &ex)
345
+ {
346
+ Napi::Error::New(env, std::string("libdatachannel error while opening WebSocket: ") + ex.what())
347
+ .ThrowAsJavaScriptException();
348
+ return;
349
+ }
350
+ }
351
+
352
+ void WebSocketWrapper::close(const Napi::CallbackInfo &info)
353
+ {
354
+ PLOG_DEBUG << "close() called";
355
+ doClose();
356
+ }
357
+
358
+ void WebSocketWrapper::forceClose(const Napi::CallbackInfo &info)
359
+ {
360
+ PLOG_DEBUG << "forceClose() called";
361
+ doForceClose();
362
+ }
363
+
364
+ Napi::Value WebSocketWrapper::sendMessage(const Napi::CallbackInfo &info)
365
+ {
366
+ PLOG_DEBUG << "sendMessage() called";
367
+ if (!mWebSocketPtr)
368
+ {
369
+ Napi::Error::New(info.Env(), "sendMessage() called on destroyed channel").ThrowAsJavaScriptException();
370
+ return info.Env().Null();
371
+ }
372
+
373
+ Napi::Env env = info.Env();
374
+ int length = info.Length();
375
+
376
+ // Allow call with NULL
377
+ if (length < 1 || (!info[0].IsString() && !info[0].IsNull()))
378
+ {
379
+ Napi::TypeError::New(env, "String or Null expected").ThrowAsJavaScriptException();
380
+ return info.Env().Null();
381
+ }
382
+
383
+ try
384
+ {
385
+ return Napi::Boolean::New(info.Env(), mWebSocketPtr->send(info[0].As<Napi::String>().ToString()));
386
+ }
387
+ catch (std::exception &ex)
388
+ {
389
+ Napi::Error::New(env, std::string("libdatachannel error while sending data channel message: ") + ex.what())
390
+ .ThrowAsJavaScriptException();
391
+ return Napi::Boolean::New(info.Env(), false);
392
+ }
393
+ }
394
+
395
+ Napi::Value WebSocketWrapper::sendMessageBinary(const Napi::CallbackInfo &info)
396
+ {
397
+ PLOG_DEBUG << "sendMessageBinary() called";
398
+ if (!mWebSocketPtr)
399
+ {
400
+ Napi::Error::New(info.Env(), "sendMessagBinary() called on destroyed channel").ThrowAsJavaScriptException();
401
+ return info.Env().Null();
402
+ }
403
+
404
+ Napi::Env env = info.Env();
405
+ int length = info.Length();
406
+
407
+ if (length < 1 || !info[0].IsBuffer())
408
+ {
409
+ Napi::TypeError::New(env, "Buffer expected").ThrowAsJavaScriptException();
410
+ return info.Env().Null();
411
+ }
412
+
413
+ try
414
+ {
415
+ Napi::Uint8Array buffer = info[0].As<Napi::Uint8Array>();
416
+ return Napi::Boolean::New(info.Env(), mWebSocketPtr->send((std::byte *)buffer.Data(), buffer.ByteLength()));
417
+ }
418
+ catch (std::exception &ex)
419
+ {
420
+ Napi::Error::New(env, std::string("libdatachannel error while sending data channel message: ") + ex.what())
421
+ .ThrowAsJavaScriptException();
422
+ return Napi::Boolean::New(info.Env(), false);
423
+ }
424
+ }
425
+
426
+ Napi::Value WebSocketWrapper::isOpen(const Napi::CallbackInfo &info)
427
+ {
428
+ PLOG_DEBUG << "isOpen() called";
429
+ Napi::Env env = info.Env();
430
+
431
+ if (!mWebSocketPtr)
432
+ {
433
+ return Napi::Boolean::New(info.Env(), false);
434
+ }
435
+
436
+ try
437
+ {
438
+ return Napi::Boolean::New(info.Env(), mWebSocketPtr->isOpen());
439
+ }
440
+ catch (std::exception &ex)
441
+ {
442
+ Napi::Error::New(env, std::string("libdatachannel error: ") + ex.what()).ThrowAsJavaScriptException();
443
+ return Napi::Boolean::New(info.Env(), false);
444
+ }
445
+ }
446
+
447
+ Napi::Value WebSocketWrapper::bufferedAmount(const Napi::CallbackInfo &info)
448
+ {
449
+ PLOG_DEBUG << "bufferedAmount() called";
450
+ Napi::Env env = info.Env();
451
+
452
+ if (!mWebSocketPtr)
453
+ {
454
+ return Napi::Number::New(info.Env(), 0);
455
+ }
456
+
457
+ try
458
+ {
459
+ return Napi::Number::New(info.Env(), mWebSocketPtr->bufferedAmount());
460
+ }
461
+ catch (std::exception &ex)
462
+ {
463
+ Napi::Error::New(env, std::string("libdatachannel error: ") + ex.what()).ThrowAsJavaScriptException();
464
+ return Napi::Number::New(info.Env(), 0);
465
+ }
466
+ }
467
+
468
+ Napi::Value WebSocketWrapper::maxMessageSize(const Napi::CallbackInfo &info)
469
+ {
470
+ PLOG_DEBUG << "maxMessageSize() called";
471
+ Napi::Env env = info.Env();
472
+
473
+ if (!mWebSocketPtr)
474
+ {
475
+ return Napi::Number::New(info.Env(), 0);
476
+ }
477
+
478
+ try
479
+ {
480
+ return Napi::Number::New(info.Env(), mWebSocketPtr->maxMessageSize());
481
+ }
482
+ catch (std::exception &ex)
483
+ {
484
+ Napi::Error::New(env, std::string("libdatachannel error: ") + ex.what()).ThrowAsJavaScriptException();
485
+ return Napi::Number::New(info.Env(), 0);
486
+ }
487
+ }
488
+
489
+ Napi::Value WebSocketWrapper::remoteAddress(const Napi::CallbackInfo &info)
490
+ {
491
+ PLOG_DEBUG << "remoteAddress() called";
492
+ Napi::Env env = info.Env();
493
+
494
+ if (!mWebSocketPtr)
495
+ {
496
+ return env.Undefined();
497
+ }
498
+
499
+ try
500
+ {
501
+ auto address = mWebSocketPtr->remoteAddress();
502
+ if (address.has_value())
503
+ {
504
+ return Napi::String::New(info.Env(), address.value());
505
+ }
506
+ else
507
+ {
508
+ return env.Undefined();
509
+ }
510
+ }
511
+ catch (std::exception &ex)
512
+ {
513
+ Napi::Error::New(env, std::string("libdatachannel error: ") + ex.what()).ThrowAsJavaScriptException();
514
+ return env.Undefined();
515
+ }
516
+ }
517
+
518
+ Napi::Value WebSocketWrapper::path(const Napi::CallbackInfo &info)
519
+ {
520
+ PLOG_DEBUG << "path() called";
521
+ Napi::Env env = info.Env();
522
+
523
+ if (!mWebSocketPtr)
524
+ {
525
+ return env.Undefined();
526
+ }
527
+
528
+ try
529
+ {
530
+ auto path = mWebSocketPtr->path();
531
+ if (path.has_value())
532
+ {
533
+ return Napi::String::New(info.Env(), path.value());
534
+ }
535
+ else
536
+ {
537
+ return env.Undefined();
538
+ }
539
+ }
540
+ catch (std::exception &ex)
541
+ {
542
+ Napi::Error::New(env, std::string("libdatachannel error: ") + ex.what()).ThrowAsJavaScriptException();
543
+ return env.Undefined();
544
+ }
545
+ }
546
+
547
+ void WebSocketWrapper::setBufferedAmountLowThreshold(const Napi::CallbackInfo &info)
548
+ {
549
+ PLOG_DEBUG << "setBufferedAmountLowThreshold() called";
550
+ if (!mWebSocketPtr)
551
+ {
552
+ Napi::Error::New(info.Env(), "setBufferedAmountLowThreshold() called on destroyed channel")
553
+ .ThrowAsJavaScriptException();
554
+ return;
555
+ }
556
+
557
+ Napi::Env env = info.Env();
558
+ int length = info.Length();
559
+
560
+ if (length < 1 || !info[0].IsNumber())
561
+ {
562
+ Napi::TypeError::New(env, "Number expected").ThrowAsJavaScriptException();
563
+ return;
564
+ }
565
+
566
+ try
567
+ {
568
+ mWebSocketPtr->setBufferedAmountLowThreshold(info[0].ToNumber().Uint32Value());
569
+ }
570
+ catch (std::exception &ex)
571
+ {
572
+ Napi::Error::New(env, std::string("libdatachannel error: ") + ex.what()).ThrowAsJavaScriptException();
573
+ return;
574
+ }
575
+ }
576
+
577
+ void WebSocketWrapper::onOpen(const Napi::CallbackInfo &info)
578
+ {
579
+ PLOG_DEBUG << "new onOpen() called";
580
+ if (!mWebSocketPtr)
581
+ {
582
+ Napi::Error::New(info.Env(), "onOpen() called on destroyed channel").ThrowAsJavaScriptException();
583
+ return;
584
+ }
585
+
586
+ Napi::Env env = info.Env();
587
+ int length = info.Length();
588
+
589
+ if (length < 1 || !info[0].IsFunction())
590
+ {
591
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
592
+ return;
593
+ }
594
+
595
+ // Callback
596
+ mOnOpenCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
597
+
598
+ mWebSocketPtr->onOpen(
599
+ [&]()
600
+ {
601
+ PLOG_DEBUG << "onOpen cb received from rtc";
602
+
603
+ if (mOnOpenCallback)
604
+ mOnOpenCallback->call(
605
+ [this](Napi::Env env, std::vector<napi_value> &args)
606
+ {
607
+ PLOG_DEBUG << "mOnOpenCallback call(1)";
608
+ // Check the WebSocket is not closed
609
+ if (instances.find(this) == instances.end())
610
+ {
611
+ PLOG_DEBUG << "WebSocket not found in instances";
612
+ throw ThreadSafeCallback::CancelException();
613
+ }
614
+
615
+ // This will run in main thread and needs to construct the
616
+ // arguments for the call
617
+ args = {};
618
+ PLOG_DEBUG << "mOnOpenCallback call(2)";
619
+ });
620
+ });
621
+ }
622
+
623
+ void WebSocketWrapper::onClosed(const Napi::CallbackInfo &info)
624
+ {
625
+ PLOG_DEBUG << "onClosed() called";
626
+ if (!mWebSocketPtr)
627
+ {
628
+ Napi::Error::New(info.Env(), "onClosed() called on destroyed WebSocket").ThrowAsJavaScriptException();
629
+ return;
630
+ }
631
+
632
+ Napi::Env env = info.Env();
633
+ int length = info.Length();
634
+
635
+ if (length < 1 || !info[0].IsFunction())
636
+ {
637
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
638
+ return;
639
+ }
640
+
641
+ // Callback
642
+ mOnClosedCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
643
+
644
+ mWebSocketPtr->onClosed(
645
+ [&]()
646
+ {
647
+ PLOG_DEBUG << "onClosed cb received from rtc";
648
+ if (mOnClosedCallback)
649
+ mOnClosedCallback->call(
650
+ [this](Napi::Env env, std::vector<napi_value> &args)
651
+ {
652
+ PLOG_DEBUG << "mOnClosedCallback call";
653
+ // Do not check if the data channel has been closed here
654
+
655
+ // This will run in main thread and needs to construct the
656
+ // arguments for the call
657
+ args = {};
658
+ },
659
+ [this] { doCleanup(); });
660
+ });
661
+ }
662
+
663
+ void WebSocketWrapper::onError(const Napi::CallbackInfo &info)
664
+ {
665
+ PLOG_DEBUG << "onError() called";
666
+ if (!mWebSocketPtr)
667
+ {
668
+ Napi::Error::New(info.Env(), "onError() called on destroyed channel").ThrowAsJavaScriptException();
669
+ return;
670
+ }
671
+
672
+ Napi::Env env = info.Env();
673
+ int length = info.Length();
674
+
675
+ if (length < 1 || !info[0].IsFunction())
676
+ {
677
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
678
+ return;
679
+ }
680
+
681
+ // Callback
682
+ mOnErrorCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
683
+
684
+ mWebSocketPtr->onError(
685
+ [&](std::string error)
686
+ {
687
+ PLOG_DEBUG << "onError cb received from rtc";
688
+ if (mOnErrorCallback)
689
+ mOnErrorCallback->call(
690
+ [this, error = std::move(error)](Napi::Env env, std::vector<napi_value> &args)
691
+ {
692
+ PLOG_DEBUG << "mOnErrorCallback call(1)";
693
+ // Check the data channel is not closed
694
+ if (instances.find(this) == instances.end())
695
+ throw ThreadSafeCallback::CancelException();
696
+
697
+ // This will run in main thread and needs to construct the
698
+ // arguments for the call
699
+ args = {Napi::String::New(env, error)};
700
+ PLOG_DEBUG << "mOnErrorCallback call(2)";
701
+ });
702
+ });
703
+ }
704
+
705
+ void WebSocketWrapper::onBufferedAmountLow(const Napi::CallbackInfo &info)
706
+ {
707
+ PLOG_DEBUG << "onBufferedAmountLow() called";
708
+ if (!mWebSocketPtr)
709
+ {
710
+ Napi::Error::New(info.Env(), "onBufferedAmountLow() called on destroyed channel").ThrowAsJavaScriptException();
711
+ return;
712
+ }
713
+
714
+ Napi::Env env = info.Env();
715
+ int length = info.Length();
716
+
717
+ if (length < 1 || !info[0].IsFunction())
718
+ {
719
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
720
+ return;
721
+ }
722
+
723
+ // Callback
724
+ mOnBufferedAmountLowCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
725
+
726
+ mWebSocketPtr->onBufferedAmountLow(
727
+ [&]()
728
+ {
729
+ PLOG_DEBUG << "onBufferedAmountLow cb received from rtc";
730
+ if (mOnBufferedAmountLowCallback)
731
+ mOnBufferedAmountLowCallback->call(
732
+ [this](Napi::Env env, std::vector<napi_value> &args)
733
+ {
734
+ PLOG_DEBUG << "mOnBufferedAmountLowCallback call(1)";
735
+ // Check the data channel is not closed
736
+ if (instances.find(this) == instances.end())
737
+ throw ThreadSafeCallback::CancelException();
738
+
739
+ // This will run in main thread and needs to construct the
740
+ // arguments for the call
741
+ args = {};
742
+ PLOG_DEBUG << "mOnBufferedAmountLowCallback call(2)";
743
+ });
744
+ });
745
+ }
746
+
747
+ void WebSocketWrapper::onMessage(const Napi::CallbackInfo &info)
748
+ {
749
+ PLOG_DEBUG << "onMessage() called";
750
+ if (!mWebSocketPtr)
751
+ {
752
+ Napi::Error::New(info.Env(), "onMessage() called on destroyed channel").ThrowAsJavaScriptException();
753
+ return;
754
+ }
755
+
756
+ Napi::Env env = info.Env();
757
+ int length = info.Length();
758
+
759
+ if (length < 1 || !info[0].IsFunction())
760
+ {
761
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
762
+ return;
763
+ }
764
+
765
+ // Callback
766
+ mOnMessageCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
767
+
768
+ PLOG_DEBUG << "setting onMessage cb on mWebSocketPtr";
769
+ mWebSocketPtr->onMessage(
770
+ [&](std::variant<rtc::binary, std::string> message)
771
+ {
772
+ PLOG_DEBUG << "onMessage cb received from rtc";
773
+ if (mOnMessageCallback)
774
+ mOnMessageCallback->call(
775
+ [this, message = std::move(message)](Napi::Env env, std::vector<napi_value> &args)
776
+ {
777
+ PLOG_DEBUG << "mOnMessageCallback call(1)";
778
+ // Check the data channel is not closed
779
+ if (instances.find(this) == instances.end())
780
+ throw ThreadSafeCallback::CancelException();
781
+
782
+ // This will run in main thread and needs to construct the
783
+ // arguments for the call
784
+ if (std::holds_alternative<std::string>(message))
785
+ {
786
+ args = {Napi::String::New(env, std::get<std::string>(message))};
787
+ }
788
+ else
789
+ {
790
+ auto bin = std::get<rtc::binary>(std::move(message));
791
+ args = {Napi::Buffer<std::byte>::Copy(env, bin.data(), bin.size())};
792
+ }
793
+ PLOG_DEBUG << "mOnMessageCallback call(2)";
794
+ });
795
+ });
796
+ }