node-datachannel 0.5.4 → 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.
- 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/lib/index.d.ts +66 -1
- package/lib/index.js +4 -0
- package/package.json +3 -2
- package/polyfill/RTCPeerConnection.js +1 -0
- package/src/main.cpp +4 -0
- package/src/rtc-wrapper.cpp +4 -0
- package/src/web-socket-server-wrapper.cpp +265 -0
- package/src/web-socket-server-wrapper.h +43 -0
- package/src/web-socket-wrapper.cpp +651 -0
- package/src/web-socket-wrapper.h +58 -0
- package/test/polyfill-connectivity.js +0 -1
- package/test/test.js +67 -0
- package/test/websockets.js +48 -0
|
@@ -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/
|
|
80
|
-
tar xf
|
|
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
|
+
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
|
-
|
|
24
|
-
|
|
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"
|
|
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.
|
|
3836
|
-
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.
|
|
3837
|
-
"integrity": "sha512-
|
|
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.
|
|
11881
|
-
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.
|
|
11882
|
-
"integrity": "sha512-
|
|
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": {
|
package/examples/media/README.md
CHANGED
|
@@ -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
|
|
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.
|
|
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
|
|
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,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
|