node-rtc-connection 2.0.4 → 2.0.5
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/index.cjs +1 -0
- package/index.mjs +1 -0
- package/package.json +10 -47
- package/dist/index.cjs +0 -32
- package/dist/index.mjs +0 -43
- package/src/crypto/der.ts +0 -205
- package/src/crypto/x509.ts +0 -146
- package/src/datachannel/RTCDataChannel.ts +0 -388
- package/src/dtls/RTCCertificate.ts +0 -396
- package/src/dtls/cipher.ts +0 -198
- package/src/dtls/connection.ts +0 -974
- package/src/dtls/prf.ts +0 -62
- package/src/dtls/protocol.ts +0 -204
- package/src/foundation/ByteBufferQueue.ts +0 -237
- package/src/foundation/RTCError.ts +0 -276
- package/src/ice/RTCIceCandidate.ts +0 -349
- package/src/ice/ice-agent.ts +0 -609
- package/src/ice/stun-message.ts +0 -260
- package/src/index.ts +0 -72
- package/src/peerconnection/RTCPeerConnection.ts +0 -430
- package/src/sctp/association.ts +0 -523
- package/src/sctp/chunks.ts +0 -350
- package/src/sctp/crc32c.ts +0 -57
- package/src/sctp/datachannel-manager.ts +0 -187
- package/src/sctp/dcep.ts +0 -94
- package/src/sdp/RTCSessionDescription.ts +0 -115
- package/src/sdp/sdp-utils.ts +0 -229
- package/src/stun/stun-client.ts +0 -936
- package/src/transport-stack.ts +0 -165
- /package/{dist/types → types}/crypto/der.d.ts +0 -0
- /package/{dist/types → types}/crypto/x509.d.ts +0 -0
- /package/{dist/types → types}/datachannel/RTCDataChannel.d.ts +0 -0
- /package/{dist/types → types}/dtls/RTCCertificate.d.ts +0 -0
- /package/{dist/types → types}/dtls/cipher.d.ts +0 -0
- /package/{dist/types → types}/dtls/connection.d.ts +0 -0
- /package/{dist/types → types}/dtls/prf.d.ts +0 -0
- /package/{dist/types → types}/dtls/protocol.d.ts +0 -0
- /package/{dist/types → types}/foundation/ByteBufferQueue.d.ts +0 -0
- /package/{dist/types → types}/foundation/RTCError.d.ts +0 -0
- /package/{dist/types → types}/ice/RTCIceCandidate.d.ts +0 -0
- /package/{dist/types → types}/ice/ice-agent.d.ts +0 -0
- /package/{dist/types → types}/ice/stun-message.d.ts +0 -0
- /package/{dist/types → types}/index.d.ts +0 -0
- /package/{dist/types → types}/peerconnection/RTCPeerConnection.d.ts +0 -0
- /package/{dist/types → types}/sctp/association.d.ts +0 -0
- /package/{dist/types → types}/sctp/chunks.d.ts +0 -0
- /package/{dist/types → types}/sctp/crc32c.d.ts +0 -0
- /package/{dist/types → types}/sctp/datachannel-manager.d.ts +0 -0
- /package/{dist/types → types}/sctp/dcep.d.ts +0 -0
- /package/{dist/types → types}/sdp/RTCSessionDescription.d.ts +0 -0
- /package/{dist/types → types}/sdp/sdp-utils.d.ts +0 -0
- /package/{dist/types → types}/stun/stun-client.d.ts +0 -0
- /package/{dist/types → types}/transport-stack.d.ts +0 -0
|
@@ -1,388 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file RTCDataChannel.ts
|
|
3
|
-
* @description WebRTC DataChannel implementation for peer-to-peer data transfer.
|
|
4
|
-
* @module datachannel/RTCDataChannel
|
|
5
|
-
*
|
|
6
|
-
* Implements the W3C RTCDataChannel interface
|
|
7
|
-
* (https://www.w3.org/TR/webrtc/#rtcdatachannel).
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { EventEmitter } from 'events';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* RTCDataChannelState - Current state of the data channel
|
|
14
|
-
* @readonly
|
|
15
|
-
* @enum {string}
|
|
16
|
-
*/
|
|
17
|
-
export const RTCDataChannelState = Object.freeze({
|
|
18
|
-
CONNECTING: 'connecting',
|
|
19
|
-
OPEN: 'open',
|
|
20
|
-
CLOSING: 'closing',
|
|
21
|
-
CLOSED: 'closed'
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
type RTCDataChannelReadyState = 'connecting' | 'open' | 'closing' | 'closed';
|
|
25
|
-
type RTCDataChannelBinaryType = 'arraybuffer' | 'blob';
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Package-internal events that wire an RTCDataChannel to the SCTP transport.
|
|
29
|
-
* They are keyed by Symbol so they never collide with — or leak into — the
|
|
30
|
-
* public event surface ('open'/'message'/'close'/'error'/'bufferedamountlow').
|
|
31
|
-
* The SCTP data-channel manager and the channel communicate purely by emitting
|
|
32
|
-
* these on the channel's own EventEmitter:
|
|
33
|
-
*
|
|
34
|
-
* - SEND channel → transport: outbound frame `(data: Buffer, isBinary: boolean)`
|
|
35
|
-
* - RECEIVE transport → channel: inbound frame `(data: Buffer, isBinary: boolean)`
|
|
36
|
-
* - OPEN transport → channel: transition the channel to 'open'
|
|
37
|
-
* - SET_ID transport → channel: assign the SCTP stream id `(id: number)`
|
|
38
|
-
*/
|
|
39
|
-
export const RTCDataChannelEvents = Object.freeze({
|
|
40
|
-
SEND: Symbol('rtcdatachannel:send'),
|
|
41
|
-
RECEIVE: Symbol('rtcdatachannel:receive'),
|
|
42
|
-
OPEN: Symbol('rtcdatachannel:open'),
|
|
43
|
-
SET_ID: Symbol('rtcdatachannel:setId'),
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* RTCDataChannelInit - Configuration for creating a data channel
|
|
48
|
-
* @typedef {Object} RTCDataChannelInit
|
|
49
|
-
* @property {boolean} [ordered=true] - Whether messages must arrive in order
|
|
50
|
-
* @property {number} [maxPacketLifeTime] - Maximum packet lifetime in milliseconds
|
|
51
|
-
* @property {number} [maxRetransmits] - Maximum number of retransmissions
|
|
52
|
-
* @property {string} [protocol=''] - Subprotocol name
|
|
53
|
-
* @property {boolean} [negotiated=false] - Whether channel was negotiated out-of-band
|
|
54
|
-
* @property {number} [id] - Channel ID (required if negotiated is true)
|
|
55
|
-
*/
|
|
56
|
-
export interface RTCDataChannelInit {
|
|
57
|
-
ordered?: boolean;
|
|
58
|
-
maxPacketLifeTime?: number;
|
|
59
|
-
maxRetransmits?: number;
|
|
60
|
-
protocol?: string;
|
|
61
|
-
negotiated?: boolean;
|
|
62
|
-
id?: number;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* @class RTCDataChannel
|
|
67
|
-
* @extends EventEmitter
|
|
68
|
-
* @description Represents a bidirectional data channel between peers.
|
|
69
|
-
* Provides reliable or unreliable data transfer with configurable ordering.
|
|
70
|
-
*
|
|
71
|
-
* Events:
|
|
72
|
-
* - 'open': Fired when the channel opens
|
|
73
|
-
* - 'message': Fired when a message is received
|
|
74
|
-
* - 'bufferedamountlow': Fired when bufferedAmount drops below threshold
|
|
75
|
-
* - 'error': Fired when an error occurs
|
|
76
|
-
* - 'closing': Fired when the channel is closing
|
|
77
|
-
* - 'close': Fired when the channel closes
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* const dataChannel = peerConnection.createDataChannel('myChannel', {
|
|
81
|
-
* ordered: true,
|
|
82
|
-
* maxRetransmits: 3
|
|
83
|
-
* });
|
|
84
|
-
*
|
|
85
|
-
* dataChannel.on('open', () => {
|
|
86
|
-
* console.log('Channel opened');
|
|
87
|
-
* dataChannel.send('Hello!');
|
|
88
|
-
* });
|
|
89
|
-
*
|
|
90
|
-
* dataChannel.on('message', (event) => {
|
|
91
|
-
* console.log('Received:', event.data);
|
|
92
|
-
* });
|
|
93
|
-
*/
|
|
94
|
-
export class RTCDataChannel extends EventEmitter {
|
|
95
|
-
#label: string;
|
|
96
|
-
#ordered: boolean;
|
|
97
|
-
#maxPacketLifeTime: number | null;
|
|
98
|
-
#maxRetransmits: number | null;
|
|
99
|
-
#protocol: string;
|
|
100
|
-
#negotiated: boolean;
|
|
101
|
-
#id: number | null;
|
|
102
|
-
#readyState: RTCDataChannelReadyState;
|
|
103
|
-
#bufferedAmount: number;
|
|
104
|
-
#bufferedAmountLowThreshold: number;
|
|
105
|
-
#binaryType: RTCDataChannelBinaryType;
|
|
106
|
-
/** Whether a transport is listening for outbound SEND events. */
|
|
107
|
-
#connected: boolean;
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Create an RTCDataChannel instance.
|
|
111
|
-
* @param {string} label - Channel label
|
|
112
|
-
* @param {RTCDataChannelInit} [init] - Channel configuration
|
|
113
|
-
*/
|
|
114
|
-
constructor(label: string, init: RTCDataChannelInit = {}) {
|
|
115
|
-
super();
|
|
116
|
-
|
|
117
|
-
if (typeof label !== 'string') {
|
|
118
|
-
throw new TypeError('label must be a string');
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Channel configuration
|
|
122
|
-
this.#label = label;
|
|
123
|
-
this.#ordered = init.ordered !== undefined ? init.ordered : true;
|
|
124
|
-
this.#maxPacketLifeTime = init.maxPacketLifeTime || null;
|
|
125
|
-
this.#maxRetransmits = init.maxRetransmits || null;
|
|
126
|
-
this.#protocol = init.protocol || '';
|
|
127
|
-
this.#negotiated = init.negotiated || false;
|
|
128
|
-
this.#id = init.id !== undefined ? init.id : null;
|
|
129
|
-
|
|
130
|
-
// State
|
|
131
|
-
this.#readyState = RTCDataChannelState.CONNECTING as RTCDataChannelReadyState;
|
|
132
|
-
this.#bufferedAmount = 0;
|
|
133
|
-
this.#bufferedAmountLowThreshold = 0;
|
|
134
|
-
this.#binaryType = 'arraybuffer'; // or 'blob'
|
|
135
|
-
this.#connected = false;
|
|
136
|
-
|
|
137
|
-
// Transport drives the channel via internal (Symbol-keyed) events.
|
|
138
|
-
this.on(RTCDataChannelEvents.SET_ID, (id: number) => { this.#id = id; });
|
|
139
|
-
this.on(RTCDataChannelEvents.OPEN, () => {
|
|
140
|
-
this.#connected = true;
|
|
141
|
-
this.#setState(RTCDataChannelState.OPEN as RTCDataChannelReadyState);
|
|
142
|
-
});
|
|
143
|
-
this.on(RTCDataChannelEvents.RECEIVE, (data: Buffer, isBinary: boolean) => {
|
|
144
|
-
this.#receiveMessage(data, isBinary);
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Get the channel label.
|
|
150
|
-
* @returns {string} Channel label
|
|
151
|
-
*/
|
|
152
|
-
get label(): string {
|
|
153
|
-
return this.#label;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Check if messages are delivered in order.
|
|
158
|
-
* @returns {boolean} True if ordered
|
|
159
|
-
*/
|
|
160
|
-
get ordered(): boolean {
|
|
161
|
-
return this.#ordered;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Get the maximum packet lifetime in milliseconds.
|
|
166
|
-
* @returns {number|null} Maximum lifetime or null if not set
|
|
167
|
-
*/
|
|
168
|
-
get maxPacketLifeTime(): number | null {
|
|
169
|
-
return this.#maxPacketLifeTime;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Get the maximum number of retransmissions.
|
|
174
|
-
* @returns {number|null} Maximum retransmits or null if not set
|
|
175
|
-
*/
|
|
176
|
-
get maxRetransmits(): number | null {
|
|
177
|
-
return this.#maxRetransmits;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Get the subprotocol name.
|
|
182
|
-
* @returns {string} Protocol name
|
|
183
|
-
*/
|
|
184
|
-
get protocol(): string {
|
|
185
|
-
return this.#protocol;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Check if the channel was negotiated out-of-band.
|
|
190
|
-
* @returns {boolean} True if negotiated
|
|
191
|
-
*/
|
|
192
|
-
get negotiated(): boolean {
|
|
193
|
-
return this.#negotiated;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Get the channel ID.
|
|
198
|
-
* @returns {number|null} Channel ID or null if not assigned
|
|
199
|
-
*/
|
|
200
|
-
get id(): number | null {
|
|
201
|
-
return this.#id;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Get the current state of the channel.
|
|
206
|
-
* @returns {string} Channel state
|
|
207
|
-
*/
|
|
208
|
-
get readyState(): RTCDataChannelReadyState {
|
|
209
|
-
return this.#readyState;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Get the number of bytes queued to send.
|
|
214
|
-
* @returns {number} Buffered amount in bytes
|
|
215
|
-
*/
|
|
216
|
-
get bufferedAmount(): number {
|
|
217
|
-
return this.#bufferedAmount;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Get the threshold for bufferedamountlow event.
|
|
222
|
-
* @returns {number} Threshold in bytes
|
|
223
|
-
*/
|
|
224
|
-
get bufferedAmountLowThreshold(): number {
|
|
225
|
-
return this.#bufferedAmountLowThreshold;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Set the threshold for bufferedamountlow event.
|
|
230
|
-
* @param {number} value - Threshold in bytes
|
|
231
|
-
*/
|
|
232
|
-
set bufferedAmountLowThreshold(value: number) {
|
|
233
|
-
this.#bufferedAmountLowThreshold = value;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Get the binary data type.
|
|
238
|
-
* @returns {string} 'arraybuffer' or 'blob'
|
|
239
|
-
*/
|
|
240
|
-
get binaryType(): RTCDataChannelBinaryType {
|
|
241
|
-
return this.#binaryType;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Set the binary data type.
|
|
246
|
-
* @param {string} value - 'arraybuffer' or 'blob'
|
|
247
|
-
* @throws {TypeError} If value is invalid
|
|
248
|
-
*/
|
|
249
|
-
set binaryType(value: RTCDataChannelBinaryType) {
|
|
250
|
-
if (value !== 'arraybuffer' && value !== 'blob') {
|
|
251
|
-
throw new TypeError('binaryType must be "arraybuffer" or "blob"');
|
|
252
|
-
}
|
|
253
|
-
this.#binaryType = value;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Check if the channel is reliable (deprecated).
|
|
258
|
-
* @returns {boolean} True if ordered and no packet lifetime/retransmit limits
|
|
259
|
-
* @deprecated Use ordered, maxPacketLifeTime, and maxRetransmits instead
|
|
260
|
-
*/
|
|
261
|
-
get reliable(): boolean {
|
|
262
|
-
return this.#ordered &&
|
|
263
|
-
this.#maxPacketLifeTime === null &&
|
|
264
|
-
this.#maxRetransmits === null;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Send a message through the channel.
|
|
269
|
-
* @param {string|ArrayBuffer|ArrayBufferView|Blob} data - Data to send
|
|
270
|
-
* @throws {Error} If channel is not open or data is invalid
|
|
271
|
-
*/
|
|
272
|
-
send(data: string | ArrayBuffer | ArrayBufferView | Buffer): void {
|
|
273
|
-
if (this.#readyState !== RTCDataChannelState.OPEN) {
|
|
274
|
-
throw new Error('RTCDataChannel.readyState is not "open"');
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
let dataToSend: Buffer;
|
|
278
|
-
let byteLength = 0;
|
|
279
|
-
let isBinary: boolean;
|
|
280
|
-
|
|
281
|
-
if (typeof data === 'string') {
|
|
282
|
-
dataToSend = Buffer.from(data, 'utf8');
|
|
283
|
-
byteLength = dataToSend.length;
|
|
284
|
-
isBinary = false;
|
|
285
|
-
} else if (data instanceof ArrayBuffer) {
|
|
286
|
-
dataToSend = Buffer.from(data);
|
|
287
|
-
byteLength = data.byteLength;
|
|
288
|
-
isBinary = true;
|
|
289
|
-
} else if (ArrayBuffer.isView(data)) {
|
|
290
|
-
dataToSend = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
|
|
291
|
-
byteLength = data.byteLength;
|
|
292
|
-
isBinary = true;
|
|
293
|
-
} else if (Buffer.isBuffer(data)) {
|
|
294
|
-
dataToSend = data as Buffer;
|
|
295
|
-
byteLength = (data as Buffer).length;
|
|
296
|
-
isBinary = true;
|
|
297
|
-
} else if (data && typeof (data as { arrayBuffer?: unknown }).arrayBuffer === 'function') {
|
|
298
|
-
// Blob-like object
|
|
299
|
-
throw new Error('Blob sending not yet implemented');
|
|
300
|
-
} else {
|
|
301
|
-
throw new TypeError('Invalid data type');
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Emit the outbound frame for the transport to carry. The isBinary flag
|
|
305
|
-
// lets the peer reconstruct the right JS type; binary is transmitted as raw
|
|
306
|
-
// bytes (no JSON), avoiding corruption of Buffer/ArrayBuffer payloads.
|
|
307
|
-
if (!this.#connected) {
|
|
308
|
-
throw new Error('Data channel not connected to a transport');
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// Update buffered amount, then decrement once the transport has taken it.
|
|
312
|
-
this.#bufferedAmount += byteLength;
|
|
313
|
-
try {
|
|
314
|
-
this.emit(RTCDataChannelEvents.SEND, dataToSend, isBinary);
|
|
315
|
-
this.#bufferedAmount = Math.max(0, this.#bufferedAmount - byteLength);
|
|
316
|
-
this.#emitBufferedAmountLow();
|
|
317
|
-
} catch (err) {
|
|
318
|
-
this.#bufferedAmount = Math.max(0, this.#bufferedAmount - byteLength);
|
|
319
|
-
this.emit('error', err);
|
|
320
|
-
throw err;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/** Emit bufferedamountlow if appropriate. */
|
|
325
|
-
#emitBufferedAmountLow(): void {
|
|
326
|
-
if (this.#bufferedAmount <= this.#bufferedAmountLowThreshold) {
|
|
327
|
-
this.emit('bufferedamountlow');
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Close the data channel.
|
|
333
|
-
*/
|
|
334
|
-
close(): void {
|
|
335
|
-
if (this.#readyState === RTCDataChannelState.CLOSING ||
|
|
336
|
-
this.#readyState === RTCDataChannelState.CLOSED) {
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
this.#setState(RTCDataChannelState.CLOSING as RTCDataChannelReadyState);
|
|
341
|
-
|
|
342
|
-
// Transition to closed asynchronously
|
|
343
|
-
setImmediate(() => {
|
|
344
|
-
if (this.#readyState === RTCDataChannelState.CLOSING) {
|
|
345
|
-
this.#setState(RTCDataChannelState.CLOSED as RTCDataChannelReadyState);
|
|
346
|
-
}
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/** Set the channel state and emit the matching lifecycle event. */
|
|
351
|
-
#setState(newState: RTCDataChannelReadyState): void {
|
|
352
|
-
const oldState = this.#readyState;
|
|
353
|
-
if (oldState === newState) {
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
this.#readyState = newState;
|
|
358
|
-
|
|
359
|
-
// Emit state-specific events
|
|
360
|
-
if (newState === RTCDataChannelState.OPEN) {
|
|
361
|
-
this.emit('open');
|
|
362
|
-
} else if (newState === RTCDataChannelState.CLOSING) {
|
|
363
|
-
this.emit('closing');
|
|
364
|
-
} else if (newState === RTCDataChannelState.CLOSED) {
|
|
365
|
-
this.emit('close');
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Deliver a received message to listeners.
|
|
371
|
-
*
|
|
372
|
-
* Mirrors the browser RTCDataChannel: text frames surface as a string;
|
|
373
|
-
* binary frames surface as an ArrayBuffer (binaryType 'arraybuffer') or a
|
|
374
|
-
* Node Buffer (binaryType 'blob', which we approximate with Buffer since
|
|
375
|
-
* Node has no Blob in older runtimes).
|
|
376
|
-
*/
|
|
377
|
-
#receiveMessage(data: Buffer, isBinary: boolean): void {
|
|
378
|
-
let payload: string | ArrayBuffer | Buffer;
|
|
379
|
-
if (!isBinary) {
|
|
380
|
-
payload = data.toString('utf8');
|
|
381
|
-
} else if (this.#binaryType === 'arraybuffer') {
|
|
382
|
-
payload = (data.buffer as ArrayBuffer).slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
383
|
-
} else {
|
|
384
|
-
payload = data;
|
|
385
|
-
}
|
|
386
|
-
this.emit('message', { data: payload });
|
|
387
|
-
}
|
|
388
|
-
}
|