node-datachannel 0.5.5 → 0.6.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.
@@ -76,8 +76,8 @@ jobs:
76
76
  sudo apt update
77
77
  sudo apt install -y g++-aarch64-linux-gnu gcc-aarch64-linux-gnu
78
78
  mkdir sysroot && cd sysroot
79
- wget https://commondatastorage.googleapis.com/chrome-linux-sysroot/toolchain/953c2471bc7e71a788309f6c2d2003e8b703305d/debian_sid_arm64_sysroot.tar.xz
80
- tar xf debian_sid_arm64_sysroot.tar.xz
79
+ wget https://commondatastorage.googleapis.com/chrome-linux-sysroot/toolchain/41a6c8dec4c4304d6509e30cbaf9218dffb4438e/debian_bullseye_arm64_sysroot.tar.xz
80
+ tar xf debian_bullseye_arm64_sysroot.tar.xz
81
81
  echo "ARM64_SYSROOT=$(pwd)" >> $GITHUB_ENV
82
82
  ls -l
83
83
  - name: Use Node.js ${{ matrix.node-version }}
package/CMakeLists.txt CHANGED
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.15)
2
2
  cmake_policy(SET CMP0091 NEW)
3
3
  cmake_policy(SET CMP0042 NEW)
4
4
 
5
- project(node_datachannel VERSION 0.5.5)
5
+ project(node_datachannel VERSION 0.6.0)
6
6
 
7
7
  # -Dnapi_build_version=8
8
8
  add_definitions(-DNAPI_VERSION=8)
@@ -20,14 +20,8 @@ if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Android")
20
20
  set(CMAKE_C_FLAGS "-Wno-error=unused-but-set-variable -Wno-error=strict-prototypes")
21
21
  endif()
22
22
 
23
- # Got linker error for arm64
24
- # /usr/lib/gcc-cross/aarch64-linux-gnu/9/../../../../aarch64-linux-gnu/bin/ld: ../sysroot/usr/lib/aarch64-linux-gnu/libcrypto.a(sha1-armv8.o): relocation R_AARCH64_PREL64 against symbol `OPENSSL_armcap_P' which may bind externally can not be used when making a shared object; recompile with -fPIC
25
- # ../sysroot/usr/lib/aarch64-linux-gnu/libcrypto.a(sha1-armv8.o): in function `sha1_block_armv8':
26
- # (.text+0x1240): dangerous relocation: unsupported relocation
27
- if(NOT ${NODE_ARCH} STREQUAL "arm64")
28
- set(OPENSSL_USE_STATIC_LIBS TRUE)
29
- find_package(OpenSSL REQUIRED)
30
- endif()
23
+ set(OPENSSL_USE_STATIC_LIBS TRUE)
24
+ find_package(OpenSSL REQUIRED)
31
25
 
32
26
  include(FetchContent)
33
27
 
@@ -39,7 +33,7 @@ FetchContent_Declare(
39
33
  )
40
34
 
41
35
  option(NO_MEDIA "Disable media transport support in libdatachannel" OFF)
42
- option(NO_WEBSOCKET "Disable WebSocket support in libdatachannel" ON)
36
+ option(NO_WEBSOCKET "Disable WebSocket support in libdatachannel" OFF)
43
37
 
44
38
  FetchContent_GetProperties(libdatachannel)
45
39
 
@@ -58,6 +52,8 @@ add_library(${PROJECT_NAME} SHARED
58
52
  src/data-channel-wrapper.cpp
59
53
  src/peer-connection-wrapper.cpp
60
54
  src/thread-safe-callback.cpp
55
+ src/web-socket-wrapper.cpp
56
+ src/web-socket-server-wrapper.cpp
61
57
  src/main.cpp
62
58
  ${CMAKE_JS_SRC}
63
59
  )
@@ -3832,9 +3832,9 @@
3832
3832
  }
3833
3833
  },
3834
3834
  "node_modules/follow-redirects": {
3835
- "version": "1.15.4",
3836
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
3837
- "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
3835
+ "version": "1.15.6",
3836
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
3837
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
3838
3838
  "dev": true,
3839
3839
  "funding": [
3840
3840
  {
@@ -11877,9 +11877,9 @@
11877
11877
  }
11878
11878
  },
11879
11879
  "follow-redirects": {
11880
- "version": "1.15.4",
11881
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
11882
- "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
11880
+ "version": "1.15.6",
11881
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
11882
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
11883
11883
  "dev": true
11884
11884
  },
11885
11885
  "foreground-child": {
@@ -20,3 +20,15 @@ Use the following gstreamer demo pipeline to display the traffic
20
20
  ```
21
21
  $ gst-launch-1.0 udpsrc address=127.0.0.1 port=5000 caps="application/x-rtp" ! queue ! rtph264depay ! video/x-h264,stream-format=byte-stream ! queue ! avdec_h264 ! queue ! autovideosink
22
22
  ```
23
+
24
+ For saving the stream to a file, use the following pipeline (mp4):
25
+
26
+ ```
27
+ gst-launch-1.0 -e udpsrc address=127.0.0.1 port=5000 caps="application/x-rtp" ! queue ! rtph264depay ! h264parse ! mp4mux ! filesink location=out.mp4
28
+ ```
29
+
30
+ ## Requirements
31
+
32
+ - GStreamer 1.0
33
+ - gstreamer1.0-libav (sudo apt-get install gstreamer1.0-libav)
34
+ - h264enc (sudo apt-get install h264enc)
package/lib/index.d.ts CHANGED
@@ -189,11 +189,58 @@ export class Track {
189
189
  onMessage(cb: (msg: Buffer) => void): void;
190
190
  }
191
191
 
192
- export class DataChannel {
192
+ export interface Channel {
193
193
  close(): void;
194
+ sendMessage(msg: string): boolean;
195
+ sendMessageBinary(buffer: Uint8Array): boolean;
196
+ isOpen(): boolean;
197
+ bufferedAmount(): number;
198
+ maxMessageSize(): number;
199
+ setBufferedAmountLowThreshold(newSize: number): void;
200
+ onOpen(cb: () => void): void;
201
+ onClosed(cb: () => void): void;
202
+ onError(cb: (err: string) => void): void;
203
+ onBufferedAmountLow(cb: () => void): void;
204
+ onMessage(cb: (msg: string | Buffer) => void): void;
205
+ }
206
+ export class DataChannel implements Channel {
194
207
  getLabel(): string;
195
208
  getId(): number;
196
209
  getProtocol(): string;
210
+
211
+ // Channel implementation
212
+ close(): void;
213
+ sendMessage(msg: string): boolean;
214
+ sendMessageBinary(buffer: Uint8Array): boolean;
215
+ isOpen(): boolean;
216
+ bufferedAmount(): number;
217
+ maxMessageSize(): number;
218
+ setBufferedAmountLowThreshold(newSize: number): void;
219
+ onOpen(cb: () => void): void;
220
+ onClosed(cb: () => void): void;
221
+ onError(cb: (err: string) => void): void;
222
+ onBufferedAmountLow(cb: () => void): void;
223
+ onMessage(cb: (msg: string | Buffer) => void): void;
224
+ }
225
+
226
+ export interface WebSocketConfiguration {
227
+ disableTlsVerification?: boolean; // default = false, if true, don't verify the TLS certificate
228
+ proxyServer?: ProxyServer; // only non-authenticated http supported for now
229
+ protocols?: string[];
230
+ connectionTimeout?: number; // miliseconds, zero to disable
231
+ pingInterval?: number; // millisecondrs, zero to disable
232
+ maxOutstandingPings?: number;
233
+ caCertificatePemFile?: string;
234
+ certificatePemFile?: string;
235
+ keyPemFile?: string;
236
+ keyPemPass?: string;
237
+ maxMessageSize: number;
238
+ }
239
+ export class WebSocket implements Channel {
240
+ constructor(config?: WebSocketConfiguration);
241
+
242
+ // Channel implementation
243
+ close(): void;
197
244
  sendMessage(msg: string): boolean;
198
245
  sendMessageBinary(buffer: Uint8Array): boolean;
199
246
  isOpen(): boolean;
@@ -207,6 +254,24 @@ export class DataChannel {
207
254
  onMessage(cb: (msg: string | Buffer) => void): void;
208
255
  }
209
256
 
257
+ export interface WebSocketServerConfiguration {
258
+ port?: number; // default 8080
259
+ enableTls?: boolean; // default = false;
260
+ certificatePemFile?: string;
261
+ keyPemFile?: string;
262
+ keyPemPass?: string;
263
+ bindAddress?: string;
264
+ connectionTimeout?: number; // milliseconds
265
+ maxMessageSize?: number;
266
+ }
267
+
268
+ export class WebSocketServer {
269
+ constructor(config?: WebSocketServerConfiguration);
270
+ port(): number;
271
+ stop(): void;
272
+ onClient(cb: (ws: WebSocket) => void): void;
273
+ }
274
+
210
275
  export class PeerConnection {
211
276
  constructor(peerName: string, config: RtcConfig);
212
277
  close(): void;
package/lib/index.js CHANGED
@@ -16,6 +16,8 @@ const {
16
16
  Audio,
17
17
  DataChannel,
18
18
  PeerConnection,
19
+ WebSocket,
20
+ WebSocketServer,
19
21
  } = nodeDataChannel;
20
22
 
21
23
  export {
@@ -29,6 +31,8 @@ export {
29
31
  Audio,
30
32
  DataChannel,
31
33
  PeerConnection,
34
+ WebSocket,
35
+ WebSocketServer,
32
36
  // Extra exports
33
37
  DataChannelStream,
34
38
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-datachannel",
3
- "version": "0.5.5",
3
+ "version": "0.6.0",
4
4
  "description": "libdatachannel node bindings",
5
5
  "type": "module",
6
6
  "exports": {
@@ -44,7 +44,8 @@
44
44
  "p2p",
45
45
  "peer-to-peer",
46
46
  "datachannel",
47
- "data channel"
47
+ "data channel",
48
+ "websocket"
48
49
  ],
49
50
  "contributors": [
50
51
  {
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 "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,6 +118,8 @@ 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)
@@ -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
+ }
@@ -0,0 +1,43 @@
1
+ #ifndef WEB_SOCKET_SERVER_WRAPPER_H
2
+ #define WEB_SOCKET_SERVER_WRAPPER_H
3
+
4
+ #include <iostream>
5
+ #include <string>
6
+ #include <variant>
7
+ #include <memory>
8
+ #include <unordered_set>
9
+
10
+ #include <napi.h>
11
+ #include <rtc/rtc.hpp>
12
+
13
+ #include "web-socket-wrapper.h"
14
+ #include "thread-safe-callback.h"
15
+
16
+ class WebSocketServerWrapper : public Napi::ObjectWrap<WebSocketServerWrapper>
17
+ {
18
+ public:
19
+ static Napi::FunctionReference constructor;
20
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
21
+ WebSocketServerWrapper(const Napi::CallbackInfo &info);
22
+ ~WebSocketServerWrapper();
23
+
24
+ // Functions
25
+
26
+ void stop(const Napi::CallbackInfo &info);
27
+ Napi::Value port(const Napi::CallbackInfo &info);
28
+
29
+ // Callbacks
30
+ void onClient(const Napi::CallbackInfo &info);
31
+
32
+ // Close all existing WebSocketServers
33
+ static void StopAll();
34
+
35
+ private:
36
+ static std::unordered_set<WebSocketServerWrapper*> instances;
37
+ std::unique_ptr<rtc::WebSocketServer> mWebSocketServerPtr = nullptr;
38
+ std::unique_ptr<ThreadSafeCallback> mOnClientCallback = nullptr;
39
+
40
+ void doStop();
41
+ };
42
+
43
+ #endif // WEB_SOCKET_SERVER_WRAPPER_H