node-rtc-connection 1.0.19 → 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.
Files changed (64) hide show
  1. package/README.md +94 -85
  2. package/dist/index.cjs +20 -5606
  3. package/dist/index.mjs +25 -5598
  4. package/dist/types/crypto/der.d.ts +107 -0
  5. package/dist/types/crypto/x509.d.ts +56 -0
  6. package/dist/types/datachannel/RTCDataChannel.d.ts +179 -0
  7. package/dist/types/dtls/RTCCertificate.d.ts +163 -0
  8. package/dist/types/dtls/cipher.d.ts +81 -0
  9. package/dist/types/dtls/connection.d.ts +81 -0
  10. package/dist/types/dtls/prf.d.ts +29 -0
  11. package/dist/types/dtls/protocol.d.ts +127 -0
  12. package/dist/types/foundation/ByteBufferQueue.d.ts +71 -0
  13. package/dist/types/foundation/RTCError.d.ts +152 -0
  14. package/dist/types/ice/RTCIceCandidate.d.ts +161 -0
  15. package/dist/types/ice/ice-agent.d.ts +154 -0
  16. package/dist/types/ice/stun-message.d.ts +92 -0
  17. package/dist/types/index.d.ts +29 -0
  18. package/dist/types/peerconnection/RTCPeerConnection.d.ts +74 -0
  19. package/dist/types/sctp/association.d.ts +77 -0
  20. package/dist/types/sctp/chunks.d.ts +200 -0
  21. package/dist/types/sctp/crc32c.d.ts +24 -0
  22. package/dist/types/sctp/datachannel-manager.d.ts +51 -0
  23. package/dist/types/sctp/dcep.d.ts +56 -0
  24. package/dist/types/sdp/RTCSessionDescription.d.ts +73 -0
  25. package/dist/types/sdp/sdp-utils.d.ts +103 -0
  26. package/dist/types/stun/stun-client.d.ts +119 -0
  27. package/dist/types/transport-stack.d.ts +68 -0
  28. package/package.json +26 -21
  29. package/src/crypto/der.ts +205 -0
  30. package/src/crypto/x509.ts +146 -0
  31. package/src/datachannel/RTCDataChannel.ts +388 -0
  32. package/src/dtls/RTCCertificate.ts +396 -0
  33. package/src/dtls/cipher.ts +198 -0
  34. package/src/dtls/connection.ts +974 -0
  35. package/src/dtls/prf.ts +62 -0
  36. package/src/dtls/protocol.ts +204 -0
  37. package/src/foundation/{ByteBufferQueue.js → ByteBufferQueue.ts} +74 -72
  38. package/src/foundation/{RTCError.js → RTCError.ts} +110 -60
  39. package/src/ice/{RTCIceCandidate.js → RTCIceCandidate.ts} +140 -92
  40. package/src/ice/ice-agent.ts +609 -0
  41. package/src/ice/stun-message.ts +260 -0
  42. package/src/index.ts +72 -0
  43. package/src/peerconnection/RTCPeerConnection.ts +430 -0
  44. package/src/sctp/association.ts +523 -0
  45. package/src/sctp/chunks.ts +350 -0
  46. package/src/sctp/crc32c.ts +57 -0
  47. package/src/sctp/datachannel-manager.ts +187 -0
  48. package/src/sctp/dcep.ts +94 -0
  49. package/src/sdp/{RTCSessionDescription.js → RTCSessionDescription.ts} +42 -29
  50. package/src/sdp/sdp-utils.ts +229 -0
  51. package/src/stun/{stun-client.js → stun-client.ts} +346 -187
  52. package/src/transport-stack.ts +165 -0
  53. package/dist/index.cjs.map +0 -1
  54. package/dist/index.mjs.map +0 -1
  55. package/src/datachannel/RTCDataChannel.js +0 -354
  56. package/src/dtls/RTCCertificate.js +0 -310
  57. package/src/dtls/RTCDtlsTransport.js +0 -247
  58. package/src/ice/RTCIceTransport.js +0 -1018
  59. package/src/index.d.ts +0 -400
  60. package/src/index.js +0 -92
  61. package/src/network/network-transport.js +0 -478
  62. package/src/peerconnection/RTCPeerConnection.js +0 -875
  63. package/src/sctp/RTCSctpTransport.js +0 -253
  64. package/src/sdp/sdp-utils.js +0 -224
@@ -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
+ }