From d678f44282e8a85166a062113f9c439685ca4667 Mon Sep 17 00:00:00 2001 From: achingbrain <alex@achingbrain.net> Date: Wed, 12 Jun 2024 09:38:25 +0100 Subject: [PATCH 1/5] feat: support setting ICE ufrag and pwd, and missing config values Updates the implementation to match the latest libdatachannel with features for setting ICE ufrag/pwd and reading the remote cert fingerprint. Also adds pass-through for missing config values. --- API.md | 28 ++++++++++++ src/cpp/peer-connection-wrapper.cpp | 70 ++++++++++++++++++++++++++++- src/cpp/peer-connection-wrapper.h | 1 + src/lib/index.ts | 5 ++- src/lib/types.ts | 19 ++++++++ 5 files changed, 119 insertions(+), 4 deletions(-) diff --git a/API.md b/API.md index e95f9a9..c4aa5c9 100644 --- a/API.md +++ b/API.md @@ -16,12 +16,19 @@ export interface RtcConfig { bindAddress?: string; enableIceTcp?: boolean; enableIceUdpMux?: boolean; + disableAutoNegotiation?: boolean; + disableFingerprintVerification?: boolean; + disableAutoGathering?: boolean; + forceMediaTransport?: boolean; portRangeBegin?: number; portRangeEnd?: number; maxMessageSize?: number; mtu?: number; iceTransportPolicy?: TransportPolicy; disableFingerprintVerification?: boolean; + certificatePemFile?: string; + keyPemFile?: string; + keyPemPass?: string; } export const enum RelayType { @@ -69,6 +76,27 @@ export const enum DescriptionType { } ``` +**setLocalDescription: (sdp: string, init?: LocalDescriptionInit) => void** + +Set Local Description and optionally the ICE ufrag/pwd to use. These should not +be set as they will be generated automatically as per the spec. +``` +export interface LocalDescriptionInit { + iceUfrag?: string; + icePwd?: string; +} +``` + +**remoteFingerprint: () => CertificateFingerprint** + +Returns the certificate fingerprint used by the remote peer +``` +export interface CertificateFingerprint { + value: string; + algorithm: 'sha-1' | 'sha-224' | 'sha-256' | 'sha-384' | 'sha-512' | 'md5' | 'md2'; +} +``` + **addRemoteCandidate: (candidate: string, mid: string) => void** Add remote candidate info diff --git a/src/cpp/peer-connection-wrapper.cpp b/src/cpp/peer-connection-wrapper.cpp index 4e3d9e7..200a9ce 100644 --- a/src/cpp/peer-connection-wrapper.cpp +++ b/src/cpp/peer-connection-wrapper.cpp @@ -41,6 +41,7 @@ Napi::Object PeerConnectionWrapper::Init(Napi::Env env, Napi::Object exports) InstanceMethod("setRemoteDescription", &PeerConnectionWrapper::setRemoteDescription), InstanceMethod("localDescription", &PeerConnectionWrapper::localDescription), InstanceMethod("remoteDescription", &PeerConnectionWrapper::remoteDescription), + InstanceMethod("remoteFingerprint", &PeerConnectionWrapper::remoteFingerprint), InstanceMethod("addRemoteCandidate", &PeerConnectionWrapper::addRemoteCandidate), InstanceMethod("createDataChannel", &PeerConnectionWrapper::createDataChannel), InstanceMethod("addTrack", &PeerConnectionWrapper::addTrack), @@ -214,6 +215,10 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N if (config.Get("disableAutoNegotiation").IsBoolean()) rtcConfig.disableAutoNegotiation = config.Get("disableAutoNegotiation").As<Napi::Boolean>(); + // disableAutoGathering option + if (config.Get("disableAutoGathering").IsBoolean()) + rtcConfig.disableAutoGathering = config.Get("disableAutoGathering").As<Napi::Boolean>(); + // forceMediaTransport option if (config.Get("forceMediaTransport").IsBoolean()) rtcConfig.forceMediaTransport = config.Get("forceMediaTransport").As<Napi::Boolean>(); @@ -251,6 +256,17 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N rtcConfig.disableFingerprintVerification = config.Get("disableFingerprintVerification").As<Napi::Boolean>(); } + // Specify certificate to use if set + if (config.Get("certificatePemFile").IsString()) { + rtcConfig.certificatePemFile = config.Get("certificatePemFile").As<Napi::String>().ToString(); + } + if (config.Get("keyPemFile").IsString()) { + rtcConfig.keyPemFile = config.Get("keyPemFile").As<Napi::String>().ToString(); + } + if (config.Get("keyPemPass").IsString()) { + rtcConfig.keyPemPass = config.Get("keyPemPass").As<Napi::String>().ToString(); + } + // Create peer-connection try { @@ -331,6 +347,7 @@ void PeerConnectionWrapper::setLocalDescription(const Napi::CallbackInfo &info) } rtc::Description::Type type = rtc::Description::Type::Unspec; + rtc::LocalDescriptionInit init; // optional if (length > 0) @@ -356,7 +373,29 @@ void PeerConnectionWrapper::setLocalDescription(const Napi::CallbackInfo &info) type = rtc::Description::Type::Rollback; } - mRtcPeerConnPtr->setLocalDescription(type); + // optional + if (length > 1) + { + PLOG_DEBUG << "setLocalDescription() called with LocalDescriptionInit"; + + if (info[1].IsObject()) + { + PLOG_DEBUG << "setLocalDescription() called with LocalDescriptionInit as object"; + Napi::Object obj = info[1].As<Napi::Object>(); + + if (obj.Get("iceUfrag").IsString()) { + PLOG_DEBUG << "setLocalDescription() has ufrag"; + init.iceUfrag = obj.Get("iceUfrag").As<Napi::String>(); + } + + if (obj.Get("icePwd").IsString()) { + PLOG_DEBUG << "setLocalDescription() has password"; + init.icePwd = obj.Get("icePwd").As<Napi::String>(); + } + } + } + + mRtcPeerConnPtr->setLocalDescription(type, init); } void PeerConnectionWrapper::setRemoteDescription(const Napi::CallbackInfo &info) @@ -1002,7 +1041,34 @@ Napi::Value PeerConnectionWrapper::maxMessageSize(const Napi::CallbackInfo &info try { - return Napi::Number::New(env, mRtcPeerConnPtr->remoteMaxMessageSize()); + return Napi::Array::New(env, mRtcPeerConnPtr->remoteMaxMessageSize()); + } + catch (std::exception &ex) + { + Napi::Error::New(env, std::string("libdatachannel error: ") + ex.what()).ThrowAsJavaScriptException(); + return Napi::Number::New(info.Env(), 0); + } +} + +Napi::Value PeerConnectionWrapper::remoteFingerprint(const Napi::CallbackInfo &info) +{ + PLOG_DEBUG << "remoteFingerprints() called"; + Napi::Env env = info.Env(); + + if (!mRtcPeerConnPtr) + { + return Napi::Number::New(info.Env(), 0); + } + + try + { + auto fingerprint = mRtcPeerConnPtr->remoteFingerprint(); + + Napi::Object fingerprintObject = Napi::Object::New(env); + fingerprintObject.Set("value", fingerprint.value); + fingerprintObject.Set("algorithm", rtc::CertificateFingerprint::AlgorithmIdentifier(fingerprint.algorithm)); + + return fingerprintObject; } catch (std::exception &ex) { diff --git a/src/cpp/peer-connection-wrapper.h b/src/cpp/peer-connection-wrapper.h index 5896e30..3865c08 100644 --- a/src/cpp/peer-connection-wrapper.h +++ b/src/cpp/peer-connection-wrapper.h @@ -33,6 +33,7 @@ class PeerConnectionWrapper : public Napi::ObjectWrap<PeerConnectionWrapper> Napi::Value iceState(const Napi::CallbackInfo &info); Napi::Value signalingState(const Napi::CallbackInfo &info); Napi::Value gatheringState(const Napi::CallbackInfo &info); + Napi::Value remoteFingerprint(const Napi::CallbackInfo &info); // Callbacks void onLocalDescription(const Napi::CallbackInfo &info); diff --git a/src/lib/index.ts b/src/lib/index.ts index 8b1dd82..5bafb2e 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,7 +1,7 @@ import nodeDataChannel from './node-datachannel'; import _DataChannelStream from './datachannel-stream'; import { WebSocketServer } from './websocket-server'; -import { Channel, DataChannelInitConfig, DescriptionType, Direction, LogLevel, RtcConfig, RTCIceConnectionState, RTCIceGatheringState, RTCPeerConnectionState, RTCSignalingState, SctpSettings, SelectedCandidateInfo } from './types'; +import { CertificateFingerprint, Channel, DataChannelInitConfig, DescriptionType, Direction, LocalDescriptionInit, LogLevel, RtcConfig, RTCIceConnectionState, RTCIceGatheringState, RTCPeerConnectionState, RTCSignalingState, SctpSettings, SelectedCandidateInfo } from './types'; import { WebSocket } from './websocket'; export function preload(): void { nodeDataChannel.preload(); } @@ -113,10 +113,11 @@ export const DataChannel: { export interface PeerConnection { close(): void; - setLocalDescription(type?: DescriptionType): void; + setLocalDescription(type?: DescriptionType, init?: LocalDescriptionInit): void; setRemoteDescription(sdp: string, type: DescriptionType): void; localDescription(): { type: DescriptionType; sdp: string } | null; remoteDescription(): { type: DescriptionType; sdp: string } | null; + remoteFingerprint(): CertificateFingerprint; addRemoteCandidate(candidate: string, mid: string): void; createDataChannel(label: string, config?: DataChannelInitConfig): DataChannel; addTrack(media: Video | Audio): Track; diff --git a/src/lib/types.ts b/src/lib/types.ts index 75541e3..a14a9a7 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -77,6 +77,10 @@ export interface RtcConfig { mtu?: number; iceTransportPolicy?: TransportPolicy; disableFingerprintVerification?: boolean; + disableAutoGathering?: boolean; + certificatePemFile?: string; + keyPemFile?: string; + keyPemPass?: string; } // Lowercase to match the description type string from libdatachannel @@ -97,6 +101,10 @@ export type RTCIceGathererState = "complete" | "gathering" | "new"; export type RTCIceGatheringState = "complete" | "gathering" | "new"; export type RTCSignalingState = "closed" | "have-local-offer" | "have-local-pranswer" | "have-remote-offer" | "have-remote-pranswer" | "stable"; +export interface LocalDescriptionInit { + iceUfrag?: string; + icePwd?: string; +} export interface DataChannelInitConfig { protocol?: string; @@ -107,6 +115,17 @@ export interface DataChannelInitConfig { maxRetransmits?: number; // Reliability } +export interface CertificateFingerprint { + /** + * @see https://developer.mozilla.org/en-US/docs/Web/API/RTCCertificate/getFingerprints#value + */ + value: string; + /** + * @see https://developer.mozilla.org/en-US/docs/Web/API/RTCCertificate/getFingerprints#algorithm + */ + algorithm: 'sha-1' | 'sha-224' | 'sha-256' | 'sha-384' | 'sha-512' | 'md5' | 'md2'; +} + export interface SelectedCandidateInfo { address: string; port: number; From f3211b15dd6e4931dfc600b7f787befc5a5afa5b Mon Sep 17 00:00:00 2001 From: achingbrain <alex@achingbrain.net> Date: Tue, 14 Jan 2025 17:54:59 +0100 Subject: [PATCH 2/5] chore: fix linting --- src/lib/node-datachannel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/node-datachannel.ts b/src/lib/node-datachannel.ts index 4a08e83..1929f2a 100644 --- a/src/lib/node-datachannel.ts +++ b/src/lib/node-datachannel.ts @@ -1,2 +1,2 @@ -let nodeDataChannel = require('../../build/Release/node_datachannel.node'); +import nodeDataChannel = require('../../build/Release/node_datachannel.node'); export default nodeDataChannel; From bd58537b31af38664119270ac2788b34a9b384e2 Mon Sep 17 00:00:00 2001 From: achingbrain <alex@achingbrain.net> Date: Thu, 16 Jan 2025 19:12:55 +0100 Subject: [PATCH 3/5] feat: support different callbacks for different ports --- src/cpp/rtc-wrapper.cpp | 59 +++++++++++++++++++++++++++++++++++++++++ src/cpp/rtc-wrapper.h | 2 ++ src/lib/index.ts | 2 ++ 3 files changed, 63 insertions(+) diff --git a/src/cpp/rtc-wrapper.cpp b/src/cpp/rtc-wrapper.cpp index db2a298..0b9a224 100644 --- a/src/cpp/rtc-wrapper.cpp +++ b/src/cpp/rtc-wrapper.cpp @@ -18,6 +18,7 @@ Napi::Object RtcWrapper::Init(Napi::Env env, Napi::Object exports) exports.Set("cleanup", Napi::Function::New(env, &RtcWrapper::cleanup)); exports.Set("preload", Napi::Function::New(env, &RtcWrapper::preload)); exports.Set("setSctpSettings", Napi::Function::New(env, &RtcWrapper::setSctpSettings)); + exports.Set("onUnhandledStunRequest", Napi::Function::New(env, &RtcWrapper::onUnhandledStunRequest)); return exports; } @@ -172,3 +173,61 @@ void RtcWrapper::setSctpSettings(const Napi::CallbackInfo &info) rtc::SetSctpSettings(settings); } + +void RtcWrapper::onUnhandledStunRequest(const Napi::CallbackInfo &info) +{ + PLOG_DEBUG << "onUnhandledStunRequest() called"; + Napi::Env env = info.Env(); + int length = info.Length(); + + if (length < 1 || !info[0].IsString()) + { + Napi::TypeError::New(env, "host (String) expected").ThrowAsJavaScriptException(); + return; + } + Napi::String host = info[0].As<Napi::String>(); + + if (length < 2 || !info[1].IsNumber()) + { + Napi::TypeError::New(env, "port (Number) expected").ThrowAsJavaScriptException(); + return; + } + Napi::Number port = info[1].As<Napi::Number>(); + + // unbind listener if cb is null, undefined, or was omitted + if (length == 2 || (info[2].IsNull() || info[2].IsUndefined())) { + unboundStunCallbacks.erase(port.ToNumber().Uint32Value()); + rtc::OnUnhandledStunRequest(host.ToString(), port.ToNumber()); + return; + } + + if (length < 3 || !info[2].IsFunction()) + { + Napi::TypeError::New(env, "cb (Function) expected").ThrowAsJavaScriptException(); + return; + } + Napi::Function cb = info[2].As<Napi::Function>(); + + std::unique_ptr<ThreadSafeCallback> callback = std::make_unique<ThreadSafeCallback>(cb); + unboundStunCallbacks[port.ToNumber().Uint32Value()] = std::move(callback); + void * ptr = &unboundStunCallbacks[port.ToNumber().Uint32Value()]; + + rtc::OnUnhandledStunRequest(host.ToString(), port.ToNumber(), [&](rtc::UnhandledStunRequest request, void *userPtr) { + PLOG_DEBUG << "mOnUnhandledStunRequestCallback call(1)"; + + std::unique_ptr<ThreadSafeCallback> &callback = *(std::unique_ptr<ThreadSafeCallback> *)userPtr; + + if (callback) { + callback->call([request = std::move(request)](Napi::Env env, std::vector<napi_value> &args) { + Napi::Object reqObj = Napi::Object::New(env); + reqObj.Set("ufrag", request.remoteUfrag.c_str()); + reqObj.Set("host", request.remoteHost.c_str()); + reqObj.Set("port", request.remotePort); + + args = {reqObj}; + }); + } + + PLOG_DEBUG << "mOnUnhandledStunRequestCallback call(2)"; + }, ptr); +} diff --git a/src/cpp/rtc-wrapper.h b/src/cpp/rtc-wrapper.h index 42ce8ed..65330ef 100644 --- a/src/cpp/rtc-wrapper.h +++ b/src/cpp/rtc-wrapper.h @@ -18,8 +18,10 @@ class RtcWrapper static void initLogger(const Napi::CallbackInfo &info); static void cleanup(const Napi::CallbackInfo &info); static void setSctpSettings(const Napi::CallbackInfo &info); + static void onUnhandledStunRequest(const Napi::CallbackInfo &info); private: static inline std::unique_ptr<ThreadSafeCallback> logCallback = nullptr; + static inline std::map<u_int16_t, std::unique_ptr<ThreadSafeCallback>> unboundStunCallbacks; }; #endif // RTC_WRAPPER_H diff --git a/src/lib/index.ts b/src/lib/index.ts index 5bafb2e..64c0fb6 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -8,6 +8,7 @@ export function preload(): void { nodeDataChannel.preload(); } export function initLogger(level: LogLevel): void { nodeDataChannel.initLogger(level); } export function cleanup(): void { nodeDataChannel.cleanup(); } export function setSctpSettings(settings: SctpSettings): void { nodeDataChannel.setSctpSettings(settings); } +export function onUnhandledStunRequest(host: string, port: number, cb?: (req: { ufrag: string, host: string, port: number }) => void): void { nodeDataChannel.onUnhandledStunRequest(host, port, cb); } export interface Audio { addAudioCodec(payloadType: number, codec: string, profile?: string): void; @@ -159,6 +160,7 @@ export default { cleanup, preload, setSctpSettings, + onUnhandledStunRequest, RtcpReceivingSession, Track, Video, From 122950dc441c6026812b377f3e9c3310ffbd6399 Mon Sep 17 00:00:00 2001 From: achingbrain <alex@achingbrain.net> Date: Fri, 17 Jan 2025 18:05:17 +0100 Subject: [PATCH 4/5] chore: align with libdatachannel --- src/cpp/rtc-wrapper.cpp | 57 ++++++++++++++++--------------- src/cpp/rtc-wrapper.h | 5 +-- src/lib/index.ts | 4 +-- src/lib/node-datachannel.ts | 1 + src/lib/types.ts | 22 ++---------- src/polyfill/RTCPeerConnection.ts | 5 +-- 6 files changed, 42 insertions(+), 52 deletions(-) diff --git a/src/cpp/rtc-wrapper.cpp b/src/cpp/rtc-wrapper.cpp index 0b9a224..3faa92d 100644 --- a/src/cpp/rtc-wrapper.cpp +++ b/src/cpp/rtc-wrapper.cpp @@ -18,7 +18,7 @@ Napi::Object RtcWrapper::Init(Napi::Env env, Napi::Object exports) exports.Set("cleanup", Napi::Function::New(env, &RtcWrapper::cleanup)); exports.Set("preload", Napi::Function::New(env, &RtcWrapper::preload)); exports.Set("setSctpSettings", Napi::Function::New(env, &RtcWrapper::setSctpSettings)); - exports.Set("onUnhandledStunRequest", Napi::Function::New(env, &RtcWrapper::onUnhandledStunRequest)); + exports.Set("listenIceUdpMux", Napi::Function::New(env, &RtcWrapper::listenIceUdpMux)); return exports; } @@ -174,48 +174,46 @@ void RtcWrapper::setSctpSettings(const Napi::CallbackInfo &info) rtc::SetSctpSettings(settings); } -void RtcWrapper::onUnhandledStunRequest(const Napi::CallbackInfo &info) +void RtcWrapper::listenIceUdpMux(const Napi::CallbackInfo &info) { - PLOG_DEBUG << "onUnhandledStunRequest() called"; + PLOG_DEBUG << "listenIceUdpMux() called"; Napi::Env env = info.Env(); int length = info.Length(); - if (length < 1 || !info[0].IsString()) + if (length < 1 || !info[0].IsNumber()) { - Napi::TypeError::New(env, "host (String) expected").ThrowAsJavaScriptException(); + Napi::TypeError::New(env, "port (Number) expected").ThrowAsJavaScriptException(); return; } - Napi::String host = info[0].As<Napi::String>(); + int port = info[0].As<Napi::Number>().ToNumber(); - if (length < 2 || !info[1].IsNumber()) + Napi::Function listener; + if (length > 1 && info[1].IsFunction()) { - Napi::TypeError::New(env, "port (Number) expected").ThrowAsJavaScriptException(); - return; + listener = info[1].As<Napi::Function>(); } - Napi::Number port = info[1].As<Napi::Number>(); - // unbind listener if cb is null, undefined, or was omitted - if (length == 2 || (info[2].IsNull() || info[2].IsUndefined())) { - unboundStunCallbacks.erase(port.ToNumber().Uint32Value()); - rtc::OnUnhandledStunRequest(host.ToString(), port.ToNumber()); - return; + std::string host; + if (length > 2 && info[2].IsString()) + { + host = info[2].As<Napi::String>().ToString(); } - if (length < 3 || !info[2].IsFunction()) + // unbind listener if cb is null, undefined, or was omitted + if (!listener) { - Napi::TypeError::New(env, "cb (Function) expected").ThrowAsJavaScriptException(); + std::lock_guard<std::mutex> guard(iceUdpMuxListenersMutex); + iceUdpMuxListeners.erase(port); + rtc::ListenIceUdpMux(port, nullptr, host); return; } - Napi::Function cb = info[2].As<Napi::Function>(); - - std::unique_ptr<ThreadSafeCallback> callback = std::make_unique<ThreadSafeCallback>(cb); - unboundStunCallbacks[port.ToNumber().Uint32Value()] = std::move(callback); - void * ptr = &unboundStunCallbacks[port.ToNumber().Uint32Value()]; - rtc::OnUnhandledStunRequest(host.ToString(), port.ToNumber(), [&](rtc::UnhandledStunRequest request, void *userPtr) { - PLOG_DEBUG << "mOnUnhandledStunRequestCallback call(1)"; + auto uniqueCallback = std::make_unique<ThreadSafeCallback>(listener); + auto callback = std::shared_ptr<ThreadSafeCallback>(std::move(uniqueCallback)); - std::unique_ptr<ThreadSafeCallback> &callback = *(std::unique_ptr<ThreadSafeCallback> *)userPtr; + rtc::IceUdpMuxCallback iceUdpMuxCallback = [callback](rtc::IceUdpMuxRequest request) + { + PLOG_DEBUG << "listenIceUdpMux IceUdpMuxCallback call(1)"; if (callback) { callback->call([request = std::move(request)](Napi::Env env, std::vector<napi_value> &args) { @@ -228,6 +226,11 @@ void RtcWrapper::onUnhandledStunRequest(const Napi::CallbackInfo &info) }); } - PLOG_DEBUG << "mOnUnhandledStunRequestCallback call(2)"; - }, ptr); + PLOG_DEBUG << "listenIceUdpMux IceUdpMuxCallback call(2)"; + }; + + std::lock_guard<std::mutex> guard(iceUdpMuxListenersMutex); + iceUdpMuxListeners[port] = std::move(iceUdpMuxCallback); + + rtc::ListenIceUdpMux(port, &iceUdpMuxListeners[port], host); } diff --git a/src/cpp/rtc-wrapper.h b/src/cpp/rtc-wrapper.h index 65330ef..49e6c03 100644 --- a/src/cpp/rtc-wrapper.h +++ b/src/cpp/rtc-wrapper.h @@ -18,10 +18,11 @@ class RtcWrapper static void initLogger(const Napi::CallbackInfo &info); static void cleanup(const Napi::CallbackInfo &info); static void setSctpSettings(const Napi::CallbackInfo &info); - static void onUnhandledStunRequest(const Napi::CallbackInfo &info); + static void listenIceUdpMux(const Napi::CallbackInfo &info); private: static inline std::unique_ptr<ThreadSafeCallback> logCallback = nullptr; - static inline std::map<u_int16_t, std::unique_ptr<ThreadSafeCallback>> unboundStunCallbacks; + static inline std::mutex iceUdpMuxListenersMutex; + static inline std::map<u_int16_t, rtc::IceUdpMuxCallback> iceUdpMuxListeners; }; #endif // RTC_WRAPPER_H diff --git a/src/lib/index.ts b/src/lib/index.ts index 64c0fb6..9df0cb6 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -8,7 +8,7 @@ export function preload(): void { nodeDataChannel.preload(); } export function initLogger(level: LogLevel): void { nodeDataChannel.initLogger(level); } export function cleanup(): void { nodeDataChannel.cleanup(); } export function setSctpSettings(settings: SctpSettings): void { nodeDataChannel.setSctpSettings(settings); } -export function onUnhandledStunRequest(host: string, port: number, cb?: (req: { ufrag: string, host: string, port: number }) => void): void { nodeDataChannel.onUnhandledStunRequest(host, port, cb); } +export function listenIceUdpMux(port: number, cb?: (req: { ufrag: string, host: string, port: number }) => void, host?: string): void { nodeDataChannel.listenIceUdpMux(port, cb, host); } export interface Audio { addAudioCodec(payloadType: number, codec: string, profile?: string): void; @@ -160,7 +160,7 @@ export default { cleanup, preload, setSctpSettings, - onUnhandledStunRequest, + listenIceUdpMux, RtcpReceivingSession, Track, Video, diff --git a/src/lib/node-datachannel.ts b/src/lib/node-datachannel.ts index 1929f2a..fb87e3c 100644 --- a/src/lib/node-datachannel.ts +++ b/src/lib/node-datachannel.ts @@ -1,2 +1,3 @@ +// @ts-expect-error no types import nodeDataChannel = require('../../build/Release/node_datachannel.node'); export default nodeDataChannel; diff --git a/src/lib/types.ts b/src/lib/types.ts index a14a9a7..ed3619d 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -47,11 +47,7 @@ export interface ProxyServer { password?: string; } -export const enum RelayType { - TurnUdp = 'TurnUdp', - TurnTcp = 'TurnTcp', - TurnTls = 'TurnTls', -} +export type RelayType = 'TurnUdp' | 'TurnTcp' | 'TurnTls' export interface IceServer { hostname: string; @@ -84,13 +80,7 @@ export interface RtcConfig { } // Lowercase to match the description type string from libdatachannel -export enum DescriptionType { - Unspec = 'unspec', - Offer = 'offer', - Answer = 'answer', - Pranswer = 'pranswer', - Rollback = 'rollback', -} +export type DescriptionType = 'unspec' | 'offer' | 'answer' | 'pranswer' | 'rollback' export type RTCSdpType = 'answer' | 'offer' | 'pranswer' | 'rollback'; @@ -137,10 +127,4 @@ export interface SelectedCandidateInfo { } // Must be same as rtc enum class Direction -export const enum Direction { - SendOnly = 'SendOnly', - RecvOnly = 'RecvOnly', - SendRecv = 'SendRecv', - Inactive = 'Inactive', - Unknown = 'Unknown', -} +export type Direction = 'SendOnly' | 'RecvOnly' | 'SendRecv' | 'Inactive' | 'Unknown' diff --git a/src/polyfill/RTCPeerConnection.ts b/src/polyfill/RTCPeerConnection.ts index a809c01..520aab3 100644 --- a/src/polyfill/RTCPeerConnection.ts +++ b/src/polyfill/RTCPeerConnection.ts @@ -121,7 +121,8 @@ export default class RTCPeerConnection extends EventTarget implements globalThis try { const peerIdentity = (config as any)?.peerIdentity ?? `peer-${getRandomString(7)}`; - this.#peerConnection = new PeerConnection(peerIdentity, + // @ts-expect-error fixme + this.#peerConnection = config.peerConnection ?? new PeerConnection(peerIdentity, { ...config, iceServers: @@ -282,7 +283,7 @@ export default class RTCPeerConnection extends EventTarget implements globalThis return this.#peerConnection.signalingState(); } - async addIceCandidate(candidate?: globalThis.RTCIceCandidateInit | RTCIceCandidate): Promise<void> { + async addIceCandidate(candidate?: globalThis.RTCIceCandidateInit | null): Promise<void> { if (!candidate || !candidate.candidate) { return; } From 29f1679d69f42ebb01a8270657ebc173678b6448 Mon Sep 17 00:00:00 2001 From: achingbrain <alex@achingbrain.net> Date: Fri, 17 Jan 2025 18:06:36 +0100 Subject: [PATCH 5/5] chore: align with libdatachannel --- src/lib/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/index.ts b/src/lib/index.ts index 9df0cb6..74e41de 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -8,7 +8,7 @@ export function preload(): void { nodeDataChannel.preload(); } export function initLogger(level: LogLevel): void { nodeDataChannel.initLogger(level); } export function cleanup(): void { nodeDataChannel.cleanup(); } export function setSctpSettings(settings: SctpSettings): void { nodeDataChannel.setSctpSettings(settings); } -export function listenIceUdpMux(port: number, cb?: (req: { ufrag: string, host: string, port: number }) => void, host?: string): void { nodeDataChannel.listenIceUdpMux(port, cb, host); } +export function listenIceUdpMux(port: number, cb?: (req: { ufrag: string, host: string, port: number }) => void | null, host?: string | null): void { nodeDataChannel.listenIceUdpMux(port, cb, host); } export interface Audio { addAudioCodec(payloadType: number, codec: string, profile?: string): void;