node-rtc-connection 1.0.18 → 2.0.4
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/README.md +94 -85
- package/dist/index.cjs +20 -5421
- package/dist/index.mjs +25 -5413
- package/dist/types/crypto/der.d.ts +107 -0
- package/dist/types/crypto/x509.d.ts +56 -0
- package/dist/types/datachannel/RTCDataChannel.d.ts +179 -0
- package/dist/types/dtls/RTCCertificate.d.ts +163 -0
- package/dist/types/dtls/cipher.d.ts +81 -0
- package/dist/types/dtls/connection.d.ts +81 -0
- package/dist/types/dtls/prf.d.ts +29 -0
- package/dist/types/dtls/protocol.d.ts +127 -0
- package/dist/types/foundation/ByteBufferQueue.d.ts +71 -0
- package/dist/types/foundation/RTCError.d.ts +152 -0
- package/dist/types/ice/RTCIceCandidate.d.ts +161 -0
- package/dist/types/ice/ice-agent.d.ts +154 -0
- package/dist/types/ice/stun-message.d.ts +92 -0
- package/dist/types/index.d.ts +29 -0
- package/dist/types/peerconnection/RTCPeerConnection.d.ts +74 -0
- package/dist/types/sctp/association.d.ts +77 -0
- package/dist/types/sctp/chunks.d.ts +200 -0
- package/dist/types/sctp/crc32c.d.ts +24 -0
- package/dist/types/sctp/datachannel-manager.d.ts +51 -0
- package/dist/types/sctp/dcep.d.ts +56 -0
- package/dist/types/sdp/RTCSessionDescription.d.ts +73 -0
- package/dist/types/sdp/sdp-utils.d.ts +103 -0
- package/dist/types/stun/stun-client.d.ts +119 -0
- package/dist/types/transport-stack.d.ts +68 -0
- package/package.json +26 -21
- package/src/crypto/der.ts +205 -0
- package/src/crypto/x509.ts +146 -0
- package/src/datachannel/RTCDataChannel.ts +388 -0
- package/src/dtls/RTCCertificate.ts +396 -0
- package/src/dtls/cipher.ts +198 -0
- package/src/dtls/connection.ts +974 -0
- package/src/dtls/prf.ts +62 -0
- package/src/dtls/protocol.ts +204 -0
- package/src/foundation/{ByteBufferQueue.js → ByteBufferQueue.ts} +74 -72
- package/src/foundation/{RTCError.js → RTCError.ts} +110 -60
- package/src/ice/{RTCIceCandidate.js → RTCIceCandidate.ts} +140 -92
- package/src/ice/ice-agent.ts +609 -0
- package/src/ice/stun-message.ts +260 -0
- package/src/index.ts +72 -0
- package/src/peerconnection/RTCPeerConnection.ts +430 -0
- package/src/sctp/association.ts +523 -0
- package/src/sctp/chunks.ts +350 -0
- package/src/sctp/crc32c.ts +57 -0
- package/src/sctp/datachannel-manager.ts +187 -0
- package/src/sctp/dcep.ts +94 -0
- package/src/sdp/{RTCSessionDescription.js → RTCSessionDescription.ts} +42 -29
- package/src/sdp/sdp-utils.ts +229 -0
- package/src/stun/stun-client.ts +936 -0
- package/src/transport-stack.ts +165 -0
- package/dist/index.cjs.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/src/datachannel/RTCDataChannel.js +0 -354
- package/src/dtls/RTCCertificate.js +0 -310
- package/src/dtls/RTCDtlsTransport.js +0 -247
- package/src/ice/RTCIceTransport.js +0 -998
- package/src/index.d.ts +0 -400
- package/src/index.js +0 -92
- package/src/network/network-transport.js +0 -478
- package/src/peerconnection/RTCPeerConnection.js +0 -851
- package/src/sctp/RTCSctpTransport.js +0 -253
- package/src/sdp/sdp-utils.js +0 -224
- package/src/stun/stun-client.js +0 -643
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file transport-stack.ts
|
|
3
|
+
* @description Composes ICE -> DTLS -> SCTP -> DCEP into one transport, the
|
|
4
|
+
* real WebRTC data-channel pipeline.
|
|
5
|
+
* @module transport-stack
|
|
6
|
+
*
|
|
7
|
+
* Wiring:
|
|
8
|
+
* IceAgent emits 'data' (non-STUN datagrams) -> DtlsConnection.handlePacket
|
|
9
|
+
* DtlsConnection.output -> IceAgent.send
|
|
10
|
+
* DtlsConnection 'data' (app records) -> SctpAssociation.receivePacket
|
|
11
|
+
* SctpAssociation 'output' -> DtlsConnection.send
|
|
12
|
+
* SctpAssociation + DataChannelManager -> RTCDataChannel
|
|
13
|
+
*
|
|
14
|
+
* The DTLS client/server role follows the negotiated a=setup; the SCTP client
|
|
15
|
+
* (INIT initiator) is the DTLS client, per RFC 8832.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { EventEmitter } from 'events';
|
|
19
|
+
import { IceAgent } from './ice/ice-agent';
|
|
20
|
+
import { DtlsConnection, ROLE as DTLS_ROLE } from './dtls/connection';
|
|
21
|
+
import { SctpAssociation } from './sctp/association';
|
|
22
|
+
import { DataChannelManager, OpenRequestInfo } from './sctp/datachannel-manager';
|
|
23
|
+
import type { RTCDataChannel, RTCDataChannelInit } from './datachannel/RTCDataChannel';
|
|
24
|
+
import type { KeyObject } from 'crypto';
|
|
25
|
+
|
|
26
|
+
export interface TransportStackOptions {
|
|
27
|
+
/** ICE controlling vs controlled (offerer is controlling). */
|
|
28
|
+
iceRole: 'controlling' | 'controlled';
|
|
29
|
+
/** DTLS role from a=setup (active=client). */
|
|
30
|
+
dtlsRole: 'client' | 'server';
|
|
31
|
+
localUfrag: string;
|
|
32
|
+
localPwd: string;
|
|
33
|
+
certDer: Buffer;
|
|
34
|
+
privateKey: KeyObject;
|
|
35
|
+
verifyFingerprint?: (fp: { algorithm: string; value: string }) => boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class TransportStack extends EventEmitter {
|
|
39
|
+
#opts: TransportStackOptions;
|
|
40
|
+
ice: IceAgent;
|
|
41
|
+
dtls: DtlsConnection | null;
|
|
42
|
+
sctp: SctpAssociation | null;
|
|
43
|
+
dcm: DataChannelManager | null;
|
|
44
|
+
#dtlsStarted: boolean;
|
|
45
|
+
|
|
46
|
+
constructor(opts: TransportStackOptions) {
|
|
47
|
+
super();
|
|
48
|
+
this.#opts = opts;
|
|
49
|
+
this.ice = new IceAgent({
|
|
50
|
+
role: opts.iceRole,
|
|
51
|
+
localUfrag: opts.localUfrag,
|
|
52
|
+
localPwd: opts.localPwd,
|
|
53
|
+
});
|
|
54
|
+
this.dtls = null;
|
|
55
|
+
this.sctp = null;
|
|
56
|
+
this.dcm = null;
|
|
57
|
+
this.#dtlsStarted = false;
|
|
58
|
+
|
|
59
|
+
this.ice.on('candidate', (c) => this.emit('candidate', c));
|
|
60
|
+
this.ice.on('error', (e) => this.emit('error', e));
|
|
61
|
+
this.ice.on('failed', () => this.emit('error', new Error('ICE failed')));
|
|
62
|
+
|
|
63
|
+
// Inbound DTLS datagrams from the selected/learned path.
|
|
64
|
+
this.ice.on('data', (msg: Buffer) => {
|
|
65
|
+
if (this.dtls) this.dtls.handlePacket(msg);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
this.ice.on('connected', () => {
|
|
69
|
+
this.emit('iceconnected');
|
|
70
|
+
this.#startDtls();
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Begin gathering local candidates.
|
|
76
|
+
* @param opts - { iceServers, iceTransportPolicy } forwarded to ICE.
|
|
77
|
+
*/
|
|
78
|
+
async gather(opts: { iceServers?: unknown[]; iceTransportPolicy?: 'all' | 'relay' } = {}): Promise<void> {
|
|
79
|
+
await this.ice.gather(opts as any);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
getLocalCandidates(): ReturnType<IceAgent['getLocalCandidates']> {
|
|
83
|
+
return this.ice.getLocalCandidates();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Provide the peer's ICE credentials and start checks when ready. */
|
|
87
|
+
setRemote(ufrag: string, pwd: string): void {
|
|
88
|
+
this.ice.setRemoteCredentials(ufrag, pwd);
|
|
89
|
+
this.ice.start();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
addRemoteCandidate(cand: { address: string; port: number; type?: string; priority?: number }): void {
|
|
93
|
+
this.ice.addRemoteCandidate(cand);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
#startDtls(): void {
|
|
97
|
+
if (this.#dtlsStarted) return;
|
|
98
|
+
this.#dtlsStarted = true;
|
|
99
|
+
|
|
100
|
+
this.dtls = new DtlsConnection({
|
|
101
|
+
role: this.#opts.dtlsRole === 'client' ? DTLS_ROLE.CLIENT : DTLS_ROLE.SERVER,
|
|
102
|
+
certDer: this.#opts.certDer,
|
|
103
|
+
privateKey: this.#opts.privateKey,
|
|
104
|
+
verifyFingerprint: this.#opts.verifyFingerprint,
|
|
105
|
+
output: (datagram: Buffer) => {
|
|
106
|
+
try { this.ice.send(datagram); } catch (e) { this.emit('error', e); }
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
this.dtls.on('connect', () => {
|
|
111
|
+
this.emit('dtlsconnected');
|
|
112
|
+
this.#startSctp();
|
|
113
|
+
});
|
|
114
|
+
this.dtls.on('data', (record: Buffer) => {
|
|
115
|
+
if (this.sctp) this.sctp.receivePacket(record);
|
|
116
|
+
});
|
|
117
|
+
this.dtls.on('error', (e) => this.emit('error', e));
|
|
118
|
+
this.dtls.on('close', () => this.emit('close'));
|
|
119
|
+
|
|
120
|
+
this.dtls.start();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
#startSctp(): void {
|
|
124
|
+
const isClient = this.#opts.dtlsRole === 'client';
|
|
125
|
+
const sctp = new SctpAssociation({ isClient });
|
|
126
|
+
this.sctp = sctp;
|
|
127
|
+
sctp.on('output', (pkt: Buffer) => {
|
|
128
|
+
try { if (this.dtls) this.dtls.send(pkt); } catch (e) { this.emit('error', e); }
|
|
129
|
+
});
|
|
130
|
+
sctp.on('error', (e) => this.emit('error', e));
|
|
131
|
+
sctp.on('close', () => this.emit('close'));
|
|
132
|
+
|
|
133
|
+
this.dcm = new DataChannelManager(sctp, isClient);
|
|
134
|
+
this.dcm.on('open-request', (info: OpenRequestInfo) => this.emit('datachannel-request', info));
|
|
135
|
+
|
|
136
|
+
sctp.on('established', () => {
|
|
137
|
+
this.emit('sctpconnected');
|
|
138
|
+
this.emit('ready');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
sctp.start();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Open a locally-initiated data channel once SCTP is established. */
|
|
145
|
+
openChannel(channel: RTCDataChannel, init: RTCDataChannelInit): void {
|
|
146
|
+
if (!this.dcm) throw new Error('SCTP not ready');
|
|
147
|
+
this.dcm.openChannel(channel, init);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Accept an inbound channel created from a 'datachannel-request'. */
|
|
151
|
+
acceptChannel(channel: RTCDataChannel, info: OpenRequestInfo): void {
|
|
152
|
+
if (!this.dcm) throw new Error('SCTP not ready');
|
|
153
|
+
this.dcm.acceptChannel(channel, info);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
isReady(): boolean {
|
|
157
|
+
return !!this.sctp && this.sctp.state === 'established';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
close(): void {
|
|
161
|
+
if (this.sctp) try { this.sctp.shutdown(); } catch { /* best-effort */ }
|
|
162
|
+
if (this.dtls) try { this.dtls.close(); } catch { /* best-effort */ }
|
|
163
|
+
if (this.ice) try { this.ice.close(); } catch { /* best-effort */ }
|
|
164
|
+
}
|
|
165
|
+
}
|