diff --git a/lib/tasks/sip_refer.js b/lib/tasks/sip_refer.js index f4d41f0e..580befea 100644 --- a/lib/tasks/sip_refer.js +++ b/lib/tasks/sip_refer.js @@ -47,7 +47,17 @@ class TaskSipRefer extends Task { /* if we fail, fall through to next verb. If success, we should get BYE from far end */ if (this.referStatus === 202) { + this._notifyTimer = setTimeout(() => { + this.logger.info('TaskSipRefer:exec - no NOTIFY received in 15 secs, exiting'); + this.performAction({refer_status: this.referStatus}) + .catch((err) => this.logger.error(err, 'TaskSipRefer:exec - error performing action')); + this.notifyTaskDone(); + }, 15000); await this.awaitTaskDone(); + if (this._notifyTimer) { + clearTimeout(this._notifyTimer); + this._notifyTimer = null; + } } else { await this.performAction({refer_status: this.referStatus}); @@ -71,10 +81,10 @@ class TaskSipRefer extends Task { const contentType = req.get('Content-Type'); this.logger.debug({body: req.body}, `TaskSipRefer:_handleNotify got ${contentType}`); - if (contentType === 'message/sipfrag') { + if (contentType?.includes('message/sipfrag')) { const arr = /SIP\/2\.0\s+(\d+)/.exec(req.body); if (arr) { - const status = arr[1]; + const status = typeof arr[1] === 'string' ? parseInt(arr[1], 10) : arr[1]; this.logger.debug(`TaskSipRefer:_handleNotify: call got status ${status}`); if (this.eventHook) { const b3 = this.getTracingPropagation(); diff --git a/package.json b/package.json index fab1f826..fb144714 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "bugs": {}, "scripts": { "start": "node app", - "test": "NODE_ENV=test JAMBONES_HOSTING=1 HTTP_POOL=1 DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 DRACHTIO_SECRET=cymru JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_PORT=3360 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=127.0.0.1 JAMBONES_REDIS_PORT=16379 JAMBONES_LOGLEVEL=debug ENABLE_METRICS=0 HTTP_PORT=3000 JAMBONES_SBCS=172.38.0.10 JAMBONES_FREESWITCH=127.0.0.1:8022:ClueCon:docker-host JAMBONES_TIME_SERIES_HOST=127.0.0.1 JAMBONES_NETWORK_CIDR=172.38.0.0/16 node test/ ", + "test": "NODE_ENV=test JAMBONES_HOSTING=1 HTTP_POOL=1 DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 DRACHTIO_SECRET=cymru JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_PORT=3360 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=127.0.0.1 JAMBONES_REDIS_PORT=16379 JAMBONES_LOGLEVEL=error ENABLE_METRICS=0 HTTP_PORT=3000 JAMBONES_SBCS=172.38.0.10 JAMBONES_FREESWITCH=127.0.0.1:8022:ClueCon:docker-host JAMBONES_TIME_SERIES_HOST=127.0.0.1 JAMBONES_NETWORK_CIDR=172.38.0.0/16 node test/ ", "coverage": "./node_modules/.bin/nyc --reporter html --report-dir ./coverage npm run test", "jslint": "eslint app.js lib" }, diff --git a/test/index.js b/test/index.js index 5f0bfdf4..a9321996 100644 --- a/test/index.js +++ b/test/index.js @@ -8,5 +8,6 @@ require('./gather-tests'); require('./sip-request-tests'); require('./create-call-test'); require('./play-tests'); +require('./sip-refer-tests'); require('./remove-test-db'); require('./docker_stop'); diff --git a/test/scenarios/uac-refer-no-notify.xml b/test/scenarios/uac-refer-no-notify.xml new file mode 100644 index 00000000..6f6d4df5 --- /dev/null +++ b/test/scenarios/uac-refer-no-notify.xml @@ -0,0 +1,95 @@ + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: + Call-ID: [call_id] + CSeq: 1 INVITE + Contact: sip:[from]@[local_ip]:[local_port] + Max-Forwards: 70 + X-Account-Sid: bb845d4b-83a9-4cde-a6e9-50f3743bab3f + Subject: uac-refer-no-notify.xml + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] + s=- + c=IN IP[media_ip_type] [media_ip] + t=0 0 + m=audio [media_port] RTP/AVP 0 + a=rtpmap:0 PCMU/8000 + + ]]> + + + + + + + + + + + + + + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: [service] [peer_tag_param] + Call-ID: [call_id] + CSeq: 1 ACK + Max-Forwards: 70 + Subject: REFER test with no NOT + Content-Length: 0 + + ]]> + + + + + + + + + + diff --git a/test/scenarios/uac-refer-with-notify.xml b/test/scenarios/uac-refer-with-notify.xml new file mode 100644 index 00000000..93a48b4d --- /dev/null +++ b/test/scenarios/uac-refer-with-notify.xml @@ -0,0 +1,115 @@ + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: + Call-ID: [call_id] + CSeq: 1 INVITE + Contact: sip:[from]@[local_ip]:[local_port] + Max-Forwards: 70 + X-Account-Sid: bb845d4b-83a9-4cde-a6e9-50f3743bab3f + Subject: uac-refer-with-notify.xml + Content-Type: application/sdp + Content-Length: [len] + + v=0 + o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] + s=- + c=IN IP[media_ip_type] [media_ip] + t=0 0 + m=audio [media_port] RTP/AVP 0 + a=rtpmap:0 PCMU/8000 + + ]]> + + + + + + + + + + + + + + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: [peer_tag_param] + Call-ID: [call_id] + CSeq: 1 ACK + Max-Forwards: 70 + Subject: uac-refer-with-notify.xml + Content-Length: 0 + + ]]> + + + + + + + + + + + ;tag=[pid]SIPpTag00[call_number] + To: [peer_tag_param] + Call-ID: [call_id] + CSeq: 2 NOTIFY + Contact: sip:sipp@[local_ip]:[local_port] + Max-Forwards: 70 + Subject: uac-refer-with-notify.xml + Content-Type: message/sipfrag;version=2.0 + Content-Length: 16 + + SIP/2.0 200 OK + ]]> + + + + diff --git a/test/sip-refer-tests.js b/test/sip-refer-tests.js new file mode 100644 index 00000000..7b5de67c --- /dev/null +++ b/test/sip-refer-tests.js @@ -0,0 +1,100 @@ +const test = require('tape'); +const { sippUac } = require('./sipp')('test_fs'); +const clearModule = require('clear-module'); +const {provisionCallHook, provisionCustomHook, provisionActionHook} = require('./utils') +const bent = require('bent'); +const getJSON = bent('json') + +const sleepFor = async(ms) => new Promise(resolve => setTimeout(resolve, ms)); + +process.on('unhandledRejection', (reason, p) => { + console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); +}); + +function connect(connectable) { + return new Promise((resolve, reject) => { + connectable.on('connect', () => { + return resolve(); + }); + }); +} + +test('\'refer\' tests w/202 and NOTIFY', {timeout: 25000}, async(t) => { + clearModule.all(); + const {srf, disconnect} = require('../app'); + + try { + await connect(srf); + + // GIVEN + const verbs = [ + { + verb: 'say', + text: 'silence_stream://100' + }, + { + verb: 'sip:refer', + referTo: '123456', + actionHook: '/actionHook' + } + ]; + const noVerbs = []; + + const from = 'refer_with_notify'; + provisionCallHook(from, verbs); + provisionActionHook(from, noVerbs) + + // THEN + await sippUac('uac-refer-with-notify.xml', '172.38.0.10', from); + t.pass('refer: successfully received 202 Accepted'); + await sleepFor(1000); + const obj = await getJSON(`http:127.0.0.1:3100/lastRequest/${from}_actionHook`); + t.ok(obj.body.final_referred_call_status === 200, 'refer: successfully received NOTIFY with 200 OK'); + //console.log(`obj: ${JSON.stringify(obj)}`); + disconnect(); + } catch (err) { + console.log(`error received: ${err}`); + disconnect(); + t.error(err); + } +}); + +test('\'refer\' tests w/202 but no NOTIFY', {timeout: 25000}, async(t) => { + clearModule.all(); + const {srf, disconnect} = require('../app'); + + try { + await connect(srf); + + // GIVEN + const verbs = [ + { + verb: 'say', + text: 'silence_stream://100' + }, + { + verb: 'sip:refer', + referTo: '123456', + actionHook: '/actionHook' + } + ]; + const noVerbs = []; + + const from = 'refer_no_notify'; + provisionCallHook(from, verbs); + provisionActionHook(from, noVerbs) + + // THEN + await sippUac('uac-refer-no-notify.xml', '172.38.0.10', from); + t.pass('refer: successfully received 202 Accepted w/o NOTIFY'); + await sleepFor(17000); + const obj = await getJSON(`http:127.0.0.1:3100/lastRequest/${from}_actionHook`); + console.log(`obj: ${JSON.stringify(obj)}`); + t.ok(obj.body.refer_status === 202, 'refer: successfully timed out and reported 202'); + disconnect(); + } catch (err) { + console.log(`error received: ${err}`); + disconnect(); + t.error(err); + } +}); diff --git a/test/sip-tests.js b/test/sip-tests.js index 00c73cb6..1e60f24b 100644 --- a/test/sip-tests.js +++ b/test/sip-tests.js @@ -53,6 +53,13 @@ test('incoming call tests', (t) => { .then(() => { return t.pass('handles in-dialog requests'); }) + .then(() => { + return sippUac('uac-refer-no-notify.xml', '172.38.0.30'); + }) + .then(() => { + return t.pass('handles sip:refer where we get 202 but no NOTIFY'); + }) + .then(() => { srf.disconnect(); t.end(); diff --git a/test/utils.js b/test/utils.js index 0a0d7626..6ce588a8 100644 --- a/test/utils.js +++ b/test/utils.js @@ -24,4 +24,13 @@ const provisionCustomHook = (from, verbs) => { post(`/customHookMapping`, mapping); } -module.exports = { provisionCallHook, provisionCustomHook} +const provisionActionHook = (from, verbs) => { + const mapping = { + from, + data: JSON.stringify(verbs) + }; + const post = bent('http://127.0.0.1:3100', 'POST', 'string', 200); + post(`/actionHook`, mapping); +} + +module.exports = { provisionCallHook, provisionCustomHook, provisionActionHook}