@babblevoice/babble-drachtio-callmanager 3.8.1 → 3.9.0
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/lib/call.js +41 -1
- package/package.json +1 -1
- package/test/interface/call.js +89 -0
package/lib/call.js
CHANGED
|
@@ -293,6 +293,12 @@ class call {
|
|
|
293
293
|
*/
|
|
294
294
|
this.parent = undefined
|
|
295
295
|
|
|
296
|
+
/**
|
|
297
|
+
* @summary Lifecycle callbacks supplied to newuac for this (outbound) call.
|
|
298
|
+
* @type { newuaccallbacks }
|
|
299
|
+
*/
|
|
300
|
+
this.callbacks = {}
|
|
301
|
+
|
|
296
302
|
/** @summary Other channels which we might need - for things like opening a channel on the same node. */
|
|
297
303
|
this.relatives = new Set()
|
|
298
304
|
|
|
@@ -1472,6 +1478,15 @@ class call {
|
|
|
1472
1478
|
this.epochs.mix = Math.floor( +new Date() / 1000 )
|
|
1473
1479
|
if( this.parent ) this.parent.epochs.mix = Math.floor( +new Date() / 1000 )
|
|
1474
1480
|
|
|
1481
|
+
/* Now we are bridged/mixed with our parent, notify any onconnect handler. */
|
|
1482
|
+
if( this.callbacks.onconnect ) {
|
|
1483
|
+
try {
|
|
1484
|
+
await this.callbacks.onconnect( this )
|
|
1485
|
+
} catch( e ) {
|
|
1486
|
+
console.trace( e )
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1475
1490
|
return this
|
|
1476
1491
|
}
|
|
1477
1492
|
|
|
@@ -3808,6 +3823,8 @@ class call {
|
|
|
3808
3823
|
return this
|
|
3809
3824
|
}
|
|
3810
3825
|
|
|
3826
|
+
this.callbacks = callbacks
|
|
3827
|
+
|
|
3811
3828
|
if( this.parent ) {
|
|
3812
3829
|
const hangups = []
|
|
3813
3830
|
for( const child of this.parent.children ) {
|
|
@@ -4062,11 +4079,18 @@ class call {
|
|
|
4062
4079
|
* @param { call } call - our call object which is early
|
|
4063
4080
|
*/
|
|
4064
4081
|
|
|
4082
|
+
/**
|
|
4083
|
+
* @callback onconnectcallback
|
|
4084
|
+
* @async
|
|
4085
|
+
* @param { call } call - the (outbound) call which has just connected and been bridged with its parent
|
|
4086
|
+
*/
|
|
4087
|
+
|
|
4065
4088
|
/**
|
|
4066
4089
|
* @typedef { object } newuaccallbacks
|
|
4067
4090
|
* @property { earlycallback } [ early ] - callback to provide a call object with early call (pre dialog)
|
|
4068
4091
|
* @property { confirmcallback } [ confirm ] - called when a dialog is confirmed but before it is bridged with a parent - this provides an opportunity for another call to adopt this call
|
|
4069
4092
|
* @property { failcallback } [ fail ] - Called when child is terminated
|
|
4093
|
+
* @property { onconnectcallback } [ onconnect ] - called after this call has connected and been bridged/mixed with its parent
|
|
4070
4094
|
*/
|
|
4071
4095
|
|
|
4072
4096
|
/**
|
|
@@ -4122,10 +4146,12 @@ class call {
|
|
|
4122
4146
|
newcall.#confignetwork( options )
|
|
4123
4147
|
|
|
4124
4148
|
let newdialog, earlypromise
|
|
4149
|
+
let settlewatchdog
|
|
4125
4150
|
try {
|
|
4126
4151
|
await newcall.#openchannelsfornewuac()
|
|
4127
4152
|
|
|
4128
|
-
|
|
4153
|
+
const settlems = newcall.options.uactimeout || callmanager.options.uactimeout
|
|
4154
|
+
const createuacpromise = callmanager.options.srf.createUAC(
|
|
4129
4155
|
options.contact + newcall.options.contactparams,
|
|
4130
4156
|
{
|
|
4131
4157
|
...newcall.options,
|
|
@@ -4182,6 +4208,18 @@ class call {
|
|
|
4182
4208
|
}
|
|
4183
4209
|
}
|
|
4184
4210
|
} )
|
|
4211
|
+
|
|
4212
|
+
const settlewatchdogpromise = new Promise( ( resolve, reject ) => {
|
|
4213
|
+
settlewatchdog = setTimeout( async () => {
|
|
4214
|
+
try {
|
|
4215
|
+
await newcall.hangup( hangupcodes.REQUEST_TIMEOUT )
|
|
4216
|
+
} catch ( e ) { /* silent */ }
|
|
4217
|
+
|
|
4218
|
+
reject( Object.assign( new Error( "newuac settle timeout" ), { status: 408 } ) )
|
|
4219
|
+
}, settlems )
|
|
4220
|
+
} )
|
|
4221
|
+
|
|
4222
|
+
newdialog = await Promise.race( [ createuacpromise, settlewatchdogpromise ] )
|
|
4185
4223
|
} catch ( err ) {
|
|
4186
4224
|
newcall.#onnewuaccatch( err )
|
|
4187
4225
|
try{
|
|
@@ -4204,6 +4242,8 @@ class call {
|
|
|
4204
4242
|
}
|
|
4205
4243
|
}
|
|
4206
4244
|
} catch( e ) { console.error( e ) }
|
|
4245
|
+
} finally {
|
|
4246
|
+
if( settlewatchdog ) clearTimeout( settlewatchdog )
|
|
4207
4247
|
}
|
|
4208
4248
|
|
|
4209
4249
|
if( earlypromise ) await earlypromise
|
package/package.json
CHANGED
package/test/interface/call.js
CHANGED
|
@@ -177,6 +177,54 @@ describe( "call object", function() {
|
|
|
177
177
|
|
|
178
178
|
} )
|
|
179
179
|
|
|
180
|
+
it( "uas.newuac - onconnect callback is called after bridge with valid audio", async function() {
|
|
181
|
+
|
|
182
|
+
const srfscenario = new srf.srfscenario()
|
|
183
|
+
|
|
184
|
+
const call = await new Promise( ( resolve ) => {
|
|
185
|
+
srfscenario.oncall( async ( call ) => { resolve( call ) } )
|
|
186
|
+
srfscenario.inbound()
|
|
187
|
+
} )
|
|
188
|
+
|
|
189
|
+
let onconnectcount = 0
|
|
190
|
+
let connectedcall
|
|
191
|
+
let audioatconnect
|
|
192
|
+
let mixepochatconnect
|
|
193
|
+
|
|
194
|
+
const child = await call.newuac( { "contact": "1000@dummy" }, {
|
|
195
|
+
"onconnect": ( c ) => {
|
|
196
|
+
onconnectcount++
|
|
197
|
+
connectedcall = c
|
|
198
|
+
audioatconnect = c.channels.audio
|
|
199
|
+
mixepochatconnect = c.epochs.mix
|
|
200
|
+
}
|
|
201
|
+
} )
|
|
202
|
+
|
|
203
|
+
/* the handler ran exactly once, during newuac */
|
|
204
|
+
expect( onconnectcount ).to.equal( 1 )
|
|
205
|
+
|
|
206
|
+
/* it received the connected (child) call */
|
|
207
|
+
expect( connectedcall ).to.equal( child )
|
|
208
|
+
|
|
209
|
+
/* the audio channel was a valid object inside the handler - it must not
|
|
210
|
+
go undefined/false (the regression this callback originally exposed) */
|
|
211
|
+
expect( audioatconnect ).to.be.an( "object" )
|
|
212
|
+
expect( child.channels.audio ).to.equal( audioatconnect )
|
|
213
|
+
|
|
214
|
+
/* it fired AFTER the mix, i.e. once genuinely connected/bridged */
|
|
215
|
+
expect( mixepochatconnect ).to.be.a( "number" ).that.is.above( 0 )
|
|
216
|
+
|
|
217
|
+
await child.hangup()
|
|
218
|
+
await call.hangup()
|
|
219
|
+
|
|
220
|
+
expect( await callstore.stats() ).to.deep.include( {
|
|
221
|
+
"storebycallid": 0,
|
|
222
|
+
"storebyuuid": 0,
|
|
223
|
+
"storebyentity": 0
|
|
224
|
+
} )
|
|
225
|
+
|
|
226
|
+
} )
|
|
227
|
+
|
|
180
228
|
it( "uas.newuac - create uac by entity no registrar", async function() {
|
|
181
229
|
const srfscenario = new srf.srfscenario()
|
|
182
230
|
|
|
@@ -469,6 +517,47 @@ describe( "call object", function() {
|
|
|
469
517
|
expect( child.state.cleaned ).to.be.true
|
|
470
518
|
} )
|
|
471
519
|
|
|
520
|
+
it( "uas.newuac - createUAC never settles - watchdog forces return", async function() {
|
|
521
|
+
|
|
522
|
+
const srfscenario = new srf.srfscenario()
|
|
523
|
+
/* simulate a far end that never sends a final response and never honours
|
|
524
|
+
CANCEL: createUAC never resolves or rejects. Without the settle watchdog
|
|
525
|
+
newuac would hang here forever (and this test would hit the mocha timeout). */
|
|
526
|
+
srfscenario.oncreateUAC( () => new Promise( () => {} ) )
|
|
527
|
+
|
|
528
|
+
const call = await new Promise( ( resolve ) => {
|
|
529
|
+
srfscenario.oncall( async ( call ) => { resolve( call ) } )
|
|
530
|
+
srfscenario.inbound()
|
|
531
|
+
} )
|
|
532
|
+
|
|
533
|
+
const start = Date.now()
|
|
534
|
+
const child = await call.newuac( { "contact": "1000@dummy", "uactimeout": 50 } )
|
|
535
|
+
const elapsed = Date.now() - start
|
|
536
|
+
|
|
537
|
+
/* the watchdog must make newuac return promptly (~uactimeout) rather than
|
|
538
|
+
block on the never-settling createUAC */
|
|
539
|
+
expect( elapsed ).to.be.below( 1000 )
|
|
540
|
+
|
|
541
|
+
expect( child.destroyed ).to.be.true
|
|
542
|
+
expect( child.hangup_cause.sip ).to.equal( 408 )
|
|
543
|
+
expect( child.hangup_cause.reason ).to.equal( "REQUEST_TIMEOUT" )
|
|
544
|
+
expect( child.hangup_cause.src ).to.equal( "us" )
|
|
545
|
+
|
|
546
|
+
/* clean up runs on the event loop */
|
|
547
|
+
await new Promise( resolve => setTimeout( resolve, 100 ) )
|
|
548
|
+
|
|
549
|
+
await call.hangup()
|
|
550
|
+
|
|
551
|
+
expect( await callstore.stats() ).to.deep.include( {
|
|
552
|
+
"storebycallid": 0,
|
|
553
|
+
"storebyuuid": 0,
|
|
554
|
+
"storebyentity": 0
|
|
555
|
+
} )
|
|
556
|
+
|
|
557
|
+
expect( call.state.cleaned ).to.be.true
|
|
558
|
+
expect( child.state.cleaned ).to.be.true
|
|
559
|
+
} )
|
|
560
|
+
|
|
472
561
|
it( "uas.newuac - child remote hangup", async function() {
|
|
473
562
|
|
|
474
563
|
const srfscenario = new srf.srfscenario()
|