@babblevoice/babble-drachtio-callmanager 1.6.13 → 1.6.14

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 CHANGED
@@ -339,7 +339,7 @@ class call {
339
339
  }
340
340
 
341
341
  /**
342
- * Set the entity information aainst this call. Itis either set by authentication
342
+ * Set the entity information against this call. Itis either set by authentication
343
343
  * or externally (for example if we auth by network location).
344
344
  * Either ( e.uri ) or ( e.realm and e.username ) are required.
345
345
  * @param { entity } e
@@ -460,6 +460,7 @@ class call {
460
460
 
461
461
  if( !parsed ) parsed = {}
462
462
  let parseduri = parseuri( parsed.uri )
463
+ if( !parseduri ) parseduri = { "user": parsed.user, "host": parsed.host }
463
464
  if( !parsed.params ) parsed.params = {}
464
465
 
465
466
  return {
@@ -880,49 +881,6 @@ class call {
880
881
  } )
881
882
  }
882
883
 
883
- /**
884
- Called from newuac when we are answered and we have a dialog,
885
- this = child call (the new call - the bleg)
886
- @private
887
- */
888
- async _onanswer() {
889
-
890
- let hangups = []
891
- if( this.parent ) {
892
- for( let child of this.parent.children ) {
893
- if( child.uuid !== this.uuid ) {
894
- child.detach()
895
- /* do not await - we do not want to delay the winner in
896
- connecting by waiting for the completion of the hangups */
897
- hangups.push( child.hangup( hangupcodes.LOSE_RACE ) )
898
- }
899
- }
900
- }
901
-
902
- if( this.state.destroyed ) return this
903
- callstore.set( this )
904
-
905
- if( true === this.options.noAck ) {
906
- await this._onlatebridge()
907
- } else {
908
- await this._onearlybridge()
909
- }
910
-
911
- this.established = true
912
- this.sip.tags.remote = this._dialog.sip.remoteTag
913
-
914
- let r = this._promises.resolve.newuac
915
- this._promises.resolve.newuac = false
916
- this._promises.reject.newuac = false
917
-
918
- if( hangups.length > 0 ) {
919
- await Promise.all( hangups )
920
- }
921
-
922
- if( r ) r( this )
923
- return this
924
- }
925
-
926
884
  /* A simple implimentation if we are offered candidates */
927
885
  static async _parsesdpcandidates( target, sdp ) {
928
886
 
@@ -994,6 +952,14 @@ class call {
994
952
  }
995
953
  }
996
954
 
955
+ /* are we still established? */
956
+ if( !this.established || this.state.destroyed ) return this
957
+ if( !this.channels.audio ) {
958
+ /* something bad has happened */
959
+ this.hangup( hangupcodes.NOT_ACCEPTABLE )
960
+ return this
961
+ }
962
+
997
963
  this.channels.audio.mix( this.parent.channels.audio )
998
964
 
999
965
  this._em.emit( "call.mix", this )
@@ -1105,7 +1071,7 @@ class call {
1105
1071
  }
1106
1072
  }
1107
1073
 
1108
- if( this.children.length > 0 ) return this.children[ 0 ]
1074
+ if( this.children.length > 0 ) return this.children.values().next().value
1109
1075
 
1110
1076
  return false
1111
1077
  }
@@ -2260,18 +2226,21 @@ class call {
2260
2226
 
2261
2227
  this._sethangupcause( "us", reason )
2262
2228
 
2263
- if( this.established ) {
2229
+ if( this.state.established ) {
2264
2230
  try {
2265
2231
  await this._dialog.destroy()
2266
2232
  } catch( e ) { console.trace( e ) }
2267
2233
 
2268
- } else if( "uac" === this.type ) {
2234
+ } else if( "uac" === this.type &&
2235
+ this.state.trying ) {
2269
2236
  try {
2270
- if( this._req ) this._req.cancel()
2237
+ this.canceled = true
2238
+ if( this._req ) {
2239
+ this._req.cancel( ( err, cancel ) => {
2240
+ if( err ) console.trace( err )
2241
+ } )
2242
+ }
2271
2243
  } catch( e ) { console.trace( e ) }
2272
-
2273
- this.canceled = true
2274
-
2275
2244
  } else {
2276
2245
  try {
2277
2246
  this._res.send( this.hangup_cause.sip )
@@ -2431,7 +2400,7 @@ class call {
2431
2400
  @param { earlycallback } [ callbacks.early ] - callback to provide a call object with early call (pre dialog)
2432
2401
  @param { confirmcallback } [ callbacks.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
2433
2402
  @param { failcallback } [ callbacks.fail ] - Called when child is terminated
2434
- @return { Promise< call | false > } - returns a promise which resolves to a new call object if a dialog has been confirmed. If none are confirmed ten return false. Each attempt is fed into callbacks.early.
2403
+ @return { Promise< call | false > } - returns a promise which resolves to a new call object if a dialog has been confirmed. If none are confirmed then return false. Each attempt is fed into callbacks.early.
2435
2404
  */
2436
2405
  static async newuac( options, callbacks = {} ) {
2437
2406
 
@@ -2461,7 +2430,7 @@ class call {
2461
2430
  return false
2462
2431
  }
2463
2432
 
2464
- let othercalls = []
2433
+ const numcontacts = contactinfo.contacts.length
2465
2434
  let ourcallbacks = {}
2466
2435
  let failcount = 0
2467
2436
 
@@ -2471,22 +2440,30 @@ class call {
2471
2440
  } )
2472
2441
 
2473
2442
  ourcallbacks.early = ( c ) => {
2474
- othercalls.push( c )
2475
2443
  if( callbacks.early ) callbacks.early( c )
2476
2444
  }
2477
2445
 
2478
2446
  ourcallbacks.fail = ( c ) => {
2479
2447
  failcount++
2480
- if( failcount >= othercalls.length ) {
2448
+ if( failcount >= numcontacts ) {
2481
2449
  /* we have no more to try */
2482
- waitonchildrenresolve( c )
2450
+ if( false !== waitonchildrenresolve ) {
2451
+ waitonchildrenresolve( false )
2452
+ waitonchildrenresolve = false
2453
+ }
2483
2454
  }
2484
2455
  if( callbacks.fail ) callbacks.fail( c )
2485
2456
  }
2486
2457
 
2487
2458
  ourcallbacks.confirm = ( c ) => {
2488
- waitonchildrenresolve( c )
2489
- if( callbacks.confirm ) callbacks.confirm( c )
2459
+ if( false !== waitonchildrenresolve ) {
2460
+ waitonchildrenresolve( c )
2461
+ waitonchildrenresolve = false
2462
+ if( callbacks.confirm ) callbacks.confirm( c )
2463
+ return
2464
+ }
2465
+ c.hangup( hangupcodes.LOSE_RACE )
2466
+ if( callbacks.fail ) callbacks.fail( c )
2490
2467
  }
2491
2468
 
2492
2469
  for( let contact of contactinfo.contacts ) {
@@ -2499,17 +2476,6 @@ class call {
2499
2476
  }
2500
2477
 
2501
2478
  let child = await waitonchildrenpromise
2502
-
2503
- if( child && !child.parent ) {
2504
- /* we have to terminate other calls we generated as this
2505
- will not happen in the call object without a parent */
2506
- for( let other of othercalls ) {
2507
- if( other.uuid !== child.uuid ) {
2508
- other.detach()
2509
- other.hangup( hangupcodes.LOSE_RACE )
2510
- }
2511
- }
2512
- }
2513
2479
  return child
2514
2480
  }
2515
2481
 
@@ -2579,10 +2545,6 @@ class call {
2579
2545
  newcall.options = { ...callmanager.options, ...newcall.options, ...options }
2580
2546
  newcall.options.headers = tmpheaders
2581
2547
 
2582
- newcall._timers.newuac = setTimeout( () => {
2583
- newcall.hangup( hangupcodes.REQUEST_TIMEOUT )
2584
- }, newcall.options.uactimeout )
2585
-
2586
2548
  if( newcall.options.late ) {
2587
2549
  newcall.options.noAck = true /* this is a MUST for late negotiation */
2588
2550
  } else {
@@ -2612,67 +2574,101 @@ class call {
2612
2574
  if( addressparts.port ) newcall.network.remote.port = addressparts.port
2613
2575
  }
2614
2576
 
2615
- newcall._dialog = await callmanager.options.srf.createUAC( options.contact, newcall.options, {
2616
- cbRequest: ( err, req ) => {
2577
+ try {
2578
+ newcall._dialog = await callmanager.options.srf.createUAC( options.contact, newcall.options, {
2579
+ cbRequest: ( err, req ) => {
2617
2580
 
2618
- if( !req ) {
2619
- newcall.state.destroyed = true
2620
- console.trace( "No req object??", err )
2621
- return
2622
- }
2581
+ if( !req ) {
2582
+ newcall.state.destroyed = true
2583
+ console.trace( "No req object??", err )
2584
+ return
2585
+ }
2623
2586
 
2624
- newcall._req = req
2625
- newcall.state.trying = true
2587
+ newcall._timers.newuac = setTimeout( () => {
2588
+ newcall.hangup( hangupcodes.REQUEST_TIMEOUT )
2589
+ }, newcall.options.uactimeout )
2626
2590
 
2627
- newcall.sip = {
2628
- "callid": req.getParsedHeader( "call-id" ),
2629
- "tags": {
2630
- "local": req.getParsedHeader( "from" ).params.tag,
2631
- "remote": ""
2632
- },
2633
- "contact": [ { "uri": options.contact } ]
2634
- }
2591
+ newcall._req = req
2592
+ newcall.state.trying = true
2635
2593
 
2636
- callstore.set( newcall )
2637
- if( callbacks && callbacks.early ) callbacks.early( newcall )
2638
- callmanager.options.em.emit( "call.new", newcall )
2639
- },
2640
- cbProvisional: async ( res ) => {
2641
- newcall._res = res
2642
- if( 180 === res.status ) {
2643
- newcall._onring()
2644
- } else if( 183 === res.status ) {
2645
- await newcall._onearly()
2594
+ newcall.sip = {
2595
+ "callid": req.getParsedHeader( "call-id" ),
2596
+ "tags": {
2597
+ "local": req.getParsedHeader( "from" ).params.tag,
2598
+ "remote": ""
2599
+ },
2600
+ "contact": [ { "uri": options.contact } ]
2601
+ }
2602
+
2603
+ callstore.set( newcall )
2604
+ if( callbacks && callbacks.early ) callbacks.early( newcall )
2605
+ callmanager.options.em.emit( "call.new", newcall )
2606
+ },
2607
+ cbProvisional: async ( res ) => {
2608
+ newcall._res = res
2609
+ if( 180 === res.status ) {
2610
+ newcall._onring()
2611
+ } else if( 183 === res.status ) {
2612
+ await newcall._onearly()
2613
+ }
2614
+
2615
+ if( newcall.canceled ) {
2616
+ newcall.hangup()
2617
+ }
2646
2618
  }
2619
+ } )
2620
+ } catch ( err ) {
2621
+
2622
+ if( !newcall._state._hangup ) {
2623
+ if ( undefined !== err.status ) {
2624
+ let reason = hangupcodes.REQUEST_TERMINATED
2625
+ if( err.status in inboundsiperros ) reason = inboundsiperros[ err.status ]
2647
2626
 
2648
- if( newcall.canceled ) {
2649
- newcall.hangup()
2627
+ newcall.state.destroyed = true
2628
+ if( newcall ) newcall._onhangup( "wire", reason )
2629
+ } else {
2630
+ console.trace( err )
2650
2631
  }
2651
2632
  }
2652
- } ).catch( ( err ) => {
2653
- if ( undefined !== err.status ) {
2654
- let reason = hangupcodes.REQUEST_TERMINATED
2655
- if( err.status in inboundsiperros ) reason = inboundsiperros[ err.status ]
2633
+ } finally {
2634
+
2635
+ if( newcall._timers.newuac ) clearTimeout( newcall._timers.newuac )
2636
+ newcall._timers.newuac = false
2656
2637
 
2657
- if( newcall ) newcall._onhangup( "wire", reason )
2658
- } else {
2659
- console.trace( err )
2638
+ if( newcall.state.destroyed || newcall.state.canceled || newcall._state._hangup ) {
2639
+ if( callbacks && callbacks.fail ) callbacks.fail( newcall )
2640
+ return newcall
2660
2641
  }
2661
- } )
2662
2642
 
2663
- if( newcall._timers.newuac ) clearTimeout( newcall._timers.newuac )
2664
- newcall._timers.newuac = false
2643
+ newcall.sdp.remote = sdpgen.create( newcall._dialog.remote.sdp )
2665
2644
 
2666
- if( newcall.state.destroyed ) {
2645
+ if( newcall.state.destroyed ) return this
2646
+ newcall.established = true
2667
2647
 
2668
- if( callbacks && callbacks.fail ) callbacks.fail( newcall )
2669
- return newcall
2670
- }
2648
+ if( newcall.parent ) {
2649
+ for( const child of newcall.parent.children ) {
2650
+ if( child.uuid !== newcall.uuid && !child.state.established ) {
2651
+ child.detach()
2652
+ /* do not await - we do not want to delay the winner in
2653
+ connecting by waiting for the completion of the hangups */
2654
+ child.hangup( hangupcodes.LOSE_RACE )
2655
+ }
2656
+ }
2657
+ }
2671
2658
 
2672
- newcall.sdp.remote = sdpgen.create( newcall._dialog.remote.sdp )
2659
+ callstore.set( newcall )
2673
2660
 
2674
- if( callbacks.confirm ) await callbacks.confirm( newcall )
2675
- return await newcall._onanswer()
2661
+ newcall.sip.tags.remote = newcall._dialog.sip.remoteTag
2662
+
2663
+ if( true === newcall.options.noAck ) {
2664
+ await newcall._onlatebridge()
2665
+ } else {
2666
+ await newcall._onearlybridge()
2667
+ }
2668
+
2669
+ if( callbacks.confirm ) await callbacks.confirm( newcall )
2670
+ return newcall
2671
+ }
2676
2672
  }
2677
2673
 
2678
2674
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babblevoice/babble-drachtio-callmanager",
3
- "version": "1.6.13",
3
+ "version": "1.6.14",
4
4
  "description": "Call processing to create a PBX",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -205,7 +205,7 @@ describe( "call object", function() {
205
205
  expect( call.state.cleaned ).to.be.true
206
206
  } )
207
207
 
208
- it( `uas.newuac - create uac by entity with registrar #123`, async function() {
208
+ it( `uas.newuac - create uac by entity with registrar`, async function() {
209
209
 
210
210
  let options = {
211
211
  "registrar": {
@@ -228,6 +228,15 @@ describe( "call object", function() {
228
228
  srfscenario.inbound()
229
229
  } )
230
230
 
231
+ call._req.setparsedheader( "remote-party-id", {
232
+ "name": "",
233
+ "uri": "",
234
+ "user": "0123456789",
235
+ "host": "someotherrealm.com",
236
+ "params": { "privacy": false },
237
+ "type": "callerid"
238
+ } )
239
+
231
240
  let child = await call.newuac( { "entity": { "uri": "1000@dummy" } } )
232
241
 
233
242
  expect( await callstore.stats() ).to.deep.include( {
@@ -248,7 +257,7 @@ describe( "call object", function() {
248
257
  child.update()
249
258
 
250
259
  expect( requestoptions.method ).to.equal( "update" )
251
- expect( requestoptions.headers[ "P-Preferred-Identity" ] ).to.equal( `"" <sip:1000@dummy.com>` )
260
+ expect( requestoptions.headers[ "P-Preferred-Identity" ] ).to.equal( `"" <sip:0123456789@someotherrealm.com>` )
252
261
 
253
262
  await call.hangup()
254
263
 
@@ -311,15 +311,20 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
311
311
 
312
312
  it( `Create call and send 183 - early - SAVPF and 200 ok`, async () => {
313
313
 
314
- /*
315
- Phone (SAVPF) BV Gateway
316
- |---------INVITE------>| |(1)
317
- | |---------INVITE------>|(2)
318
- | |<--------183 (w-sdp)--|(3)
319
- |<--------183 (w-sdp)--| |(4)
320
- | |<--------200 (w-sdp)--|(5)
321
- |<--------200 (w-sdp)--| |(6)
322
- */
314
+ /**
315
+ * Markdown - mermaid
316
+ sequenceDiagram
317
+ participant Phone
318
+ participant babble
319
+ participant Gateway
320
+
321
+ Phone->>babble: INVITE (1)
322
+ babble->>Gateway: INVITE (2)
323
+ Gateway->>babble: 183 w-sdp (3)
324
+ babble->>Phone: 183 w-sdp (4)
325
+ Gateway->>babble: Ok w-sdp (200)(5)
326
+ babble->>Phone: Ok w-sdp (200) (6)
327
+ */
323
328
 
324
329
  /* Setup the mock RTP server */
325
330
  let srfscenario = new srf.srfscenario( { savpf: true } )
@@ -447,9 +452,10 @@ a=sendrecv`.replace(/(\r\n|\n|\r)/gm, "\r\n")
447
452
  expect( channelmessages[ 2 ].remote.port ).to.equal( 48356 )
448
453
  expect( channelmessages[ 2 ].remote.address ).to.equal( "82.19.206.102" )
449
454
  expect( channelmessages[ 3 ].channel ).to.equal( "mix" )
450
-
451
- expect( channelmessages[ 6 ].channel ).to.equal( "close" )
455
+ /* with 183 we now get a second duplicate mix - this could be tidied but it is safe */
456
+ expect( channelmessages[ 6 ].channel ).to.equal( "mix" )
452
457
  expect( channelmessages[ 7 ].channel ).to.equal( "close" )
458
+ expect( channelmessages[ 8 ].channel ).to.equal( "close" )
453
459
 
454
460
  expect( msginfo.body ).to.include( "UDP/TLS/RTP/SAVPF" )
455
461