livekit-client 2.17.3 → 2.18.1
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/dist/livekit-client.e2ee.worker.js +1 -1
- package/dist/livekit-client.e2ee.worker.js.map +1 -1
- package/dist/livekit-client.e2ee.worker.mjs +8 -7
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +7823 -5772
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/api/SignalClient.d.ts +12 -4
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/e2ee/constants.d.ts.map +1 -1
- package/dist/src/e2ee/types.d.ts +6 -0
- package/dist/src/e2ee/types.d.ts.map +1 -1
- package/dist/src/e2ee/utils.d.ts +2 -1
- package/dist/src/e2ee/utils.d.ts.map +1 -1
- package/dist/src/e2ee/worker/DataCryptor.d.ts.map +1 -1
- package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
- package/dist/src/index.d.ts +5 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts +5 -0
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/PCTransportManager.d.ts +1 -1
- package/dist/src/room/PCTransportManager.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +27 -9
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +13 -3
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/data-stream/outgoing/OutgoingDataStreamManager.d.ts.map +1 -1
- package/dist/src/room/data-track/LocalDataTrack.d.ts +48 -0
- package/dist/src/room/data-track/LocalDataTrack.d.ts.map +1 -0
- package/dist/src/room/data-track/RemoteDataTrack.d.ts +46 -0
- package/dist/src/room/data-track/RemoteDataTrack.d.ts.map +1 -0
- package/dist/src/room/data-track/depacketizer.d.ts +6 -6
- package/dist/src/room/data-track/depacketizer.d.ts.map +1 -1
- package/dist/src/room/data-track/frame.d.ts +14 -0
- package/dist/src/room/data-track/frame.d.ts.map +1 -1
- package/dist/src/room/data-track/handle.d.ts +2 -2
- package/dist/src/room/data-track/handle.d.ts.map +1 -1
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts +104 -0
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts.map +1 -0
- package/dist/src/room/data-track/incoming/errors.d.ts +24 -0
- package/dist/src/room/data-track/incoming/errors.d.ts.map +1 -0
- package/dist/src/room/data-track/incoming/pipeline.d.ts +38 -0
- package/dist/src/room/data-track/incoming/pipeline.d.ts.map +1 -0
- package/dist/src/room/data-track/incoming/types.d.ts +20 -0
- package/dist/src/room/data-track/incoming/types.d.ts.map +1 -0
- package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +63 -28
- package/dist/src/room/data-track/outgoing/OutgoingDataTrackManager.d.ts.map +1 -1
- package/dist/src/room/data-track/outgoing/errors.d.ts +20 -10
- package/dist/src/room/data-track/outgoing/errors.d.ts.map +1 -1
- package/dist/src/room/data-track/outgoing/pipeline.d.ts +9 -8
- package/dist/src/room/data-track/outgoing/pipeline.d.ts.map +1 -1
- package/dist/src/room/data-track/outgoing/types.d.ts +16 -7
- package/dist/src/room/data-track/outgoing/types.d.ts.map +1 -1
- package/dist/src/room/data-track/packet/errors.d.ts +2 -4
- package/dist/src/room/data-track/packet/errors.d.ts.map +1 -1
- package/dist/src/room/data-track/packet/extensions.d.ts +4 -4
- package/dist/src/room/data-track/packet/extensions.d.ts.map +1 -1
- package/dist/src/room/data-track/packet/index.d.ts +5 -5
- package/dist/src/room/data-track/packet/index.d.ts.map +1 -1
- package/dist/src/room/data-track/packet/serializable.d.ts +4 -4
- package/dist/src/room/data-track/packet/serializable.d.ts.map +1 -1
- package/dist/src/room/data-track/packetizer.d.ts +6 -6
- package/dist/src/room/data-track/packetizer.d.ts.map +1 -1
- package/dist/src/room/data-track/track-interfaces.d.ts +23 -0
- package/dist/src/room/data-track/track-interfaces.d.ts.map +1 -0
- package/dist/src/room/data-track/types.d.ts +15 -0
- package/dist/src/room/data-track/types.d.ts.map +1 -0
- package/dist/src/room/events.d.ts +24 -3
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +11 -1
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +14 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/token-source/TokenSource.d.ts +1 -1
- package/dist/src/room/token-source/TokenSource.d.ts.map +1 -1
- package/dist/src/room/token-source/types.d.ts +1 -0
- package/dist/src/room/token-source/types.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +2 -1
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/utils/abort-signal-polyfill.d.ts +13 -0
- package/dist/src/utils/abort-signal-polyfill.d.ts.map +1 -0
- package/dist/src/utils/deferrable-map.d.ts +32 -0
- package/dist/src/utils/deferrable-map.d.ts.map +1 -0
- package/dist/src/utils/subscribeToEvents.d.ts +3 -0
- package/dist/src/utils/subscribeToEvents.d.ts.map +1 -1
- package/dist/ts4.2/api/SignalClient.d.ts +12 -4
- package/dist/ts4.2/e2ee/types.d.ts +6 -0
- package/dist/ts4.2/e2ee/utils.d.ts +2 -1
- package/dist/ts4.2/index.d.ts +5 -4
- package/dist/ts4.2/room/PCTransport.d.ts +5 -0
- package/dist/ts4.2/room/PCTransportManager.d.ts +1 -1
- package/dist/ts4.2/room/RTCEngine.d.ts +27 -9
- package/dist/ts4.2/room/Room.d.ts +13 -3
- package/dist/ts4.2/room/data-track/LocalDataTrack.d.ts +48 -0
- package/dist/ts4.2/room/data-track/RemoteDataTrack.d.ts +46 -0
- package/dist/ts4.2/room/data-track/depacketizer.d.ts +6 -6
- package/dist/ts4.2/room/data-track/frame.d.ts +14 -0
- package/dist/ts4.2/room/data-track/handle.d.ts +2 -2
- package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +110 -0
- package/dist/ts4.2/room/data-track/incoming/errors.d.ts +24 -0
- package/dist/ts4.2/room/data-track/incoming/pipeline.d.ts +38 -0
- package/dist/ts4.2/room/data-track/incoming/types.d.ts +20 -0
- package/dist/ts4.2/room/data-track/outgoing/OutgoingDataTrackManager.d.ts +63 -29
- package/dist/ts4.2/room/data-track/outgoing/errors.d.ts +20 -10
- package/dist/ts4.2/room/data-track/outgoing/pipeline.d.ts +9 -8
- package/dist/ts4.2/room/data-track/outgoing/types.d.ts +16 -7
- package/dist/ts4.2/room/data-track/packet/errors.d.ts +2 -4
- package/dist/ts4.2/room/data-track/packet/extensions.d.ts +4 -4
- package/dist/ts4.2/room/data-track/packet/index.d.ts +5 -6
- package/dist/ts4.2/room/data-track/packet/serializable.d.ts +4 -4
- package/dist/ts4.2/room/data-track/packetizer.d.ts +6 -6
- package/dist/ts4.2/room/data-track/track-interfaces.d.ts +23 -0
- package/dist/ts4.2/room/data-track/types.d.ts +15 -0
- package/dist/ts4.2/room/events.d.ts +24 -3
- package/dist/ts4.2/room/participant/LocalParticipant.d.ts +11 -1
- package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +14 -1
- package/dist/ts4.2/room/token-source/TokenSource.d.ts +1 -1
- package/dist/ts4.2/room/token-source/types.d.ts +1 -0
- package/dist/ts4.2/room/utils.d.ts +2 -1
- package/dist/ts4.2/utils/abort-signal-polyfill.d.ts +13 -0
- package/dist/ts4.2/utils/deferrable-map.d.ts +32 -0
- package/dist/ts4.2/utils/subscribeToEvents.d.ts +3 -0
- package/package.json +4 -2
- package/src/api/SignalClient.test.ts +9 -4
- package/src/api/SignalClient.ts +116 -9
- package/src/e2ee/constants.ts +1 -0
- package/src/e2ee/types.ts +6 -0
- package/src/e2ee/utils.ts +4 -3
- package/src/e2ee/worker/DataCryptor.ts +1 -4
- package/src/e2ee/worker/FrameCryptor.ts +1 -4
- package/src/e2ee/worker/ParticipantKeyHandler.ts +1 -1
- package/src/index.ts +6 -4
- package/src/room/PCTransport.ts +41 -1
- package/src/room/PCTransportManager.ts +1 -1
- package/src/room/RTCEngine.ts +274 -112
- package/src/room/Room.ts +152 -15
- package/src/room/data-stream/outgoing/OutgoingDataStreamManager.ts +9 -9
- package/src/room/data-track/LocalDataTrack.ts +126 -0
- package/src/room/data-track/RemoteDataTrack.ts +80 -0
- package/src/room/data-track/depacketizer.ts +23 -26
- package/src/room/data-track/frame.ts +28 -2
- package/src/room/data-track/handle.ts +2 -8
- package/src/room/data-track/incoming/IncomingDataTrackManager.test.ts +555 -0
- package/src/room/data-track/incoming/IncomingDataTrackManager.ts +589 -0
- package/src/room/data-track/incoming/errors.ts +57 -0
- package/src/room/data-track/incoming/pipeline.ts +121 -0
- package/src/room/data-track/incoming/types.ts +22 -0
- package/src/room/data-track/outgoing/OutgoingDataTrackManager.test.ts +240 -27
- package/src/room/data-track/outgoing/OutgoingDataTrackManager.ts +165 -84
- package/src/room/data-track/outgoing/errors.ts +40 -11
- package/src/room/data-track/outgoing/pipeline.ts +25 -23
- package/src/room/data-track/outgoing/types.ts +14 -6
- package/src/room/data-track/packet/errors.ts +2 -14
- package/src/room/data-track/packet/extensions.ts +21 -26
- package/src/room/data-track/packet/index.test.ts +22 -33
- package/src/room/data-track/packet/index.ts +4 -6
- package/src/room/data-track/packet/serializable.ts +4 -4
- package/src/room/data-track/packetizer.test.ts +2 -2
- package/src/room/data-track/packetizer.ts +7 -10
- package/src/room/data-track/track-interfaces.ts +53 -0
- package/src/room/data-track/types.ts +31 -0
- package/src/room/events.ts +26 -1
- package/src/room/participant/LocalParticipant.ts +57 -6
- package/src/room/participant/RemoteParticipant.ts +26 -1
- package/src/room/token-source/TokenSource.ts +8 -2
- package/src/room/token-source/types.ts +4 -0
- package/src/room/utils.ts +5 -1
- package/src/utils/abort-signal-polyfill.ts +63 -0
- package/src/utils/deferrable-map.ts +109 -0
- package/src/utils/subscribeToEvents.ts +18 -1
- package/dist/src/room/data-track/e2ee.d.ts +0 -12
- package/dist/src/room/data-track/e2ee.d.ts.map +0 -1
- package/dist/src/room/data-track/track.d.ts +0 -30
- package/dist/src/room/data-track/track.d.ts.map +0 -1
- package/dist/src/utils/throws.d.ts +0 -36
- package/dist/src/utils/throws.d.ts.map +0 -1
- package/dist/ts4.2/room/data-track/e2ee.d.ts +0 -12
- package/dist/ts4.2/room/data-track/track.d.ts +0 -30
- package/dist/ts4.2/utils/throws.d.ts +0 -39
- package/src/room/data-track/e2ee.ts +0 -14
- package/src/room/data-track/track.ts +0 -50
- package/src/utils/throws.ts +0 -42
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { Throws } from '@livekit/throws-transformer/throws';
|
|
2
|
+
import type { BaseE2EEManager } from '../../../e2ee/E2eeManager';
|
|
3
|
+
import { LoggerNames, getLogger } from '../../../logger';
|
|
4
|
+
import DataTrackDepacketizer, { DataTrackDepacketizerDropError } from '../depacketizer';
|
|
5
|
+
import type { DataTrackFrameInternal } from '../frame';
|
|
6
|
+
import { DataTrackPacket } from '../packet';
|
|
7
|
+
import { type DataTrackInfo } from '../types';
|
|
8
|
+
|
|
9
|
+
const log = getLogger(LoggerNames.DataTracks);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Options for creating a {@link IncomingDataTrackPipeline}.
|
|
13
|
+
*/
|
|
14
|
+
type Options = {
|
|
15
|
+
info: DataTrackInfo;
|
|
16
|
+
publisherIdentity: string;
|
|
17
|
+
e2eeManager: BaseE2EEManager | null;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Pipeline for an individual data track subscription.
|
|
22
|
+
*/
|
|
23
|
+
export default class IncomingDataTrackPipeline {
|
|
24
|
+
private publisherIdentity: string;
|
|
25
|
+
|
|
26
|
+
private e2eeManager: BaseE2EEManager | null;
|
|
27
|
+
|
|
28
|
+
private depacketizer: DataTrackDepacketizer;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Creates a new pipeline with the given options.
|
|
32
|
+
*/
|
|
33
|
+
constructor(options: Options) {
|
|
34
|
+
const hasProvider = options.e2eeManager !== null;
|
|
35
|
+
if (options.info.usesE2ee !== hasProvider) {
|
|
36
|
+
// @throws-transformer ignore - this should be treated as a "panic" and not be caught
|
|
37
|
+
throw new Error(
|
|
38
|
+
'IncomingDataTrackPipeline: DataTrackInfo.usesE2ee must match presence of decryptionProvider',
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const depacketizer = new DataTrackDepacketizer();
|
|
43
|
+
|
|
44
|
+
this.publisherIdentity = options.publisherIdentity;
|
|
45
|
+
this.e2eeManager = options.e2eeManager ?? null;
|
|
46
|
+
this.depacketizer = depacketizer;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
updateE2eeManager(e2eeManager: BaseE2EEManager | null) {
|
|
50
|
+
this.e2eeManager = e2eeManager;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async processPacket(
|
|
54
|
+
packet: DataTrackPacket,
|
|
55
|
+
): Promise<Throws<DataTrackFrameInternal | null, DataTrackDepacketizerDropError>> {
|
|
56
|
+
const frame = this.depacketize(packet);
|
|
57
|
+
if (!frame) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const decrypted = await this.decryptIfNeeded(frame);
|
|
62
|
+
if (!decrypted) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return decrypted;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Depacketize the given frame, log if a drop occurs.
|
|
71
|
+
*/
|
|
72
|
+
private depacketize(
|
|
73
|
+
packet: DataTrackPacket,
|
|
74
|
+
): Throws<DataTrackFrameInternal | null, DataTrackDepacketizerDropError> {
|
|
75
|
+
let frame: DataTrackFrameInternal | null;
|
|
76
|
+
try {
|
|
77
|
+
frame = this.depacketizer.push(packet);
|
|
78
|
+
} catch (err) {
|
|
79
|
+
// In a future version, use this to maintain drop statistics.
|
|
80
|
+
// FIXME: is this a good idea?
|
|
81
|
+
log.warn(`Data frame depacketize error: ${err}`);
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
return frame;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Decrypt the frame's payload if E2EE is enabled for this track.
|
|
89
|
+
*/
|
|
90
|
+
private async decryptIfNeeded(
|
|
91
|
+
frame: DataTrackFrameInternal,
|
|
92
|
+
): Promise<DataTrackFrameInternal | null> {
|
|
93
|
+
const e2eeManager = this.e2eeManager;
|
|
94
|
+
|
|
95
|
+
if (!e2eeManager) {
|
|
96
|
+
return frame;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const e2ee = frame.extensions?.e2ee ?? null;
|
|
100
|
+
if (!e2ee) {
|
|
101
|
+
log.error('Missing E2EE meta');
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let result;
|
|
106
|
+
try {
|
|
107
|
+
result = await e2eeManager.handleEncryptedData(
|
|
108
|
+
frame.payload,
|
|
109
|
+
e2ee.iv,
|
|
110
|
+
this.publisherIdentity,
|
|
111
|
+
e2ee.keyIndex,
|
|
112
|
+
);
|
|
113
|
+
} catch (err) {
|
|
114
|
+
log.error(`Error decrypting packet: ${err}`);
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
frame.payload = result.payload;
|
|
119
|
+
return frame;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type Participant from '../../participant/Participant';
|
|
2
|
+
import type RemoteDataTrack from '../RemoteDataTrack';
|
|
3
|
+
import { type DataTrackSid } from '../types';
|
|
4
|
+
|
|
5
|
+
/** Request sent to the SFU to update the subscription for a data track. */
|
|
6
|
+
export type EventSfuUpdateSubscription = {
|
|
7
|
+
/** Identifier of the affected track. */
|
|
8
|
+
sid: DataTrackSid;
|
|
9
|
+
/** Whether to subscribe or unsubscribe. */
|
|
10
|
+
subscribe: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/** A track has been published by a remote participant and is available to be subscribed to. */
|
|
14
|
+
export type EventTrackAvailable = {
|
|
15
|
+
track: RemoteDataTrack;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** A track has been unpublished by a remote participant and can no longer be subscribed to. */
|
|
19
|
+
export type EventTrackUnavailable = {
|
|
20
|
+
sid: DataTrackSid;
|
|
21
|
+
publisherIdentity: Participant['identity'];
|
|
22
|
+
};
|
|
@@ -1,31 +1,76 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
2
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import {
|
|
4
|
+
type DecryptDataResponseMessage,
|
|
5
|
+
type EncryptDataResponseMessage,
|
|
6
|
+
LocalDataTrack,
|
|
7
|
+
} from '../../..';
|
|
8
|
+
import { type BaseE2EEManager } from '../../../e2ee/E2eeManager';
|
|
3
9
|
import { subscribeToEvents } from '../../../utils/subscribeToEvents';
|
|
4
|
-
import
|
|
10
|
+
import RTCEngine from '../../RTCEngine';
|
|
11
|
+
import Room from '../../Room';
|
|
5
12
|
import { DataTrackHandle } from '../handle';
|
|
6
13
|
import { DataTrackPacket, FrameMarker } from '../packet';
|
|
7
14
|
import OutgoingDataTrackManager, {
|
|
8
|
-
DataTrackOutgoingManagerCallbacks,
|
|
15
|
+
type DataTrackOutgoingManagerCallbacks,
|
|
9
16
|
Descriptor,
|
|
10
17
|
} from './OutgoingDataTrackManager';
|
|
11
18
|
import { DataTrackPublishError } from './errors';
|
|
12
19
|
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
/** Fake encryption provider for testing e2ee data track features. */
|
|
21
|
+
export class PrefixingEncryptionProvider implements BaseE2EEManager {
|
|
22
|
+
isEnabled = true;
|
|
23
|
+
|
|
24
|
+
isDataChannelEncryptionEnabled = true;
|
|
25
|
+
|
|
26
|
+
setup(_room: Room) {}
|
|
27
|
+
|
|
28
|
+
setupEngine(_engine: RTCEngine) {}
|
|
29
|
+
|
|
30
|
+
setParticipantCryptorEnabled(_enabled: boolean, _participantIdentity: string) {}
|
|
31
|
+
|
|
32
|
+
setSifTrailer(_trailer: Uint8Array) {}
|
|
33
|
+
|
|
34
|
+
on(_event: any, _listener: any): this {
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** A fake "encryption" provider used for test purposes. Adds a prefix to the payload. */
|
|
39
|
+
async encryptData(data: Uint8Array): Promise<EncryptDataResponseMessage['data']> {
|
|
16
40
|
const prefix = new Uint8Array([0xde, 0xad, 0xbe, 0xef]);
|
|
17
41
|
|
|
18
|
-
const output = new Uint8Array(prefix.length +
|
|
42
|
+
const output = new Uint8Array(prefix.length + data.length);
|
|
19
43
|
output.set(prefix, 0);
|
|
20
|
-
output.set(
|
|
44
|
+
output.set(data, prefix.length);
|
|
21
45
|
|
|
22
46
|
return {
|
|
47
|
+
uuid: crypto.randomUUID(),
|
|
23
48
|
payload: output,
|
|
24
49
|
iv: new Uint8Array(12), // Just leaving this empty, is this a bad idea?
|
|
25
50
|
keyIndex: 0,
|
|
26
51
|
};
|
|
27
|
-
}
|
|
28
|
-
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** A fake "decryption" provider used for test purposes. Assumes the payload is prefixed with
|
|
55
|
+
* 0xdeafbeef, which is stripped off. */
|
|
56
|
+
async handleEncryptedData(
|
|
57
|
+
payload: Uint8Array,
|
|
58
|
+
_iv: Uint8Array,
|
|
59
|
+
_participantIdentity: string,
|
|
60
|
+
_keyIndex: number,
|
|
61
|
+
): Promise<DecryptDataResponseMessage['data']> {
|
|
62
|
+
if (payload[0] !== 0xde || payload[1] !== 0xad || payload[2] !== 0xbe || payload[3] !== 0xef) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`PrefixingEncryptionProvider: first four bytes of payload were not 0xdeadbeef, found ${payload.slice(0, 4)}`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
uuid: crypto.randomUUID(),
|
|
70
|
+
payload: payload.slice(4),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
29
74
|
|
|
30
75
|
describe('DataTrackOutgoingManager', () => {
|
|
31
76
|
it('should test track publishing (ok case)', async () => {
|
|
@@ -34,8 +79,11 @@ describe('DataTrackOutgoingManager', () => {
|
|
|
34
79
|
'sfuPublishRequest',
|
|
35
80
|
]);
|
|
36
81
|
|
|
82
|
+
const localDataTrack = new LocalDataTrack({ name: 'test' }, manager);
|
|
83
|
+
expect(localDataTrack.isPublished()).toStrictEqual(false);
|
|
84
|
+
|
|
37
85
|
// 1. Publish a data track
|
|
38
|
-
const publishRequestPromise =
|
|
86
|
+
const publishRequestPromise = localDataTrack.publish();
|
|
39
87
|
|
|
40
88
|
// 2. This publish request should be sent along to the SFU
|
|
41
89
|
const sfuPublishEvent = await managerEvents.waitFor('sfuPublishRequest');
|
|
@@ -55,7 +103,7 @@ describe('DataTrackOutgoingManager', () => {
|
|
|
55
103
|
});
|
|
56
104
|
|
|
57
105
|
// Make sure that the original input event resolves.
|
|
58
|
-
|
|
106
|
+
await publishRequestPromise;
|
|
59
107
|
expect(localDataTrack.isPublished()).toStrictEqual(true);
|
|
60
108
|
});
|
|
61
109
|
|
|
@@ -66,7 +114,8 @@ describe('DataTrackOutgoingManager', () => {
|
|
|
66
114
|
]);
|
|
67
115
|
|
|
68
116
|
// 1. Publish a data track
|
|
69
|
-
const
|
|
117
|
+
const localDataTrack = new LocalDataTrack({ name: 'test' }, manager);
|
|
118
|
+
const publishRequestPromise = localDataTrack.publish();
|
|
70
119
|
|
|
71
120
|
// 2. This publish request should be sent along to the SFU
|
|
72
121
|
const sfuPublishEvent = await managerEvents.waitFor('sfuPublishRequest');
|
|
@@ -78,7 +127,9 @@ describe('DataTrackOutgoingManager', () => {
|
|
|
78
127
|
});
|
|
79
128
|
|
|
80
129
|
// Make sure that the rejection bubbles back to the caller
|
|
81
|
-
expect(publishRequestPromise).rejects.toThrowError(
|
|
130
|
+
await expect(publishRequestPromise).rejects.toThrowError(
|
|
131
|
+
'Data track publication limit reached',
|
|
132
|
+
);
|
|
82
133
|
});
|
|
83
134
|
|
|
84
135
|
it('should test track publishing (cancellation half way through)', async () => {
|
|
@@ -90,7 +141,8 @@ describe('DataTrackOutgoingManager', () => {
|
|
|
90
141
|
|
|
91
142
|
// 1. Publish a data track
|
|
92
143
|
const controller = new AbortController();
|
|
93
|
-
const
|
|
144
|
+
const localDataTrack = new LocalDataTrack({ name: 'test' }, manager);
|
|
145
|
+
const publishRequestPromise = localDataTrack.publish(controller.signal);
|
|
94
146
|
|
|
95
147
|
// 2. This publish request should be sent along to the SFU
|
|
96
148
|
const sfuPublishEvent = await managerEvents.waitFor('sfuPublishRequest');
|
|
@@ -107,7 +159,96 @@ describe('DataTrackOutgoingManager', () => {
|
|
|
107
159
|
expect(sfuUnpublishEvent.handle).toStrictEqual(handle);
|
|
108
160
|
|
|
109
161
|
// 5. Make sure cancellation is bubbled up as an error to stop further execution
|
|
110
|
-
expect(publishRequestPromise).rejects.toStrictEqual(DataTrackPublishError.cancelled());
|
|
162
|
+
await expect(publishRequestPromise).rejects.toStrictEqual(DataTrackPublishError.cancelled());
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should test track publishing (cancellation before it starts)', async () => {
|
|
166
|
+
const manager = new OutgoingDataTrackManager();
|
|
167
|
+
const managerEvents = subscribeToEvents<DataTrackOutgoingManagerCallbacks>(manager, [
|
|
168
|
+
'sfuPublishRequest',
|
|
169
|
+
'sfuUnpublishRequest',
|
|
170
|
+
]);
|
|
171
|
+
|
|
172
|
+
// Publish a data track
|
|
173
|
+
const localDataTrack = new LocalDataTrack({ name: 'test' }, manager);
|
|
174
|
+
const publishRequestPromise = localDataTrack.publish(AbortSignal.abort(/* already aborted */));
|
|
175
|
+
|
|
176
|
+
// Make sure cancellation is immediately bubbled up
|
|
177
|
+
await expect(publishRequestPromise).rejects.toStrictEqual(DataTrackPublishError.cancelled());
|
|
178
|
+
|
|
179
|
+
// And there were no pending sfu publish requests sent
|
|
180
|
+
expect(managerEvents.areThereBufferedEvents('sfuPublishRequest')).toBe(false);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should test track publishing, unpublishing, and republishing again', async () => {
|
|
184
|
+
const manager = new OutgoingDataTrackManager();
|
|
185
|
+
const managerEvents = subscribeToEvents<DataTrackOutgoingManagerCallbacks>(manager, [
|
|
186
|
+
'sfuPublishRequest',
|
|
187
|
+
'sfuUnpublishRequest',
|
|
188
|
+
]);
|
|
189
|
+
|
|
190
|
+
// 1. Create a local data track
|
|
191
|
+
const localDataTrack = new LocalDataTrack({ name: 'test' }, manager);
|
|
192
|
+
expect(localDataTrack.isPublished()).toStrictEqual(false);
|
|
193
|
+
|
|
194
|
+
// 2. Publish it
|
|
195
|
+
const publishRequestPromise = localDataTrack.publish();
|
|
196
|
+
|
|
197
|
+
// 3. This publish request should be sent along to the SFU
|
|
198
|
+
const sfuPublishEvent = await managerEvents.waitFor('sfuPublishRequest');
|
|
199
|
+
expect(sfuPublishEvent.name).toStrictEqual('test');
|
|
200
|
+
expect(sfuPublishEvent.usesE2ee).toStrictEqual(false);
|
|
201
|
+
const handle = sfuPublishEvent.handle;
|
|
202
|
+
|
|
203
|
+
// 4. Respond to the SFU publish request with an OK response
|
|
204
|
+
manager.receivedSfuPublishResponse(handle, {
|
|
205
|
+
type: 'ok',
|
|
206
|
+
data: {
|
|
207
|
+
sid: 'bogus-sid',
|
|
208
|
+
pubHandle: sfuPublishEvent.handle,
|
|
209
|
+
name: 'test',
|
|
210
|
+
usesE2ee: false,
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Make sure that the original input event resolves.
|
|
215
|
+
await publishRequestPromise;
|
|
216
|
+
|
|
217
|
+
// 5. Now the data track should be published
|
|
218
|
+
expect(localDataTrack.isPublished()).toStrictEqual(true);
|
|
219
|
+
|
|
220
|
+
// 6. Unpublish the data track
|
|
221
|
+
const unpublishRequestPromise = localDataTrack.unpublish();
|
|
222
|
+
const sfuUnpublishEvent = await managerEvents.waitFor('sfuUnpublishRequest');
|
|
223
|
+
manager.receivedSfuUnpublishResponse(sfuUnpublishEvent.handle);
|
|
224
|
+
await unpublishRequestPromise;
|
|
225
|
+
|
|
226
|
+
// 7. Now the data track should be unpublished
|
|
227
|
+
expect(localDataTrack.isPublished()).toStrictEqual(false);
|
|
228
|
+
|
|
229
|
+
// 8. Now, republish the track and make sure that be done a second time
|
|
230
|
+
const publishRequestPromise2 = localDataTrack.publish();
|
|
231
|
+
const sfuPublishEvent2 = await managerEvents.waitFor('sfuPublishRequest');
|
|
232
|
+
expect(sfuPublishEvent2.name).toStrictEqual('test');
|
|
233
|
+
expect(sfuPublishEvent2.usesE2ee).toStrictEqual(false);
|
|
234
|
+
const handle2 = sfuPublishEvent2.handle;
|
|
235
|
+
manager.receivedSfuPublishResponse(handle2, {
|
|
236
|
+
type: 'ok',
|
|
237
|
+
data: {
|
|
238
|
+
sid: 'bogus-sid',
|
|
239
|
+
pubHandle: sfuPublishEvent2.handle,
|
|
240
|
+
name: 'test',
|
|
241
|
+
usesE2ee: false,
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
await publishRequestPromise2;
|
|
245
|
+
|
|
246
|
+
// 9. Ensure that the track is published again
|
|
247
|
+
expect(localDataTrack.isPublished()).toStrictEqual(true);
|
|
248
|
+
|
|
249
|
+
// 10. Also ensure that the handle used on the second publish attempt differs from the first
|
|
250
|
+
// publish attempt.
|
|
251
|
+
expect(handle).not.toStrictEqual(handle2);
|
|
111
252
|
});
|
|
112
253
|
|
|
113
254
|
it.each([
|
|
@@ -187,18 +328,17 @@ describe('DataTrackOutgoingManager', () => {
|
|
|
187
328
|
]),
|
|
188
329
|
);
|
|
189
330
|
const managerEvents = subscribeToEvents<DataTrackOutgoingManagerCallbacks>(manager, [
|
|
190
|
-
'
|
|
331
|
+
'packetAvailable',
|
|
191
332
|
]);
|
|
192
333
|
|
|
193
|
-
const localDataTrack =
|
|
194
|
-
expect(localDataTrack).not.toStrictEqual(null);
|
|
334
|
+
const localDataTrack = LocalDataTrack.withExplicitHandle({ name: 'track name' }, manager, 5);
|
|
195
335
|
|
|
196
336
|
// Kick off sending the bytes...
|
|
197
|
-
localDataTrack.tryPush(inputBytes);
|
|
337
|
+
localDataTrack.tryPush({ payload: inputBytes });
|
|
198
338
|
|
|
199
339
|
// ... and make sure the corresponding events are emitted to tell the SFU to send the packets
|
|
200
340
|
for (const outputPacketJson of outputPacketsJson) {
|
|
201
|
-
const packetBytes = await managerEvents.waitFor('
|
|
341
|
+
const packetBytes = await managerEvents.waitFor('packetAvailable');
|
|
202
342
|
const [packet] = DataTrackPacket.fromBinary(packetBytes.bytes);
|
|
203
343
|
|
|
204
344
|
expect(packet.toJSON()).toStrictEqual(outputPacketJson);
|
|
@@ -208,15 +348,16 @@ describe('DataTrackOutgoingManager', () => {
|
|
|
208
348
|
|
|
209
349
|
it('should send e2ee encrypted datatrack payload', async () => {
|
|
210
350
|
const manager = new OutgoingDataTrackManager({
|
|
211
|
-
|
|
351
|
+
e2eeManager: new PrefixingEncryptionProvider(),
|
|
212
352
|
});
|
|
213
353
|
const managerEvents = subscribeToEvents<DataTrackOutgoingManagerCallbacks>(manager, [
|
|
214
354
|
'sfuPublishRequest',
|
|
215
|
-
'
|
|
355
|
+
'packetAvailable',
|
|
216
356
|
]);
|
|
217
357
|
|
|
218
358
|
// 1. Publish a data track
|
|
219
|
-
const
|
|
359
|
+
const localDataTrack = new LocalDataTrack({ name: 'test' }, manager);
|
|
360
|
+
const publishRequestPromise = localDataTrack.publish();
|
|
220
361
|
|
|
221
362
|
// 2. This publish request should be sent along to the SFU
|
|
222
363
|
const sfuPublishEvent = await managerEvents.waitFor('sfuPublishRequest');
|
|
@@ -236,14 +377,14 @@ describe('DataTrackOutgoingManager', () => {
|
|
|
236
377
|
});
|
|
237
378
|
|
|
238
379
|
// Get the connected local data track
|
|
239
|
-
|
|
380
|
+
await publishRequestPromise;
|
|
240
381
|
expect(localDataTrack.isPublished()).toStrictEqual(true);
|
|
241
382
|
|
|
242
383
|
// Kick off sending the payload bytes
|
|
243
|
-
localDataTrack.tryPush(new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05]));
|
|
384
|
+
localDataTrack.tryPush({ payload: new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05]) });
|
|
244
385
|
|
|
245
386
|
// Make sure the packet that was sent was encrypted with the PrefixingEncryptionProvider
|
|
246
|
-
const packetBytes = await managerEvents.waitFor('
|
|
387
|
+
const packetBytes = await managerEvents.waitFor('packetAvailable');
|
|
247
388
|
const [packet] = DataTrackPacket.fromBinary(packetBytes.bytes);
|
|
248
389
|
|
|
249
390
|
expect(packet.toJSON()).toStrictEqual({
|
|
@@ -311,6 +452,76 @@ describe('DataTrackOutgoingManager', () => {
|
|
|
311
452
|
expect(manager.getDescriptor(5)).toStrictEqual(null);
|
|
312
453
|
});
|
|
313
454
|
|
|
455
|
+
it('should test a full reconnect', async () => {
|
|
456
|
+
const pubHandle = 5;
|
|
457
|
+
// Create a manager prefilled with a descriptor
|
|
458
|
+
const manager = OutgoingDataTrackManager.withDescriptors(
|
|
459
|
+
new Map([
|
|
460
|
+
[
|
|
461
|
+
DataTrackHandle.fromNumber(5),
|
|
462
|
+
Descriptor.active(
|
|
463
|
+
{
|
|
464
|
+
sid: 'bogus-sid',
|
|
465
|
+
pubHandle,
|
|
466
|
+
name: 'test',
|
|
467
|
+
usesE2ee: false,
|
|
468
|
+
},
|
|
469
|
+
null,
|
|
470
|
+
),
|
|
471
|
+
],
|
|
472
|
+
]),
|
|
473
|
+
);
|
|
474
|
+
const managerEvents = subscribeToEvents<DataTrackOutgoingManagerCallbacks>(manager, [
|
|
475
|
+
'sfuPublishRequest',
|
|
476
|
+
'packetAvailable',
|
|
477
|
+
'sfuUnpublishRequest',
|
|
478
|
+
]);
|
|
479
|
+
const localDataTrack = LocalDataTrack.withExplicitHandle({ name: 'track name' }, manager, 5);
|
|
480
|
+
|
|
481
|
+
// Make sure the descriptor is in there
|
|
482
|
+
expect(manager.getDescriptor(5)?.type).toStrictEqual('active');
|
|
483
|
+
|
|
484
|
+
// Simulate a full reconnect, which means that any published tracks will need to be republished.
|
|
485
|
+
manager.sfuWillRepublishTracks();
|
|
486
|
+
|
|
487
|
+
// Even though behind the scenes the SFU publications are not active, the user should still see
|
|
488
|
+
// it as "published", sfu reconnects are an implementation detail
|
|
489
|
+
expect(localDataTrack.isPublished()).toStrictEqual(true);
|
|
490
|
+
|
|
491
|
+
// But, even though `isPublished` is true, pushing data should drop (no sfu to send them to!)
|
|
492
|
+
await expect(() =>
|
|
493
|
+
localDataTrack.tryPush({ payload: new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05]) }),
|
|
494
|
+
).rejects.toThrowError('Frame was dropped');
|
|
495
|
+
|
|
496
|
+
// 2. This publish request should be sent along to the SFU
|
|
497
|
+
const sfuPublishEvent = await managerEvents.waitFor('sfuPublishRequest');
|
|
498
|
+
expect(sfuPublishEvent.name).toStrictEqual('test');
|
|
499
|
+
expect(sfuPublishEvent.usesE2ee).toStrictEqual(false);
|
|
500
|
+
const handle = sfuPublishEvent.handle;
|
|
501
|
+
expect(handle).toStrictEqual(pubHandle);
|
|
502
|
+
|
|
503
|
+
// 3. Respond to the SFU publish request with an OK response
|
|
504
|
+
manager.receivedSfuPublishResponse(handle, {
|
|
505
|
+
type: 'ok',
|
|
506
|
+
data: {
|
|
507
|
+
sid: 'bogus-sid-REPUBLISHED',
|
|
508
|
+
pubHandle: sfuPublishEvent.handle,
|
|
509
|
+
name: 'test',
|
|
510
|
+
usesE2ee: false,
|
|
511
|
+
},
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
// After all this, the local data track should still be published
|
|
515
|
+
expect(localDataTrack.isPublished()).toStrictEqual(true);
|
|
516
|
+
|
|
517
|
+
// And the sid should be the new value
|
|
518
|
+
expect(localDataTrack.info!.sid).toStrictEqual('bogus-sid-REPUBLISHED');
|
|
519
|
+
|
|
520
|
+
// And now that the tracks are backed by the SFU again, pushes should function!
|
|
521
|
+
await localDataTrack.tryPush({ payload: new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05]) });
|
|
522
|
+
await managerEvents.waitFor('packetAvailable');
|
|
523
|
+
});
|
|
524
|
+
|
|
314
525
|
it('should query currently active descriptors', async () => {
|
|
315
526
|
// Create a manager prefilled with a descriptor
|
|
316
527
|
const manager = OutgoingDataTrackManager.withDescriptors(
|
|
@@ -378,7 +589,9 @@ describe('DataTrackOutgoingManager', () => {
|
|
|
378
589
|
const shutdownPromise = manager.shutdown();
|
|
379
590
|
|
|
380
591
|
// The pending data track should be cancelled
|
|
381
|
-
expect(pendingDescriptor.completionFuture.promise).rejects.toThrowError(
|
|
592
|
+
await expect(pendingDescriptor.completionFuture.promise).rejects.toThrowError(
|
|
593
|
+
'Room disconnected',
|
|
594
|
+
);
|
|
382
595
|
|
|
383
596
|
// And the active data track should be requested to be unpublished
|
|
384
597
|
const unpublishEvent = await managerEvents.waitFor('sfuUnpublishRequest');
|