@babblevoice/babble-drachtio-callmanager 3.7.1 → 3.7.3

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.
Files changed (3) hide show
  1. package/index.js +1 -1
  2. package/lib/call.js +114 -54
  3. package/package.json +1 -1
package/index.js CHANGED
@@ -8,7 +8,7 @@ const default_options = {
8
8
  "preferedcodecs": "g722 ilbc pcmu pcma",
9
9
  //"transcode": true, - this never made it into the software - TODO
10
10
  "uactimeout": 30000, /* timeout when calling a client */
11
- "seexpire": 120000, /* session expires timeout mS */
11
+ "seexpire": 1800*1000, /* session expires timeout mS recomended in RFC 4028 - 30 minutes */
12
12
  "rfc2833": true, /* Enable RFC 2833 - DTMF */
13
13
  "late": false, /* Late negotiation */
14
14
  "registrar": false, /* our registrar object or falsey */
package/lib/call.js CHANGED
@@ -14,6 +14,7 @@ const net = require( "net" )
14
14
  const parseurire = /^(sips?):(?:([^\s>:@]+)(?::([^\s@>]+))?@)?([\w\-.]+)(?::(\d+))?((?:;[^\s=?>;]+(?:=[^\s?;]+)?)*)(?:\?(([^\s&=>]+=[^\s&=>]+)(&[^\s&=>]+=[^\s&=>]+)*))?$/
15
15
  const parseuriparamsre = /([^;=]+)(=([^;=]+))?/g
16
16
  const parseuriheadersre = /[^&=]+=[^&=]+/g
17
+ const sessionexpirevaluere = /^(\d+)(?:;refresher=(uac|uas))?$/
17
18
 
18
19
  function parseuri( s ) {
19
20
  if( "object" === typeof s )
@@ -153,6 +154,9 @@ let callmanager = {
153
154
  class call {
154
155
 
155
156
  #noack = false
157
+ #seexpire = callmanager.options.seexpire
158
+ #refresher = "uac"
159
+ #expirelasttouch = +new Date()
156
160
 
157
161
  /**
158
162
  * Construct our call object with all defaults, including a default UUID.
@@ -543,6 +547,10 @@ class call {
543
547
  return "uas"==this.type?"inbound":"outbound"
544
548
  }
545
549
 
550
+ /**
551
+ *
552
+ * @param { object } startingpoint
553
+ */
546
554
  #overridecallerid( startingpoint ) {
547
555
 
548
556
  if( this.options.callerid ) {
@@ -552,6 +560,10 @@ class call {
552
560
  }
553
561
  }
554
562
 
563
+ /**
564
+ *
565
+ * @param { object } startingpoint
566
+ */
555
567
  #overridecalledid( startingpoint ) {
556
568
  startingpoint.privacy = this.options.privacy
557
569
 
@@ -653,6 +665,10 @@ class call {
653
665
  this.#fromremoteheaders( startingpoint )
654
666
  }
655
667
 
668
+ /**
669
+ *
670
+ * @returns { object }
671
+ */
656
672
  #callerid() {
657
673
  const startingpoint = {
658
674
  "name": "",
@@ -699,6 +715,10 @@ class call {
699
715
  return this.#callerid()
700
716
  }
701
717
 
718
+ /**
719
+ *
720
+ * @returns { object }
721
+ */
702
722
  #calledid() {
703
723
  const startingpoint = {
704
724
  "name": "",
@@ -710,24 +730,7 @@ class call {
710
730
  }
711
731
 
712
732
  if( "uas" == this.type ) this.#calledidforuas( startingpoint )
713
- else {
714
- if( !this.options.partycalled ) this.#calledidforuac( startingpoint )
715
-
716
- const other = this.other
717
-
718
- if( other ) {
719
- if( "uas" == other.type ) {
720
- if( this.options.partycalled ) {
721
- other.#calleridforuas( startingpoint )
722
- } else {
723
- other.#calledidforuas( startingpoint )
724
- }
725
- } else {
726
- other.#calledidforuac( startingpoint )
727
- }
728
- other.#overridecalledid( startingpoint )
729
- }
730
- }
733
+ else if( !this.options.partycalled ) this.#calledidforuac( startingpoint )
731
734
 
732
735
  this.#overridecalledid( startingpoint )
733
736
 
@@ -735,11 +738,11 @@ class call {
735
738
  }
736
739
 
737
740
  /**
738
- * Returns the called object. i.e.
739
- * If we are inbound then this is the destination
740
- * If we are outbound then this is the entity we are calling
741
- * @returns { remoteid }
742
- */
741
+ * Returns the called object. i.e.
742
+ * If we are inbound then this is the destination
743
+ * If we are outbound then this is the entity we are calling
744
+ * @returns { remoteid }
745
+ */
743
746
  get calledid() {
744
747
  return this.#calledid()
745
748
  }
@@ -1306,7 +1309,7 @@ class call {
1306
1309
  async _onearlybridge() {
1307
1310
  if( this.destroyed ) return
1308
1311
 
1309
- this._addevents( this._dialog )
1312
+ this.#addevents( this._dialog )
1310
1313
 
1311
1314
  this.sdp.remote = sdpgen.create( this._dialog.remote.sdp )
1312
1315
 
@@ -1392,7 +1395,7 @@ class call {
1392
1395
 
1393
1396
  this._dialog = await this._dialog.ack( this.sdp.local.toString() )
1394
1397
  this.established = true
1395
- this._addevents( this._dialog )
1398
+ this.#addevents( this._dialog )
1396
1399
 
1397
1400
  await this.#answerparent()
1398
1401
 
@@ -1794,19 +1797,23 @@ class call {
1794
1797
  return
1795
1798
  }
1796
1799
 
1800
+ const headers = {
1801
+ "User-Agent": "project",
1802
+ "Supported": "replaces, timer",
1803
+ "Require": "timer",
1804
+ "Session-Expires": `${this.#seexpire/1000};refresher=${this.#refresher}`
1805
+ }
1806
+
1797
1807
  const dialog = await callmanager.options.srf.createUAS( this._req, this._res, {
1798
1808
  localSdp: this.sdp.local.toString(),
1799
- headers: {
1800
- "User-Agent": "project",
1801
- "Supported": "replaces"
1802
- }
1809
+ headers
1803
1810
  } )
1804
1811
 
1805
1812
  this.#setdialog( dialog )
1806
1813
  this.sip.tags.local = dialog.sip.localTag
1807
1814
  callstore.set( this )
1808
1815
 
1809
- this._addevents( this._dialog )
1816
+ this.#addevents( this._dialog )
1810
1817
  this._em.emit( "call.answered", this )
1811
1818
  callmanager.options.em.emit( "call.answered", this )
1812
1819
 
@@ -2407,51 +2414,77 @@ class call {
2407
2414
  }
2408
2415
 
2409
2416
  /**
2410
- Add events for the drachtio dialog object that this object requires.
2411
- @private
2417
+ * Allow a good margin of error - but hangup if we have disappeared
2418
+ */
2419
+ #hasseexpiredexpired() {
2420
+
2421
+ const now = +new Date()
2422
+ if( ( now - this.#expirelasttouch ) > ( this.#seexpire * 2 ) ) {
2423
+ this.hangup( hangupcodes.USER_GONE )
2424
+ return true
2425
+ }
2426
+ return false
2427
+ }
2428
+
2429
+ /**
2430
+ *
2431
+ */
2432
+ #checkthischannelforwebrtcdirection() {
2433
+ if( this._iswebrtc && this.channels && this.channels.audio ) {
2434
+ /* we are sending invite so we MUST offer be actpass */
2435
+ this.channels.secure.audio.offer = true
2436
+ this.sdp.local
2437
+ .secure( this.channels.audio.local.dtls.fingerprint, this.#getsrtpsetup( this.channels.secure.audio ) )
2438
+ }
2439
+ }
2440
+
2441
+ /**
2442
+ * Add events for the drachtio dialog object that this object requires.
2443
+ * @param { object } dialog
2412
2444
  */
2413
- _addevents( dialog ) {
2445
+ #addevents( dialog ) {
2414
2446
 
2415
2447
  /* Drachtio doesn't appear to have finished SE support, i.e. it sends
2416
2448
  a regular INVITE when we set the Supported: timer and Session-Expires headers
2417
2449
  but it doesn't appear to indicate to us when it does fail. It most cases our
2418
2450
  RTP stall timer will kick in first, but if a call is placed on hold followed
2419
- by AWOL... */
2451
+ by AWOL... My new startegy is we perform teh reinvite - if we are the refresher
2452
+ which should refresh any timer in Drachtio - and we check for revites and timeouts. */
2420
2453
  this._timers.seinterval = setInterval( async () => {
2421
2454
 
2422
2455
  try {
2423
- if( this.destroyed ) {
2424
- /* this should be done - but we are still running */
2456
+ if( this.destroyed || this.#hasseexpiredexpired() ) {
2425
2457
  clearInterval( this._timers.seinterval )
2426
2458
  return
2427
2459
  }
2428
2460
 
2461
+ if( this.#refresher !== this.type ) return
2429
2462
  if( "function" != typeof dialog.request ) return
2430
2463
 
2431
- if( this._iswebrtc && this.channels && this.channels.audio ) {
2432
- /* we are sending invite so we MUST offer be actpass */
2433
- this.channels.secure.audio.offer = true
2434
- this.sdp.local
2435
- .secure( this.channels.audio.local.dtls.fingerprint, this.#getsrtpsetup( this.channels.secure.audio ) )
2436
- }
2464
+ this.#checkthischannelforwebrtcdirection()
2437
2465
 
2438
2466
  const opts = {
2439
- "method": "INVITE",
2440
- "body": this.sdp.local.toString()
2467
+ method: "INVITE",
2468
+ body: this.sdp.local.toString(),
2469
+ headers: this.options.headers
2441
2470
  }
2442
2471
 
2443
2472
  const res = await dialog.request( opts )
2444
- .catch( ( e ) => {
2445
- console.trace( e )
2446
- this.hangup( hangupcodes.USER_GONE )
2447
- } )
2448
-
2449
- if( !this.destroyed && 200 != res.msg.status ) {
2473
+
2474
+ if( this.destroyed ) return
2475
+ if( 200 != res.msg.status ) {
2450
2476
  this.hangup( hangupcodes.USER_GONE )
2477
+ return
2451
2478
  }
2452
- } catch( e ) { /* empty */ }
2453
2479
 
2454
- }, callmanager.options.seexpire )
2480
+ this.#expirelasttouch = +new Date()
2481
+
2482
+ } catch( e ) {
2483
+ console.trace( e )
2484
+ }
2485
+
2486
+ }, this.#seexpire * 0.45 )
2487
+ this.#expirelasttouch = +new Date()
2455
2488
 
2456
2489
  dialog.on( "destroy", ( /* req */ ) => {
2457
2490
  if( this._state._hangup ) return
@@ -2484,6 +2517,7 @@ class call {
2484
2517
 
2485
2518
  dialog.on( "modify", ( req, res ) => {
2486
2519
  // The application must respond, using the res parameter provided.
2520
+ this.#expirelasttouch = +new Date()
2487
2521
  if( "INVITE" !== req.msg.method ) return
2488
2522
 
2489
2523
  this.channels.secure.audio.reinvite = true
@@ -2515,6 +2549,7 @@ class call {
2515
2549
 
2516
2550
  dialog.on( "refer", async ( req, res ) => {
2517
2551
  try {
2552
+ this.#expirelasttouch = +new Date()
2518
2553
  /*
2519
2554
  We only support the xfer of 2 legged calls. The xfered call will pick up
2520
2555
  the auth from the transferee. For example, inbound anonymous call, gets handled
@@ -3694,7 +3729,7 @@ class call {
3694
3729
  * @property { boolean } [ clicktocall ] - if set to true, will set autoanswer to true and swap the source and desination on caller ID, set to true for both caller and called
3695
3730
  * @property { boolean } [ partycalled ] - reverses the direction of the call from the invite, from the point of view of the called in a 3pcc context
3696
3731
  * @property { boolean } [ partycaller ] - reverses the direction of the call from the invite, from the point of view of the caller in a 3pcc context
3697
- * @property { boolean } [ late ] - late negotiation
3732
+ * @property { boolean } [ late ] - late negotiation
3698
3733
  * @property { boolean } [ privacy ] - sets the privacy
3699
3734
  * @property { entity } [ entity ] - used to store this call against and look up a contact string if not supplied.
3700
3735
  * @property { object } [ entity ]
@@ -3786,6 +3821,8 @@ class call {
3786
3821
  newcall.options.headers[ "allow-events" ] = "talk, hold, presence, as-feature-event, dialog, call-info, sla, include-session-description, message-summary, refer"
3787
3822
  newcall.options.headers[ "allow" ] = "INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO, UPDATE, REGISTER, REFER, NOTIFY, PUBLISH, SUBSCRIBE"
3788
3823
  newcall.options.headers[ "supported" ] = "timer, path, replaces"
3824
+ newcall.options.headers[ "min-se" ] = "90"
3825
+ newcall.options.headers[ "session-expires" ] = `${callmanager.options.seexpire/1000}`
3789
3826
 
3790
3827
  newcall.#confignetwork( options )
3791
3828
  await newcall.#openchannelsfornewuac()
@@ -3861,6 +3898,7 @@ class call {
3861
3898
  return newcall
3862
3899
  }
3863
3900
 
3901
+ newcall.#parsesessionexpires( newdialog.res )
3864
3902
  newcall.parseallow( newdialog.res )
3865
3903
 
3866
3904
  newcall.#setdialog( newdialog )
@@ -3893,10 +3931,30 @@ class call {
3893
3931
  this.established = true
3894
3932
  }
3895
3933
 
3934
+ /**
3935
+ *
3936
+ * @param { object } req
3937
+ */
3938
+ #parsesessionexpires( req ) {
3939
+ if( !req ) return
3940
+ const se = req.get( "Session-Expires" )
3941
+ if( !se ) return
3942
+
3943
+ const results = se.match( sessionexpirevaluere )
3944
+ let expiresin = parseInt( results[ 1 ] )
3945
+
3946
+ /* min and max values */
3947
+ if( 90 > expiresin ) expiresin = 90
3948
+ if( 3600 < expiresin ) expiresin = 3600
3949
+ this.#seexpire = expiresin * 1000 /* mS */
3950
+
3951
+ if( results[ 2 ] ) this.#refresher = results[ 2 ]
3952
+ }
3953
+
3896
3954
  /**
3897
3955
  * Take as input a request or a response.
3898
- *
3899
3956
  * @param { object } req
3957
+ * @private - apart from test
3900
3958
  */
3901
3959
  parseallow( req ) {
3902
3960
  if( !req || !req.get ) return
@@ -3928,6 +3986,7 @@ class call {
3928
3986
  const c = new call()
3929
3987
 
3930
3988
  c.type = "uas"
3989
+ c.#refresher = "uas"
3931
3990
 
3932
3991
  /**
3933
3992
  @typedef { Object } source
@@ -3957,6 +4016,7 @@ class call {
3957
4016
  */
3958
4017
  c._res = res
3959
4018
 
4019
+ c.#parsesessionexpires( req )
3960
4020
  c.parseallow( req )
3961
4021
 
3962
4022
  await callstore.set( c )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babblevoice/babble-drachtio-callmanager",
3
- "version": "3.7.1",
3
+ "version": "3.7.3",
4
4
  "description": "Call processing to create a PBX",
5
5
  "main": "index.js",
6
6
  "scripts": {