-
Notifications
You must be signed in to change notification settings - Fork 17
Joao/fix reconnection after verto invite only #1227
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
82e9b58
be09de2
e195e97
d90b023
2fd2f12
783d4e8
571132f
8e7faa8
26a9a10
d6a9bed
2cea4c1
2982f35
f549670
02cb470
595fd47
67b77c0
5c60b10
26cb6f1
85c58cc
406877b
39f46b0
1e44780
9519fc2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
'@signalwire/webrtc': minor | ||
'@signalwire/core': minor | ||
'@signalwire/js': minor | ||
'@sw-internal/e2e-js': patch | ||
--- | ||
|
||
CHANGED improved the handling of WebSockets reconnections. |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -20,6 +20,10 @@ import { | |||||
VertoAnswer, | ||||||
UpdateMediaParams, | ||||||
UpdateMediaDirection, | ||||||
asyncRetry, | ||||||
constDelay, | ||||||
SYMBOL_EXECUTE_CONNECTION_CLOSED, | ||||||
SYMBOL_EXECUTE_TIMEOUT, | ||||||
} from '@signalwire/core' | ||||||
import type { ReduxComponent, VertoModifyResponse } from '@signalwire/core' | ||||||
import RTCPeer from './RTCPeer' | ||||||
|
@@ -96,6 +100,10 @@ export class BaseConnection< | |||||
return this.state === 'active' | ||||||
} | ||||||
|
||||||
get requesting() { | ||||||
return this.state === 'requesting' | ||||||
} | ||||||
|
||||||
get trying() { | ||||||
return this.state === 'trying' | ||||||
} | ||||||
|
@@ -277,6 +285,8 @@ export class BaseConnection< | |||||
} | ||||||
} | ||||||
|
||||||
private _myWorkers: Task[] = [] | ||||||
|
||||||
getRTCPeerById(rtcPeerId: string) { | ||||||
return this.rtcPeerMap.get(rtcPeerId) | ||||||
} | ||||||
|
@@ -285,6 +295,10 @@ export class BaseConnection< | |||||
return this.rtcPeerMap.set(rtcPeer.uuid, rtcPeer) | ||||||
} | ||||||
|
||||||
removeRTCPeer(rtcPeerId: string) { | ||||||
return this.rtcPeerMap.delete(rtcPeerId) | ||||||
} | ||||||
|
||||||
setActiveRTCPeer(rtcPeerId: string) { | ||||||
this.peer = this.getRTCPeerById(rtcPeerId) | ||||||
} | ||||||
|
@@ -741,38 +755,74 @@ export class BaseConnection< | |||||
} | ||||||
|
||||||
runRTCPeerWorkers(rtcPeerId: string) { | ||||||
this.runWorker('vertoEventWorker', { | ||||||
const vertoWorker = this.runWorker('vertoEventWorker', { | ||||||
worker: workers.vertoEventWorker, | ||||||
initialState: { rtcPeerId }, | ||||||
}) | ||||||
this._myWorkers.push(vertoWorker) | ||||||
|
||||||
const main = !(this.options.additionalDevice || this.options.screenShare) | ||||||
|
||||||
if (main) { | ||||||
this.runWorker('roomSubscribedWorker', { | ||||||
const subscribedWorker = this.runWorker('roomSubscribedWorker', { | ||||||
worker: workers.roomSubscribedWorker, | ||||||
initialState: { rtcPeerId }, | ||||||
}) | ||||||
this._myWorkers.push(subscribedWorker) | ||||||
|
||||||
this.runWorker('promoteDemoteWorker', { | ||||||
const promoteDemoteWorker = this.runWorker('promoteDemoteWorker', { | ||||||
worker: workers.promoteDemoteWorker, | ||||||
initialState: { rtcPeerId }, | ||||||
}) | ||||||
this._myWorkers.push(promoteDemoteWorker) | ||||||
} | ||||||
} | ||||||
|
||||||
removeRTCWorkers() { | ||||||
for (const task of this._myWorkers) { | ||||||
this.cancelWorker(task) | ||||||
} | ||||||
this._myWorkers = [] | ||||||
} | ||||||
|
||||||
private _destroyPeer() { | ||||||
if (this.peer) { | ||||||
//clean up previous attempt | ||||||
this.peer.detachAndStop() | ||||||
this.removeRTCWorkers() | ||||||
this.removeRTCPeer(this.peer.uuid) | ||||||
this.peer = undefined | ||||||
} | ||||||
} | ||||||
|
||||||
/** @internal */ | ||||||
invite<T>(): Promise<T> { | ||||||
return new Promise(async (resolve, reject) => { | ||||||
this.direction = 'outbound' | ||||||
this.peer = this._buildPeer('offer') | ||||||
try { | ||||||
await this.peer.start() | ||||||
resolve(this as any as T) | ||||||
} catch (error) { | ||||||
this.logger.error('Invite error', error) | ||||||
reject(error) | ||||||
} | ||||||
return asyncRetry({ | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think the retry logic should be touching the internal methods/or API calls. This will make the SDK inconsistent, and it also does not care about developer-passed params, which means the developer can not control the retry specifically for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, I am confused. The global retry will also retry this API? Because this will in the end call the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The global retry doesn't retry In a nutshell, the SDK can't replay a We may find other RPC with the same characteristics, but this PR only focuses on the |
||||||
asyncCallable: async () => { | ||||||
return new Promise(async (resolve, reject) => { | ||||||
await this._waitUntilSessionAuthorized() | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How do you think the user can call this API if the session is not authorized? This is already a part of the connect logic. If that does not happen, the user can't call this API. If that happens, then do we need to wait here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not the user calling it, but he next scheduled retry, which is normally faster than the authentication. |
||||||
this.direction = 'outbound' | ||||||
this.peer = this._buildPeer('offer') | ||||||
try { | ||||||
await this.peer.start() | ||||||
resolve(this as any as T) | ||||||
} catch (error) { | ||||||
this.logger.error('Invite error', error) | ||||||
this._destroyPeer() | ||||||
reject(error) | ||||||
} | ||||||
}) | ||||||
}, | ||||||
maxRetries: 5, | ||||||
delayFn: constDelay({ initialDelay: 0 }), | ||||||
expectedErrorHandler: (error) => { | ||||||
if (this.requesting && [SYMBOL_EXECUTE_CONNECTION_CLOSED, SYMBOL_EXECUTE_TIMEOUT].includes(error)) { // eslint-disable-line max-len, no-nested-ternaryerror === SYMBOL_EXECUTE_CONNECTION_CLOSED) { | ||||||
this.logger.debug('Retrying verto.invite with new RTCPeer') | ||||||
return false // we should retry | ||||||
} | ||||||
// other case are expected to be handle upstream | ||||||
return true | ||||||
}, | ||||||
}) | ||||||
} | ||||||
|
||||||
|
@@ -794,7 +844,7 @@ export class BaseConnection< | |||||
} | ||||||
|
||||||
/** @internal */ | ||||||
onLocalSDPReady(rtcPeer: RTCPeer<EventTypes>) { | ||||||
async onLocalSDPReady(rtcPeer: RTCPeer<EventTypes>) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not an async method
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
if (!rtcPeer.instance.localDescription) { | ||||||
this.logger.error('Missing localDescription', rtcPeer) | ||||||
throw new Error('Invalid RTCPeerConnection localDescription') | ||||||
|
@@ -899,11 +949,15 @@ export class BaseConnection< | |||||
node_id: nodeId ?? this.options.nodeId, | ||||||
subscribe, | ||||||
}) | ||||||
if (this.state === 'requesting') { | ||||||
// The Server Created the call, and now is trying to connect us to the destination | ||||||
this.setState('trying') | ||||||
} | ||||||
Comment on lines
+952
to
+955
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the point? Where are you using this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The SDK should be in the This makes more sense in 1:1 calls, supported by the CF SDK. I believe that's why it was missing in the Video SDK. |
||||||
this.logger.debug('Invite response', response) | ||||||
|
||||||
this.resuming = false | ||||||
} catch (error) { | ||||||
this.setState('hangup') | ||||||
this.setState('hangup') | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. run prettier please |
||||||
throw error | ||||||
} | ||||||
} | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please run the prettier