node-datachannel 0.27.0 → 0.29.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.
Files changed (104) hide show
  1. package/.eslintrc.json +25 -25
  2. package/.prettierignore +7 -0
  3. package/.prettierrc +1 -0
  4. package/API.md +34 -4
  5. package/BULDING.md +12 -2
  6. package/CMakeLists.txt +10 -4
  7. package/README.md +22 -18
  8. package/dist/cjs/index.cjs.map +1 -1
  9. package/dist/cjs/lib/datachannel-stream.cjs +13 -13
  10. package/dist/cjs/lib/datachannel-stream.cjs.map +1 -1
  11. package/dist/cjs/lib/index.cjs +4 -1
  12. package/dist/cjs/lib/index.cjs.map +1 -1
  13. package/dist/cjs/lib/websocket-server.cjs.map +1 -1
  14. package/dist/cjs/lib/websocket.cjs.map +1 -1
  15. package/dist/cjs/polyfill/Events.cjs +52 -3
  16. package/dist/cjs/polyfill/Events.cjs.map +1 -1
  17. package/dist/cjs/polyfill/Exception.cjs.map +1 -1
  18. package/dist/cjs/polyfill/RTCCertificate.cjs +5 -2
  19. package/dist/cjs/polyfill/RTCCertificate.cjs.map +1 -1
  20. package/dist/cjs/polyfill/RTCDataChannel.cjs +43 -22
  21. package/dist/cjs/polyfill/RTCDataChannel.cjs.map +1 -1
  22. package/dist/cjs/polyfill/RTCDtlsTransport.cjs +6 -5
  23. package/dist/cjs/polyfill/RTCDtlsTransport.cjs.map +1 -1
  24. package/dist/cjs/polyfill/RTCError.cjs +9 -2
  25. package/dist/cjs/polyfill/RTCError.cjs.map +1 -1
  26. package/dist/cjs/polyfill/RTCIceCandidate.cjs +13 -8
  27. package/dist/cjs/polyfill/RTCIceCandidate.cjs.map +1 -1
  28. package/dist/cjs/polyfill/RTCIceTransport.cjs +26 -16
  29. package/dist/cjs/polyfill/RTCIceTransport.cjs.map +1 -1
  30. package/dist/cjs/polyfill/RTCPeerConnection.cjs +80 -70
  31. package/dist/cjs/polyfill/RTCPeerConnection.cjs.map +1 -1
  32. package/dist/cjs/polyfill/RTCSctpTransport.cjs +9 -11
  33. package/dist/cjs/polyfill/RTCSctpTransport.cjs.map +1 -1
  34. package/dist/cjs/polyfill/RTCSessionDescription.cjs +10 -2
  35. package/dist/cjs/polyfill/RTCSessionDescription.cjs.map +1 -1
  36. package/dist/cjs/polyfill/index.cjs.map +1 -1
  37. package/dist/esm/index.mjs.map +1 -1
  38. package/dist/esm/lib/datachannel-stream.mjs.map +1 -1
  39. package/dist/esm/lib/index.mjs +4 -2
  40. package/dist/esm/lib/index.mjs.map +1 -1
  41. package/dist/esm/lib/websocket-server.mjs.map +1 -1
  42. package/dist/esm/lib/websocket.mjs.map +1 -1
  43. package/dist/esm/polyfill/Events.mjs +52 -4
  44. package/dist/esm/polyfill/Events.mjs.map +1 -1
  45. package/dist/esm/polyfill/Exception.mjs.map +1 -1
  46. package/dist/esm/polyfill/RTCCertificate.mjs +5 -2
  47. package/dist/esm/polyfill/RTCCertificate.mjs.map +1 -1
  48. package/dist/esm/polyfill/RTCDataChannel.mjs +43 -22
  49. package/dist/esm/polyfill/RTCDataChannel.mjs.map +1 -1
  50. package/dist/esm/polyfill/RTCDtlsTransport.mjs +6 -5
  51. package/dist/esm/polyfill/RTCDtlsTransport.mjs.map +1 -1
  52. package/dist/esm/polyfill/RTCError.mjs +9 -2
  53. package/dist/esm/polyfill/RTCError.mjs.map +1 -1
  54. package/dist/esm/polyfill/RTCIceCandidate.mjs +13 -8
  55. package/dist/esm/polyfill/RTCIceCandidate.mjs.map +1 -1
  56. package/dist/esm/polyfill/RTCIceTransport.mjs +26 -17
  57. package/dist/esm/polyfill/RTCIceTransport.mjs.map +1 -1
  58. package/dist/esm/polyfill/RTCPeerConnection.mjs +80 -70
  59. package/dist/esm/polyfill/RTCPeerConnection.mjs.map +1 -1
  60. package/dist/esm/polyfill/RTCSctpTransport.mjs +9 -11
  61. package/dist/esm/polyfill/RTCSctpTransport.mjs.map +1 -1
  62. package/dist/esm/polyfill/RTCSessionDescription.mjs +10 -2
  63. package/dist/esm/polyfill/RTCSessionDescription.mjs.map +1 -1
  64. package/dist/esm/polyfill/index.mjs.map +1 -1
  65. package/dist/types/lib/index.d.ts +14 -3
  66. package/dist/types/lib/types.d.ts +30 -7
  67. package/dist/types/polyfill/Events.d.ts +4 -1
  68. package/dist/types/polyfill/RTCCertificate.d.ts +1 -0
  69. package/dist/types/polyfill/RTCDataChannel.d.ts +6 -6
  70. package/dist/types/polyfill/RTCDtlsTransport.d.ts +5 -4
  71. package/dist/types/polyfill/RTCError.d.ts +1 -0
  72. package/dist/types/polyfill/RTCIceCandidate.d.ts +1 -1
  73. package/dist/types/polyfill/RTCIceTransport.d.ts +17 -8
  74. package/dist/types/polyfill/RTCPeerConnection.d.ts +22 -11
  75. package/dist/types/polyfill/RTCSctpTransport.d.ts +4 -3
  76. package/dist/types/polyfill/RTCSessionDescription.d.ts +1 -0
  77. package/jest.config.ts +8 -8
  78. package/package.json +6 -3
  79. package/rollup.config.mjs +1 -1
  80. package/src/cpp/data-channel-wrapper.cpp +11 -0
  81. package/src/cpp/ice-udp-mux-listener-wrapper.cpp +150 -0
  82. package/src/cpp/ice-udp-mux-listener-wrapper.h +43 -0
  83. package/src/cpp/main.cpp +2 -0
  84. package/src/cpp/peer-connection-wrapper.cpp +68 -2
  85. package/src/cpp/peer-connection-wrapper.h +1 -0
  86. package/src/index.ts +0 -1
  87. package/src/lib/datachannel-stream.ts +83 -84
  88. package/src/lib/index.ts +175 -139
  89. package/src/lib/types.ts +122 -71
  90. package/src/lib/websocket-server.ts +25 -25
  91. package/src/lib/websocket.ts +19 -19
  92. package/src/polyfill/Events.ts +71 -18
  93. package/src/polyfill/Exception.ts +24 -24
  94. package/src/polyfill/README.md +8 -8
  95. package/src/polyfill/RTCCertificate.ts +16 -12
  96. package/src/polyfill/RTCDataChannel.ts +216 -191
  97. package/src/polyfill/RTCDtlsTransport.ts +39 -39
  98. package/src/polyfill/RTCError.ts +63 -56
  99. package/src/polyfill/RTCIceCandidate.ts +120 -116
  100. package/src/polyfill/RTCIceTransport.ts +72 -64
  101. package/src/polyfill/RTCPeerConnection.ts +436 -420
  102. package/src/polyfill/RTCSctpTransport.ts +44 -47
  103. package/src/polyfill/RTCSessionDescription.ts +26 -17
  104. package/src/polyfill/index.ts +22 -22
@@ -0,0 +1,150 @@
1
+ #include "ice-udp-mux-listener-wrapper.h"
2
+
3
+ #include "plog/Log.h"
4
+
5
+ #include <cctype>
6
+ #include <sstream>
7
+
8
+ Napi::FunctionReference IceUdpMuxListenerWrapper::constructor = Napi::FunctionReference();
9
+ std::unordered_set<IceUdpMuxListenerWrapper *> IceUdpMuxListenerWrapper::instances;
10
+
11
+ void IceUdpMuxListenerWrapper::StopAll()
12
+ {
13
+ PLOG_DEBUG << "IceUdpMuxListenerWrapper StopAll() called";
14
+ auto copy(instances);
15
+ for (auto inst : copy)
16
+ inst->doCleanup();
17
+ }
18
+
19
+ Napi::Object IceUdpMuxListenerWrapper::Init(Napi::Env env, Napi::Object exports)
20
+ {
21
+ Napi::HandleScope scope(env);
22
+
23
+ Napi::Function func = DefineClass(
24
+ env,
25
+ "IceUdpMuxListener",
26
+ {
27
+ InstanceMethod("stop", &IceUdpMuxListenerWrapper::stop),
28
+ InstanceMethod("onUnhandledStunRequest", &IceUdpMuxListenerWrapper::onUnhandledStunRequest),
29
+ InstanceMethod("port", &IceUdpMuxListenerWrapper::port),
30
+ InstanceMethod("address", &IceUdpMuxListenerWrapper::address)
31
+ });
32
+
33
+ // If this is not the first call, we don't want to reassign the constructor (hot-reload problem)
34
+ if(constructor.IsEmpty())
35
+ {
36
+ constructor = Napi::Persistent(func);
37
+ constructor.SuppressDestruct();
38
+ }
39
+
40
+ exports.Set("IceUdpMuxListener", func);
41
+ return exports;
42
+ }
43
+
44
+ IceUdpMuxListenerWrapper::IceUdpMuxListenerWrapper(const Napi::CallbackInfo &info) : Napi::ObjectWrap<IceUdpMuxListenerWrapper>(info)
45
+ {
46
+ PLOG_DEBUG << "IceUdpMuxListenerWrapper Constructor called";
47
+ Napi::Env env = info.Env();
48
+ int length = info.Length();
49
+
50
+ // We expect (Number, String?) as param
51
+ if (length > 0 && info[0].IsNumber()) {
52
+ // Port
53
+ mPort = info[0].As<Napi::Number>().ToNumber().Uint32Value();
54
+ } else {
55
+ Napi::TypeError::New(env, "Port (Number) and optional Address (String) expected").ThrowAsJavaScriptException();
56
+ return;
57
+ }
58
+
59
+ if (length > 1 && info[1].IsString()) {
60
+ // Address
61
+ mAddress = info[1].As<Napi::String>().ToString();
62
+ }
63
+
64
+ iceUdpMuxListenerPtr = std::make_unique<rtc::IceUdpMuxListener>(mPort, mAddress);
65
+ instances.insert(this);
66
+ }
67
+
68
+ IceUdpMuxListenerWrapper::~IceUdpMuxListenerWrapper()
69
+ {
70
+ PLOG_DEBUG << "IceUdpMuxListenerWrapper Destructor called";
71
+ doCleanup();
72
+ }
73
+
74
+ void IceUdpMuxListenerWrapper::doCleanup()
75
+ {
76
+ PLOG_DEBUG << "IceUdpMuxListenerWrapper::doCleanup() called";
77
+
78
+ if (iceUdpMuxListenerPtr)
79
+ {
80
+ iceUdpMuxListenerPtr->stop();
81
+ iceUdpMuxListenerPtr.reset();
82
+ }
83
+
84
+ mOnUnhandledStunRequestCallback.reset();
85
+ instances.erase(this);
86
+ }
87
+
88
+ Napi::Value IceUdpMuxListenerWrapper::port(const Napi::CallbackInfo &info)
89
+ {
90
+ Napi::Env env = info.Env();
91
+
92
+ return Napi::Number::New(env, mPort);
93
+ }
94
+
95
+ Napi::Value IceUdpMuxListenerWrapper::address(const Napi::CallbackInfo &info)
96
+ {
97
+ Napi::Env env = info.Env();
98
+
99
+ if (!mAddress.has_value()) {
100
+ return env.Undefined();
101
+ }
102
+
103
+ return Napi::String::New(env, mAddress.value());
104
+ }
105
+
106
+ void IceUdpMuxListenerWrapper::stop(const Napi::CallbackInfo &info)
107
+ {
108
+ PLOG_DEBUG << "IceUdpMuxListenerWrapper::stop() called";
109
+ doCleanup();
110
+ }
111
+
112
+ void IceUdpMuxListenerWrapper::onUnhandledStunRequest(const Napi::CallbackInfo &info)
113
+ {
114
+ PLOG_DEBUG << "IceUdpMuxListenerWrapper::onUnhandledStunRequest() called";
115
+ Napi::Env env = info.Env();
116
+ int length = info.Length();
117
+
118
+ if (!iceUdpMuxListenerPtr)
119
+ {
120
+ Napi::Error::New(env, "IceUdpMuxListenerWrapper::onUnhandledStunRequest() called on destroyed IceUdpMuxListener").ThrowAsJavaScriptException();
121
+ return;
122
+ }
123
+
124
+ if (length < 1 || !info[0].IsFunction())
125
+ {
126
+ Napi::TypeError::New(env, "Function expected").ThrowAsJavaScriptException();
127
+ return;
128
+ }
129
+
130
+ // Callback
131
+ mOnUnhandledStunRequestCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>());
132
+
133
+ iceUdpMuxListenerPtr->OnUnhandledStunRequest([&](rtc::IceUdpMuxRequest request)
134
+ {
135
+ PLOG_DEBUG << "IceUdpMuxListenerWrapper::onUnhandledStunRequest() IceUdpMuxCallback call(1)";
136
+
137
+ if (mOnUnhandledStunRequestCallback) {
138
+ mOnUnhandledStunRequestCallback->call([request = std::move(request)](Napi::Env env, std::vector<napi_value> &args) {
139
+ Napi::Object reqObj = Napi::Object::New(env);
140
+ reqObj.Set("ufrag", request.remoteUfrag.c_str());
141
+ reqObj.Set("host", request.remoteAddress.c_str());
142
+ reqObj.Set("port", request.remotePort);
143
+
144
+ args = {reqObj};
145
+ });
146
+ }
147
+
148
+ PLOG_DEBUG << "IceUdpMuxListenerWrapper::onUnhandledStunRequest() IceUdpMuxCallback call(2)";
149
+ });
150
+ }
@@ -0,0 +1,43 @@
1
+ #ifndef ICE_UDP_MUX_LISTENER_WRAPPER_H
2
+ #define ICE_UDP_MUX_LISTENER_WRAPPER_H
3
+
4
+ #include <napi.h>
5
+ #include <rtc/rtc.hpp>
6
+ #include <unordered_set>
7
+
8
+ #include "thread-safe-callback.h"
9
+
10
+ class IceUdpMuxListenerWrapper : public Napi::ObjectWrap<IceUdpMuxListenerWrapper>
11
+ {
12
+ public:
13
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
14
+ IceUdpMuxListenerWrapper(const Napi::CallbackInfo &info);
15
+ ~IceUdpMuxListenerWrapper();
16
+
17
+ // Functions
18
+ void stop(const Napi::CallbackInfo &info);
19
+ void onUnhandledStunRequest(const Napi::CallbackInfo &info);
20
+
21
+ // Stop listening on all ports
22
+ static void StopAll();
23
+
24
+ // Properties
25
+ Napi::Value port(const Napi::CallbackInfo &info);
26
+ Napi::Value address(const Napi::CallbackInfo &info);
27
+ Napi::Value unhandledStunRequestCallback(const Napi::CallbackInfo &info);
28
+
29
+ // Callback Ptrs
30
+ std::unique_ptr<ThreadSafeCallback> mOnUnhandledStunRequestCallback = nullptr;
31
+
32
+ private:
33
+ static Napi::FunctionReference constructor;
34
+ static std::unordered_set<IceUdpMuxListenerWrapper *> instances;
35
+
36
+ void doCleanup();
37
+
38
+ std::optional<std::string> mAddress;
39
+ uint16_t mPort;
40
+ std::unique_ptr<rtc::IceUdpMuxListener> iceUdpMuxListenerPtr = nullptr;
41
+ };
42
+
43
+ #endif // ICE_UDP_MUX_LISTENER_WRAPPER_H
package/src/cpp/main.cpp CHANGED
@@ -2,6 +2,7 @@
2
2
  #include "rtc-wrapper.h"
3
3
  #include "peer-connection-wrapper.h"
4
4
  #include "data-channel-wrapper.h"
5
+ #include "ice-udp-mux-listener-wrapper.h"
5
6
 
6
7
  #if RTC_ENABLE_MEDIA == 1
7
8
  #include "media-rtcpreceivingsession-wrapper.h"
@@ -27,6 +28,7 @@ Napi::Object InitAll(Napi::Env env, Napi::Object exports)
27
28
  #endif
28
29
 
29
30
  DataChannelWrapper::Init(env, exports);
31
+ IceUdpMuxListenerWrapper::Init(env, exports);
30
32
  PeerConnectionWrapper::Init(env, exports);
31
33
 
32
34
  #if RTC_ENABLE_WEBSOCKET == 1
@@ -44,6 +44,7 @@ Napi::Object PeerConnectionWrapper::Init(Napi::Env env, Napi::Object exports)
44
44
  InstanceMethod("setRemoteDescription", &PeerConnectionWrapper::setRemoteDescription),
45
45
  InstanceMethod("localDescription", &PeerConnectionWrapper::localDescription),
46
46
  InstanceMethod("remoteDescription", &PeerConnectionWrapper::remoteDescription),
47
+ InstanceMethod("remoteFingerprint", &PeerConnectionWrapper::remoteFingerprint),
47
48
  InstanceMethod("addRemoteCandidate", &PeerConnectionWrapper::addRemoteCandidate),
48
49
  InstanceMethod("createDataChannel", &PeerConnectionWrapper::createDataChannel),
49
50
 
@@ -220,6 +221,10 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N
220
221
  if (config.Get("disableAutoNegotiation").IsBoolean())
221
222
  rtcConfig.disableAutoNegotiation = config.Get("disableAutoNegotiation").As<Napi::Boolean>();
222
223
 
224
+ // disableAutoGathering option
225
+ if (config.Get("disableAutoGathering").IsBoolean())
226
+ rtcConfig.disableAutoGathering = config.Get("disableAutoGathering").As<Napi::Boolean>();
227
+
223
228
  // forceMediaTransport option
224
229
  if (config.Get("forceMediaTransport").IsBoolean())
225
230
  rtcConfig.forceMediaTransport = config.Get("forceMediaTransport").As<Napi::Boolean>();
@@ -257,6 +262,17 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N
257
262
  rtcConfig.disableFingerprintVerification = config.Get("disableFingerprintVerification").As<Napi::Boolean>();
258
263
  }
259
264
 
265
+ // Specify certificate to use if set
266
+ if (config.Get("certificatePemFile").IsString()) {
267
+ rtcConfig.certificatePemFile = config.Get("certificatePemFile").As<Napi::String>().ToString();
268
+ }
269
+ if (config.Get("keyPemFile").IsString()) {
270
+ rtcConfig.keyPemFile = config.Get("keyPemFile").As<Napi::String>().ToString();
271
+ }
272
+ if (config.Get("keyPemPass").IsString()) {
273
+ rtcConfig.keyPemPass = config.Get("keyPemPass").As<Napi::String>().ToString();
274
+ }
275
+
260
276
  // Create peer-connection
261
277
  try
262
278
  {
@@ -337,6 +353,7 @@ void PeerConnectionWrapper::setLocalDescription(const Napi::CallbackInfo &info)
337
353
  }
338
354
 
339
355
  rtc::Description::Type type = rtc::Description::Type::Unspec;
356
+ rtc::LocalDescriptionInit init;
340
357
 
341
358
  // optional
342
359
  if (length > 0)
@@ -362,7 +379,29 @@ void PeerConnectionWrapper::setLocalDescription(const Napi::CallbackInfo &info)
362
379
  type = rtc::Description::Type::Rollback;
363
380
  }
364
381
 
365
- mRtcPeerConnPtr->setLocalDescription(type);
382
+ // optional
383
+ if (length > 1)
384
+ {
385
+ PLOG_DEBUG << "setLocalDescription() called with LocalDescriptionInit";
386
+
387
+ if (info[1].IsObject())
388
+ {
389
+ PLOG_DEBUG << "setLocalDescription() called with LocalDescriptionInit as object";
390
+ Napi::Object obj = info[1].As<Napi::Object>();
391
+
392
+ if (obj.Get("iceUfrag").IsString()) {
393
+ PLOG_DEBUG << "setLocalDescription() has ufrag";
394
+ init.iceUfrag = obj.Get("iceUfrag").As<Napi::String>();
395
+ }
396
+
397
+ if (obj.Get("icePwd").IsString()) {
398
+ PLOG_DEBUG << "setLocalDescription() has password";
399
+ init.icePwd = obj.Get("icePwd").As<Napi::String>();
400
+ }
401
+ }
402
+ }
403
+
404
+ mRtcPeerConnPtr->setLocalDescription(type, init);
366
405
  }
367
406
 
368
407
  void PeerConnectionWrapper::setRemoteDescription(const Napi::CallbackInfo &info)
@@ -1008,7 +1047,34 @@ Napi::Value PeerConnectionWrapper::maxMessageSize(const Napi::CallbackInfo &info
1008
1047
 
1009
1048
  try
1010
1049
  {
1011
- return Napi::Number::New(env, mRtcPeerConnPtr->remoteMaxMessageSize());
1050
+ return Napi::Array::New(env, mRtcPeerConnPtr->remoteMaxMessageSize());
1051
+ }
1052
+ catch (std::exception &ex)
1053
+ {
1054
+ Napi::Error::New(env, std::string("libdatachannel error: ") + ex.what()).ThrowAsJavaScriptException();
1055
+ return Napi::Number::New(info.Env(), 0);
1056
+ }
1057
+ }
1058
+
1059
+ Napi::Value PeerConnectionWrapper::remoteFingerprint(const Napi::CallbackInfo &info)
1060
+ {
1061
+ PLOG_DEBUG << "remoteFingerprints() called";
1062
+ Napi::Env env = info.Env();
1063
+
1064
+ if (!mRtcPeerConnPtr)
1065
+ {
1066
+ return Napi::Number::New(info.Env(), 0);
1067
+ }
1068
+
1069
+ try
1070
+ {
1071
+ auto fingerprint = mRtcPeerConnPtr->remoteFingerprint();
1072
+
1073
+ Napi::Object fingerprintObject = Napi::Object::New(env);
1074
+ fingerprintObject.Set("value", fingerprint.value);
1075
+ fingerprintObject.Set("algorithm", rtc::CertificateFingerprint::AlgorithmIdentifier(fingerprint.algorithm));
1076
+
1077
+ return fingerprintObject;
1012
1078
  }
1013
1079
  catch (std::exception &ex)
1014
1080
  {
@@ -38,6 +38,7 @@ public:
38
38
  Napi::Value iceState(const Napi::CallbackInfo &info);
39
39
  Napi::Value signalingState(const Napi::CallbackInfo &info);
40
40
  Napi::Value gatheringState(const Napi::CallbackInfo &info);
41
+ Napi::Value remoteFingerprint(const Napi::CallbackInfo &info);
41
42
 
42
43
  // Callbacks
43
44
  void onLocalDescription(const Napi::CallbackInfo &info);
package/src/index.ts CHANGED
@@ -5,6 +5,5 @@
5
5
  import n from './lib/index';
6
6
  import p from './polyfill/index';
7
7
 
8
-
9
8
  export const nodeDataChannel = n;
10
9
  export const polyfill = p;
@@ -10,92 +10,91 @@ import * as stream from 'stream';
10
10
  * the protocol level, and is preserved here throughout.
11
11
  */
12
12
  export default class DataChannelStream extends stream.Duplex {
13
- private _rawChannel: any;
14
- private _readActive: boolean;
15
-
16
-
17
- constructor(rawChannel: any, streamOptions?: Omit<stream.DuplexOptions, 'objectMode'>) {
18
- super({
19
- allowHalfOpen: false, // Default to autoclose on end().
20
- ...streamOptions,
21
- objectMode: true, // Preserve the string/buffer distinction (WebRTC treats them differently)
22
- });
23
-
24
- this._rawChannel = rawChannel;
25
- this._readActive = true;
26
-
27
- rawChannel.onMessage((msg: any) => {
28
- if (!this._readActive) return; // If the buffer is full, drop messages.
29
-
30
- // If the push is rejected, we pause reading until the next call to _read().
31
- this._readActive = this.push(msg);
32
- });
33
-
34
- // When the DataChannel closes, the readable & writable ends close
35
- rawChannel.onClosed(() => {
36
- this.push(null);
37
- this.destroy();
38
- });
39
-
40
- rawChannel.onError((errMsg: string) => {
41
- this.destroy(new Error(`DataChannel error: ${errMsg}`));
42
- });
43
-
44
- // Buffer all writes until the DataChannel opens
45
- if (!rawChannel.isOpen()) {
46
- this.cork();
47
- rawChannel.onOpen(() => this.uncork());
48
- }
13
+ private _rawChannel: any;
14
+ private _readActive: boolean;
15
+
16
+ constructor(rawChannel: any, streamOptions?: Omit<stream.DuplexOptions, 'objectMode'>) {
17
+ super({
18
+ allowHalfOpen: false, // Default to autoclose on end().
19
+ ...streamOptions,
20
+ objectMode: true, // Preserve the string/buffer distinction (WebRTC treats them differently)
21
+ });
22
+
23
+ this._rawChannel = rawChannel;
24
+ this._readActive = true;
25
+
26
+ rawChannel.onMessage((msg: any) => {
27
+ if (!this._readActive) return; // If the buffer is full, drop messages.
28
+
29
+ // If the push is rejected, we pause reading until the next call to _read().
30
+ this._readActive = this.push(msg);
31
+ });
32
+
33
+ // When the DataChannel closes, the readable & writable ends close
34
+ rawChannel.onClosed(() => {
35
+ this.push(null);
36
+ this.destroy();
37
+ });
38
+
39
+ rawChannel.onError((errMsg: string) => {
40
+ this.destroy(new Error(`DataChannel error: ${errMsg}`));
41
+ });
42
+
43
+ // Buffer all writes until the DataChannel opens
44
+ if (!rawChannel.isOpen()) {
45
+ this.cork();
46
+ rawChannel.onOpen(() => this.uncork());
49
47
  }
50
-
51
- _read(): void {
52
- // Stop dropping messages, if the buffer filling up meant we were doing so before.
53
- this._readActive = true;
54
- }
55
-
56
- _write(chunk, _encoding, callback): void {
57
- let sentOk;
58
-
59
- try {
60
- if (Buffer.isBuffer(chunk)) {
61
- sentOk = this._rawChannel.sendMessageBinary(chunk);
62
- } else if (typeof chunk === 'string') {
63
- sentOk = this._rawChannel.sendMessage(chunk);
64
- } else {
65
- const typeName = chunk.constructor.name || typeof chunk;
66
- throw new Error(`Cannot write ${typeName} to DataChannel stream`);
67
- }
68
- } catch (err) {
69
- return callback(err);
70
- }
71
-
72
- if (sentOk) {
73
- callback(null);
74
- } else {
75
- callback(new Error('Failed to write to DataChannel'));
76
- }
77
- }
78
-
79
- _final(callback): void {
80
- if (!this.allowHalfOpen) this.destroy();
81
- callback(null);
82
- }
83
-
84
- _destroy(maybeErr, callback): void {
85
- // When the stream is destroyed, we close the DataChannel.
86
- this._rawChannel.close();
87
- callback(maybeErr);
88
- }
89
-
90
- get label(): string {
91
- return this._rawChannel.getLabel();
92
- }
93
-
94
- get id(): number {
95
- return this._rawChannel.getId();
48
+ }
49
+
50
+ _read(): void {
51
+ // Stop dropping messages, if the buffer filling up meant we were doing so before.
52
+ this._readActive = true;
53
+ }
54
+
55
+ _write(chunk, _encoding, callback): void {
56
+ let sentOk;
57
+
58
+ try {
59
+ if (Buffer.isBuffer(chunk)) {
60
+ sentOk = this._rawChannel.sendMessageBinary(chunk);
61
+ } else if (typeof chunk === 'string') {
62
+ sentOk = this._rawChannel.sendMessage(chunk);
63
+ } else {
64
+ const typeName = chunk.constructor.name || typeof chunk;
65
+ throw new Error(`Cannot write ${typeName} to DataChannel stream`);
66
+ }
67
+ } catch (err) {
68
+ return callback(err);
96
69
  }
97
70
 
98
- get protocol(): string {
99
- return this._rawChannel.getProtocol();
71
+ if (sentOk) {
72
+ callback(null);
73
+ } else {
74
+ callback(new Error('Failed to write to DataChannel'));
100
75
  }
76
+ }
77
+
78
+ _final(callback): void {
79
+ if (!this.allowHalfOpen) this.destroy();
80
+ callback(null);
81
+ }
82
+
83
+ _destroy(maybeErr, callback): void {
84
+ // When the stream is destroyed, we close the DataChannel.
85
+ this._rawChannel.close();
86
+ callback(maybeErr);
87
+ }
88
+
89
+ get label(): string {
90
+ return this._rawChannel.getLabel();
91
+ }
92
+
93
+ get id(): number {
94
+ return this._rawChannel.getId();
95
+ }
96
+
97
+ get protocol(): string {
98
+ return this._rawChannel.getProtocol();
99
+ }
101
100
  }