diff --git a/lib/session/call-session.js b/lib/session/call-session.js index a2ec9d32..842398b1 100644 --- a/lib/session/call-session.js +++ b/lib/session/call-session.js @@ -1172,6 +1172,11 @@ class CallSession extends Emitter { this.wakeupResolver({reason: 'session ended'}); this.wakeupResolver = null; } + + if (this._maxCallDurationTimer) { + clearTimeout(this._maxCallDurationTimer); + this._maxCallDurationTimer = null; + } } /** @@ -2680,6 +2685,19 @@ Duration=${duration} ` this.verbHookSpan = null; } } + + /** + * _onMaxCallDuration - called when the call has reached the maximum duration + */ +_onMaxCallDuration() { + this.logger.info(`callSession:_onMaxCallDuration tearing down call as it has reached ${this.timeLimit}s`); + if (!this.dlg) { + this.logger.debug('CallSession:_onMaxCallDuration - no dialog, call already gone'); + return; + } + this._jambonzHangup('Max Call Duration'); + this._maxCallDurationTimer = null; + } } module.exports = CallSession; diff --git a/lib/tasks/rest_dial.js b/lib/tasks/rest_dial.js index ee347078..7d21a9a4 100644 --- a/lib/tasks/rest_dial.js +++ b/lib/tasks/rest_dial.js @@ -12,6 +12,7 @@ class TaskRestDial extends Task { this.from = this.data.from; this.callerName = this.data.callerName; + this.timeLimit = this.data.timeLimit; this.fromHost = this.data.fromHost; this.to = this.data.to; this.call_hook = this.data.call_hook; @@ -66,6 +67,9 @@ class TaskRestDial extends Task { const cs = this.callSession; cs.setDialog(dlg); cs.referHook = this.referHook; + if (this.timeLimit) { + cs.startMaxCallDurationTimer(this.timeLimit); + } this.logger.debug('TaskRestDial:_onConnect - call connected'); if (this.sipRequestWithinDialogHook) this._initSipRequestWithinDialogHandler(cs, dlg); try { diff --git a/package-lock.json b/package-lock.json index 4dd99134..85474a61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,8 +17,8 @@ "@jambonz/realtimedb-helpers": "^0.8.8", "@jambonz/speech-utils": "^0.1.20", "@jambonz/stats-collector": "^0.1.10", - "@jambonz/time-series": "^0.2.9", - "@jambonz/verb-specifications": "^0.0.83", + "@jambonz/time-series": "^0.2.13", + "@jambonz/verb-specifications": "^0.0.91", "@opentelemetry/api": "^1.8.0", "@opentelemetry/exporter-jaeger": "^1.23.0", "@opentelemetry/exporter-trace-otlp-http": "^0.50.0", @@ -1577,9 +1577,10 @@ } }, "node_modules/@jambonz/verb-specifications": { - "version": "0.0.83", - "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.83.tgz", - "integrity": "sha512-3m1o3gnWw1yEwNfkZ6IIyUhdUqsQ3aWBNoWJHswe4udvVbEIxPHi+8jKGTJVF2vxddzwfOWp7RmMCV9RmKWzkw==", + "version": "0.0.91", + "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.91.tgz", + "integrity": "sha512-C9UIKytogOdUp5sGqfEetl82/8lDQ8zgEU5diQKpU4dZeA3M8qItqjJGSXexNh4PK9ECUYNorsPoRGNxGcr2IA==", + "license": "MIT", "dependencies": { "debug": "^4.3.4", "pino": "^8.8.0" @@ -10674,9 +10675,9 @@ } }, "@jambonz/verb-specifications": { - "version": "0.0.83", - "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.83.tgz", - "integrity": "sha512-3m1o3gnWw1yEwNfkZ6IIyUhdUqsQ3aWBNoWJHswe4udvVbEIxPHi+8jKGTJVF2vxddzwfOWp7RmMCV9RmKWzkw==", + "version": "0.0.91", + "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.91.tgz", + "integrity": "sha512-C9UIKytogOdUp5sGqfEetl82/8lDQ8zgEU5diQKpU4dZeA3M8qItqjJGSXexNh4PK9ECUYNorsPoRGNxGcr2IA==", "requires": { "debug": "^4.3.4", "pino": "^8.8.0" diff --git a/package.json b/package.json index a75dd3be..7a873968 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "@jambonz/realtimedb-helpers": "^0.8.8", "@jambonz/speech-utils": "^0.1.20", "@jambonz/stats-collector": "^0.1.10", - "@jambonz/time-series": "^0.2.9", - "@jambonz/verb-specifications": "^0.0.83", + "@jambonz/verb-specifications": "^0.0.91", + "@jambonz/time-series": "^0.2.13", "@opentelemetry/api": "^1.8.0", "@opentelemetry/exporter-jaeger": "^1.23.0", "@opentelemetry/exporter-trace-otlp-http": "^0.50.0", diff --git a/test/create-call-test.js b/test/create-call-test.js index 4b7b58bb..0b6b780d 100644 --- a/test/create-call-test.js +++ b/test/create-call-test.js @@ -222,3 +222,62 @@ test('test create-call app_json', async(t) => { t.error(err); } }); + +test('test create-call timeLimit', async(t) => { + clearModule.all(); + const {srf, disconnect} = require('../app'); + + try { + await connect(srf); + + + // GIVEN + let from = 'create-call-app-json'; + let account_sid = 'bb845d4b-83a9-4cde-a6e9-50f3743bab3f'; + + // Give UAS app time to come up + const p = sippUac('uas.xml', '172.38.0.10', from); + await waitFor(1000); + + const startTime = Date.now(); + + const app_json = `[ + { + "verb": "pause", + "length": 7 + } + ]`; + + const post = bent('http://127.0.0.1:3000/', 'POST', 'json', 201); + post('v1/createCall', { + 'account_sid':account_sid, + "call_hook": { + "url": "http://127.0.0.1:3100/", + "method": "POST", + "username": "username", + "password": "password" + }, + app_json, + "from": from, + "to": { + "type": "phone", + "number": "15583084809" + }, + "timeLimit": 1, + "speech_recognizer_vendor": "google", + "speech_recognizer_language": "en" + }); + + //THEN + await p; + const endTime = Date.now(); + + t.ok(endTime - startTime < 2000, 'create-call: timeLimit is respected'); + + disconnect(); + } catch (err) { + console.log(`error received: ${err}`); + disconnect(); + t.error(err); + } +});