@stream-io/video-client 1.61.0 → 1.61.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/CHANGELOG.md +6 -0
- package/dist/index.browser.es.js +14 -5
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +14 -5
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +14 -5
- package/dist/index.es.js.map +1 -1
- package/package.json +1 -1
- package/src/reporting/ClientEventReporter.ts +7 -2
- package/src/reporting/__tests__/ClientEventReporter.test.ts +2 -2
- package/src/rtc/BasePeerConnection.ts +6 -1
- package/src/rtc/__tests__/Publisher.test.ts +50 -0
package/package.json
CHANGED
|
@@ -597,11 +597,16 @@ export class ClientEventReporter {
|
|
|
597
597
|
}
|
|
598
598
|
|
|
599
599
|
if (event.state === 'failed') {
|
|
600
|
+
const iceConnected =
|
|
601
|
+
event.iceConnectionState === 'connected' ||
|
|
602
|
+
event.iceConnectionState === 'completed';
|
|
600
603
|
this.emitPeerConnectionFailure(
|
|
601
604
|
cid,
|
|
602
605
|
role,
|
|
603
|
-
'DTLS_CONNECTIVITY_FAILED',
|
|
604
|
-
|
|
606
|
+
iceConnected ? 'DTLS_CONNECTIVITY_FAILED' : 'ICE_CONNECTIVITY_FAILED',
|
|
607
|
+
iceConnected
|
|
608
|
+
? 'DTLS connectivity checks failed'
|
|
609
|
+
: 'ICE connectivity checks failed',
|
|
605
610
|
);
|
|
606
611
|
return;
|
|
607
612
|
}
|
|
@@ -755,7 +755,7 @@ describe('ClientEventReporter', () => {
|
|
|
755
755
|
});
|
|
756
756
|
});
|
|
757
757
|
|
|
758
|
-
it('reports
|
|
758
|
+
it('reports an ICE failure when the peer connection fails before ICE connected', async () => {
|
|
759
759
|
reporter.startCorrelation(cid, 'first-attempt');
|
|
760
760
|
reportPeerConnectionState(
|
|
761
761
|
PeerType.PUBLISHER_UNSPECIFIED,
|
|
@@ -779,7 +779,7 @@ describe('ClientEventReporter', () => {
|
|
|
779
779
|
expect(completed[0]).toMatchObject({
|
|
780
780
|
outcome: 'failure',
|
|
781
781
|
peer_connection: 'publish',
|
|
782
|
-
retry_failure_code: '
|
|
782
|
+
retry_failure_code: 'ICE_CONNECTIVITY_FAILED',
|
|
783
783
|
ice_state: 'NOT_CONNECTED',
|
|
784
784
|
});
|
|
785
785
|
});
|
|
@@ -344,9 +344,14 @@ export abstract class BasePeerConnection {
|
|
|
344
344
|
|
|
345
345
|
// we can't recover from a failed connection state (contrary to ICE)
|
|
346
346
|
if (state === 'failed') {
|
|
347
|
+
const reason = this.iceHasEverConnected
|
|
348
|
+
? ReconnectReason.CONNECTION_FAILED
|
|
349
|
+
: ReconnectReason.ICE_NEVER_CONNECTED;
|
|
350
|
+
clearTimeout(this.preConnectStuckTimeout);
|
|
351
|
+
this.preConnectStuckTimeout = undefined;
|
|
347
352
|
this.onReconnectionNeeded?.(
|
|
348
353
|
WebsocketReconnectStrategy.REJOIN,
|
|
349
|
-
|
|
354
|
+
reason,
|
|
350
355
|
this.peerType,
|
|
351
356
|
);
|
|
352
357
|
return;
|
|
@@ -931,6 +931,56 @@ describe('Publisher', () => {
|
|
|
931
931
|
);
|
|
932
932
|
});
|
|
933
933
|
|
|
934
|
+
it.each(['checking', 'failed'] as const)(
|
|
935
|
+
`connection-state 'failed' before ICE connects emits ICE_NEVER_CONNECTED when ICE is '%s'`,
|
|
936
|
+
async (iceState) => {
|
|
937
|
+
vi.spyOn(publisher, 'restartIce').mockResolvedValue();
|
|
938
|
+
publisher['onReconnectionNeeded'] = vi.fn();
|
|
939
|
+
// @ts-expect-error readonly field
|
|
940
|
+
publisher['pc'].iceConnectionState = iceState;
|
|
941
|
+
// @ts-expect-error readonly field
|
|
942
|
+
publisher['pc'].connectionState = 'failed';
|
|
943
|
+
|
|
944
|
+
// No ICE state-change event precedes the aggregate failure.
|
|
945
|
+
await publisher['onConnectionStateChange']();
|
|
946
|
+
|
|
947
|
+
expect(publisher.restartIce).not.toHaveBeenCalled();
|
|
948
|
+
expect(
|
|
949
|
+
publisher['onReconnectionNeeded'],
|
|
950
|
+
).toHaveBeenCalledExactlyOnceWith(
|
|
951
|
+
WebsocketReconnectStrategy.REJOIN,
|
|
952
|
+
ReconnectReason.ICE_NEVER_CONNECTED,
|
|
953
|
+
PeerType.PUBLISHER_UNSPECIFIED,
|
|
954
|
+
);
|
|
955
|
+
},
|
|
956
|
+
);
|
|
957
|
+
|
|
958
|
+
it(`connection-state 'failed' cancels the pre-connect watchdog`, async () => {
|
|
959
|
+
vi.useFakeTimers();
|
|
960
|
+
publisher['onReconnectionNeeded'] = vi.fn();
|
|
961
|
+
const watchdogMs = publisher['iceRestartDelay'] * 2;
|
|
962
|
+
|
|
963
|
+
// @ts-expect-error readonly field
|
|
964
|
+
publisher['pc'].iceConnectionState = 'disconnected';
|
|
965
|
+
publisher['onIceConnectionStateChange']();
|
|
966
|
+
expect(publisher['preConnectStuckTimeout']).toBeDefined();
|
|
967
|
+
expect(publisher['onReconnectionNeeded']).not.toHaveBeenCalled();
|
|
968
|
+
|
|
969
|
+
// Keep ICE disconnected so an uncanceled watchdog would reconnect again.
|
|
970
|
+
// @ts-expect-error readonly field
|
|
971
|
+
publisher['pc'].connectionState = 'failed';
|
|
972
|
+
await publisher['onConnectionStateChange']();
|
|
973
|
+
|
|
974
|
+
expect(publisher['preConnectStuckTimeout']).toBeUndefined();
|
|
975
|
+
expect(publisher['onReconnectionNeeded']).toHaveBeenCalledExactlyOnceWith(
|
|
976
|
+
WebsocketReconnectStrategy.REJOIN,
|
|
977
|
+
ReconnectReason.ICE_NEVER_CONNECTED,
|
|
978
|
+
PeerType.PUBLISHER_UNSPECIFIED,
|
|
979
|
+
);
|
|
980
|
+
await vi.advanceTimersByTimeAsync(watchdogMs + 100);
|
|
981
|
+
expect(publisher['onReconnectionNeeded']).toHaveBeenCalledTimes(1);
|
|
982
|
+
});
|
|
983
|
+
|
|
934
984
|
it(`connection-state 'failed' (distinct from ICE state) still fires REJOIN even after ICE was connected`, () => {
|
|
935
985
|
// mark ICE as connected first
|
|
936
986
|
// @ts-expect-error private api
|