@babblevoice/babble-drachtio-callmanager 3.7.2 → 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 +92 -31
  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.
@@ -1305,7 +1309,7 @@ class call {
1305
1309
  async _onearlybridge() {
1306
1310
  if( this.destroyed ) return
1307
1311
 
1308
- this._addevents( this._dialog )
1312
+ this.#addevents( this._dialog )
1309
1313
 
1310
1314
  this.sdp.remote = sdpgen.create( this._dialog.remote.sdp )
1311
1315
 
@@ -1391,7 +1395,7 @@ class call {
1391
1395
 
1392
1396
  this._dialog = await this._dialog.ack( this.sdp.local.toString() )
1393
1397
  this.established = true
1394
- this._addevents( this._dialog )
1398
+ this.#addevents( this._dialog )
1395
1399
 
1396
1400
  await this.#answerparent()
1397
1401
 
@@ -1793,19 +1797,23 @@ class call {
1793
1797
  return
1794
1798
  }
1795
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
+
1796
1807
  const dialog = await callmanager.options.srf.createUAS( this._req, this._res, {
1797
1808
  localSdp: this.sdp.local.toString(),
1798
- headers: {
1799
- "User-Agent": "project",
1800
- "Supported": "replaces"
1801
- }
1809
+ headers
1802
1810
  } )
1803
1811
 
1804
1812
  this.#setdialog( dialog )
1805
1813
  this.sip.tags.local = dialog.sip.localTag
1806
1814
  callstore.set( this )
1807
1815
 
1808
- this._addevents( this._dialog )
1816
+ this.#addevents( this._dialog )
1809
1817
  this._em.emit( "call.answered", this )
1810
1818
  callmanager.options.em.emit( "call.answered", this )
1811
1819
 
@@ -2406,51 +2414,77 @@ class call {
2406
2414
  }
2407
2415
 
2408
2416
  /**
2409
- Add events for the drachtio dialog object that this object requires.
2410
- @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
2411
2444
  */
2412
- _addevents( dialog ) {
2445
+ #addevents( dialog ) {
2413
2446
 
2414
2447
  /* Drachtio doesn't appear to have finished SE support, i.e. it sends
2415
2448
  a regular INVITE when we set the Supported: timer and Session-Expires headers
2416
2449
  but it doesn't appear to indicate to us when it does fail. It most cases our
2417
2450
  RTP stall timer will kick in first, but if a call is placed on hold followed
2418
- 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. */
2419
2453
  this._timers.seinterval = setInterval( async () => {
2420
2454
 
2421
2455
  try {
2422
- if( this.destroyed ) {
2423
- /* this should be done - but we are still running */
2456
+ if( this.destroyed || this.#hasseexpiredexpired() ) {
2424
2457
  clearInterval( this._timers.seinterval )
2425
2458
  return
2426
2459
  }
2427
2460
 
2461
+ if( this.#refresher !== this.type ) return
2428
2462
  if( "function" != typeof dialog.request ) return
2429
2463
 
2430
- if( this._iswebrtc && this.channels && this.channels.audio ) {
2431
- /* we are sending invite so we MUST offer be actpass */
2432
- this.channels.secure.audio.offer = true
2433
- this.sdp.local
2434
- .secure( this.channels.audio.local.dtls.fingerprint, this.#getsrtpsetup( this.channels.secure.audio ) )
2435
- }
2464
+ this.#checkthischannelforwebrtcdirection()
2436
2465
 
2437
2466
  const opts = {
2438
- "method": "INVITE",
2439
- "body": this.sdp.local.toString()
2467
+ method: "INVITE",
2468
+ body: this.sdp.local.toString(),
2469
+ headers: this.options.headers
2440
2470
  }
2441
2471
 
2442
2472
  const res = await dialog.request( opts )
2443
- .catch( ( e ) => {
2444
- console.trace( e )
2445
- this.hangup( hangupcodes.USER_GONE )
2446
- } )
2447
-
2448
- if( !this.destroyed && 200 != res.msg.status ) {
2473
+
2474
+ if( this.destroyed ) return
2475
+ if( 200 != res.msg.status ) {
2449
2476
  this.hangup( hangupcodes.USER_GONE )
2477
+ return
2450
2478
  }
2451
- } catch( e ) { /* empty */ }
2452
2479
 
2453
- }, 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()
2454
2488
 
2455
2489
  dialog.on( "destroy", ( /* req */ ) => {
2456
2490
  if( this._state._hangup ) return
@@ -2483,6 +2517,7 @@ class call {
2483
2517
 
2484
2518
  dialog.on( "modify", ( req, res ) => {
2485
2519
  // The application must respond, using the res parameter provided.
2520
+ this.#expirelasttouch = +new Date()
2486
2521
  if( "INVITE" !== req.msg.method ) return
2487
2522
 
2488
2523
  this.channels.secure.audio.reinvite = true
@@ -2514,6 +2549,7 @@ class call {
2514
2549
 
2515
2550
  dialog.on( "refer", async ( req, res ) => {
2516
2551
  try {
2552
+ this.#expirelasttouch = +new Date()
2517
2553
  /*
2518
2554
  We only support the xfer of 2 legged calls. The xfered call will pick up
2519
2555
  the auth from the transferee. For example, inbound anonymous call, gets handled
@@ -3693,7 +3729,7 @@ class call {
3693
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
3694
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
3695
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
3696
- * @property { boolean } [ late ] - late negotiation
3732
+ * @property { boolean } [ late ] - late negotiation
3697
3733
  * @property { boolean } [ privacy ] - sets the privacy
3698
3734
  * @property { entity } [ entity ] - used to store this call against and look up a contact string if not supplied.
3699
3735
  * @property { object } [ entity ]
@@ -3785,6 +3821,8 @@ class call {
3785
3821
  newcall.options.headers[ "allow-events" ] = "talk, hold, presence, as-feature-event, dialog, call-info, sla, include-session-description, message-summary, refer"
3786
3822
  newcall.options.headers[ "allow" ] = "INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, INFO, UPDATE, REGISTER, REFER, NOTIFY, PUBLISH, SUBSCRIBE"
3787
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}`
3788
3826
 
3789
3827
  newcall.#confignetwork( options )
3790
3828
  await newcall.#openchannelsfornewuac()
@@ -3860,6 +3898,7 @@ class call {
3860
3898
  return newcall
3861
3899
  }
3862
3900
 
3901
+ newcall.#parsesessionexpires( newdialog.res )
3863
3902
  newcall.parseallow( newdialog.res )
3864
3903
 
3865
3904
  newcall.#setdialog( newdialog )
@@ -3892,10 +3931,30 @@ class call {
3892
3931
  this.established = true
3893
3932
  }
3894
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
+
3895
3954
  /**
3896
3955
  * Take as input a request or a response.
3897
- *
3898
3956
  * @param { object } req
3957
+ * @private - apart from test
3899
3958
  */
3900
3959
  parseallow( req ) {
3901
3960
  if( !req || !req.get ) return
@@ -3927,6 +3986,7 @@ class call {
3927
3986
  const c = new call()
3928
3987
 
3929
3988
  c.type = "uas"
3989
+ c.#refresher = "uas"
3930
3990
 
3931
3991
  /**
3932
3992
  @typedef { Object } source
@@ -3956,6 +4016,7 @@ class call {
3956
4016
  */
3957
4017
  c._res = res
3958
4018
 
4019
+ c.#parsesessionexpires( req )
3959
4020
  c.parseallow( req )
3960
4021
 
3961
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.2",
3
+ "version": "3.7.3",
4
4
  "description": "Call processing to create a PBX",
5
5
  "main": "index.js",
6
6
  "scripts": {