Compare commits

...
Author SHA1 Message Date
Hoan Luu Huu 03d3a9695d forward reinvite error response to other destination (#133)
* forward reinvite error response to other destination

* update comment review
2024-03-07 07:48:33 -05:00
Hoan Luu Huu f5be95006c start/stop/pause/resume recording success when one of siprec server success. (#131)
* start/stop/pause/resume recording success when one of siprec server success.

* wip

* wip

* update customer header for pause and resume

* allow timeout for siprec action

* wip

* wip

* wip

* update siprec client utils
2024-03-05 13:05:42 -05:00
Dave Horton 2747bc15d6 update deps, comment out questionable code 2024-01-17 13:37:07 -05:00
Dave Horton cdfbf384fe update db-helpers 2024-01-17 13:26:37 -05:00
3 changed files with 95 additions and 47 deletions
+63 -15
View File
@@ -67,6 +67,7 @@ class CallSession extends Emitter {
this.account_sid = req.locals.account_sid;
this.service_provider_sid = req.locals.service_provider_sid;
this.srsClients = [];
this.recordingNoAnswerTimeout = (process.env.JAMBONES_RECORDING_NO_ANSWER_TIMEOUT || 2) * 1000;
}
get isFromMSTeams() {
@@ -119,6 +120,7 @@ class CallSession extends Emitter {
offer,
answer,
del,
query,
blockMedia,
unblockMedia,
blockDTMF,
@@ -133,6 +135,7 @@ class CallSession extends Emitter {
this.offer = offer;
this.answer = answer;
this.del = del;
this.query = query;
this.blockMedia = blockMedia;
this.unblockMedia = unblockMedia;
this.blockDTMF = blockDTMF;
@@ -156,7 +159,10 @@ class CallSession extends Emitter {
const wantsSrtp = this.req.locals.possibleWebRtcClient = SdpWantsSrtp(sdp);
const wantsSDES = SdpWantsSDES(sdp);
this.rtpEngineOpts = makeRtpEngineOpts(this.req, wantsSrtp, false, this.isFromMSTeams || wantsSDES);
this.rtpEngineResource = {destroy: this.del.bind(null, this.rtpEngineOpts.common)};
this.rtpEngineResource = {
destroy: this.del.bind(null, this.rtpEngineOpts.common),
query: this.query.bind(null, this.rtpEngineOpts.common),
};
const obj = parseUri(this.req.uri);
let proxy, host, uri;
@@ -330,6 +336,22 @@ class CallSession extends Emitter {
dlg.on('modify', this._onReinvite.bind(this, dlg));
}
_startRecordingNoAnswerTimer(res) {
this._clearRecordingNoAnswerTimer();
this.recordingNoAnswerTimer = setTimeout(() => {
this.logger.info('No response from SipRec server, return error to feature server');
this.isRecordingNoAnswerResponded = true;
res.send(400);
}, this.recordingNoAnswerTimeout);
}
_clearRecordingNoAnswerTimer() {
if (this.recordingNoAnswerTimer) {
clearTimeout(this.recordingNoAnswerTimer);
this.recordingNoAnswerTimer = null;
}
}
_stopRecording() {
if (this.srsClients.length) {
this.srsClients.forEach((c) => c.stop());
@@ -361,6 +383,14 @@ class CallSession extends Emitter {
dlg.on('destroy', async(bye) => {
const other = dlg.other;
this.rtpEngineResource.destroy().catch((err) => {});
/* DH: need a better understanding of why query before delete is a good idea
this.rtpEngineResource.query()
.then((results) => {
this.logger.info({results}, 'rtpengine query results');
return this.rtpEngineResource.destroy();
})
.catch((err) => {});
*/
this.activeCallIds.delete(this.req.get('Call-ID'));
try {
const headers = {};
@@ -663,6 +693,7 @@ Duration=${payload.duration} `
}
res.send(200, {body: response.sdp});
} catch (err) {
res.send(err.status || 500);
this.logger.error(err, 'Error handling reinvite');
}
}
@@ -693,12 +724,12 @@ Duration=${payload.duration} `
}
else if (reason.includes('CallRecording')) {
let succeeded = false;
const headers = contentType === 'application/json' && req.body ? JSON.parse(req.body) : {};
if (reason === 'startCallRecording') {
const from = this.req.getParsedHeader('From');
const to = this.req.getParsedHeader('To');
const aorFrom = from.uri;
const aorTo = to.uri;
const headers = contentType === 'application/json' && req.body ? JSON.parse(req.body) : {};
this.logger.info({to, from}, 'startCallRecording request for a call');
const srsUrl = req.get('X-Srs-Url');
@@ -742,11 +773,14 @@ Duration=${payload.duration} `
headers
}));
try {
succeeded = (await Promise.all(
this.srsClients.map((c) => c.start())
)).every((r) => r);
this._startRecordingNoAnswerTimer(res);
await Promise.any(this.srsClients.map((c) => c.start()));
// Only take who success accept request.
this.srsClients = this.srsClients.filter((c) => c.activated);
succeeded = true;
} catch (err) {
this.logger.error({err}, 'Error starting SipRec call recording');
succeeded = false;
}
}
else if (reason === 'stopCallRecording') {
@@ -756,11 +790,12 @@ Duration=${payload.duration} `
return;
}
try {
succeeded = (await Promise.all(
this.srsClients.map((c) => c.stop())
)).every((r) => r);
this._startRecordingNoAnswerTimer(res);
await Promise.any(this.srsClients.map((c) => c.stop()));
succeeded = true;
} catch (err) {
this.logger.error({err}, 'Error stopping SipRec call recording');
succeeded = false;
}
this.srsClients = [];
}
@@ -770,9 +805,14 @@ Duration=${payload.duration} `
res.send(400);
return;
}
succeeded = (await Promise.all(
this.srsClients.map((c) => c.pause())
)).every((r) => r);
try {
this._startRecordingNoAnswerTimer(res);
await Promise.any(this.srsClients.map((c) => c.pause({headers})));
succeeded = true;
} catch (err) {
this.logger.error({err}, 'Error pausing SipRec call recording');
succeeded = false;
}
}
else if (reason === 'resumeCallRecording') {
if (!this.srsClients.length || !this.srsClients.every((c) => c.paused)) {
@@ -780,11 +820,19 @@ Duration=${payload.duration} `
this.logger.info('discarding invalid resumeCallRecording request');
return;
}
succeeded = (await Promise.all(
this.srsClients.map((c) => c.resume())
)).every((r) => r);
try {
this._startRecordingNoAnswerTimer(res);
await Promise.any(this.srsClients.map((c) => c.resume({headers})));
succeeded = true;
} catch (err) {
this.logger.error({err}, 'Error resuming SipRec call recording');
succeeded = false;
}
}
if (!this.isRecordingNoAnswerResponded) {
this._clearRecordingNoAnswerTimer();
res.send(succeeded ? 200 : 503);
}
res.send(succeeded ? 200 : 503);
} else if (reason.includes('Dtmf')) {
const arr = /Signal=\s*([0-9#*])/.exec(req.body);
if (!arr) {
+29 -29
View File
@@ -11,12 +11,12 @@
"dependencies": {
"@aws-sdk/client-auto-scaling": "^3.360.0",
"@aws-sdk/client-sns": "^3.360.0",
"@jambonz/db-helpers": "^0.9.1",
"@jambonz/db-helpers": "^0.9.3",
"@jambonz/digest-utils": "^0.0.3",
"@jambonz/http-health-check": "^0.0.1",
"@jambonz/realtimedb-helpers": "^0.8.7",
"@jambonz/rtpengine-utils": "^0.4.3",
"@jambonz/siprec-client-utils": "^0.2.6",
"@jambonz/rtpengine-utils": "^0.4.4",
"@jambonz/siprec-client-utils": "^0.2.7",
"@jambonz/stats-collector": "^0.1.9",
"@jambonz/time-series": "^0.2.5",
"bent": "^7.3.12",
@@ -1797,9 +1797,9 @@
}
},
"node_modules/@jambonz/db-helpers": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.9.1.tgz",
"integrity": "sha512-asQQdeQEl1jCyQAxp2kMljZzExQbcG/mBxVYA2Jf0E1ReZctC206LWMWmY/rvbSHHXNZBzJlNxhD0dQB+FtJYA==",
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.9.3.tgz",
"integrity": "sha512-3XFs7NC7J7Q/eb1CwG1YJHa6N4elh8IP/4hMMDgoM9U5Loplx61XI4nZ58FIrY3C/F6gEF4UdjqKvbusEVw7cQ==",
"dependencies": {
"cidr-matcher": "^2.1.1",
"debug": "^4.3.4",
@@ -1840,19 +1840,19 @@
}
},
"node_modules/@jambonz/rtpengine-utils": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/@jambonz/rtpengine-utils/-/rtpengine-utils-0.4.3.tgz",
"integrity": "sha512-Aca2QHINtYfFTORag7I7WCw0IqTzx7ZNnboj99XshVF+kcP+AN7qbzUCMYMVC36QXCkHNP7ExhVaBWB2e3Cahg==",
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/@jambonz/rtpengine-utils/-/rtpengine-utils-0.4.4.tgz",
"integrity": "sha512-Kta5MlCPAnb4Z9yxzbPiRThI3yYbeBEslh00/g0rXSEK3voMNpQFPVMy9d83EAOoMEIMouas7/D2gVwV+2hfHA==",
"dependencies": {
"debug": "^4.3.1",
"rtpengine-client": "^0.4.11",
"rtpengine-client": "^0.4.12",
"ws": "^8.5.0"
}
},
"node_modules/@jambonz/siprec-client-utils": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/@jambonz/siprec-client-utils/-/siprec-client-utils-0.2.6.tgz",
"integrity": "sha512-z2x6nghLaCfOBPcr36f+1vxsV557X3z5JW1L1RxZg8YPWkTboNIHyHxtEcAua7V9g6EB4G1qv2KDwA+iE5n5Ww==",
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/@jambonz/siprec-client-utils/-/siprec-client-utils-0.2.7.tgz",
"integrity": "sha512-VztOToBfXnOphg/y6kO+lBYqoDH7X5Ci0daXT5zQ8v5NnaZw+UwxGeFPX3knIR3r5pmzUld0f2giFOuD4ZOF8w==",
"dependencies": {
"sdp-transform": "^2.14.1",
"uuid": "^8.3.2"
@@ -5128,9 +5128,9 @@
}
},
"node_modules/rtpengine-client": {
"version": "0.4.11",
"resolved": "https://registry.npmjs.org/rtpengine-client/-/rtpengine-client-0.4.11.tgz",
"integrity": "sha512-upWU+aDQ9SWzyL8gSoCWelInnkELR8c2Qlym9iCupwAmJb6SztZDP/wwZDWVKhusHI4o0ihx077AZrgv3fWwAg==",
"version": "0.4.12",
"resolved": "https://registry.npmjs.org/rtpengine-client/-/rtpengine-client-0.4.12.tgz",
"integrity": "sha512-Ax7Xf0ct4ZsjX+BzW/t3l19Lux4Ia6q+9BwWWcnelqi8IF0vts9OGcC3EV1/oBlVUCKnbjSYyG6kmUU5xC65vw==",
"dependencies": {
"bencode": "^2.0.3",
"uuid-random": "^1.3.2",
@@ -7498,9 +7498,9 @@
"dev": true
},
"@jambonz/db-helpers": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.9.1.tgz",
"integrity": "sha512-asQQdeQEl1jCyQAxp2kMljZzExQbcG/mBxVYA2Jf0E1ReZctC206LWMWmY/rvbSHHXNZBzJlNxhD0dQB+FtJYA==",
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.9.3.tgz",
"integrity": "sha512-3XFs7NC7J7Q/eb1CwG1YJHa6N4elh8IP/4hMMDgoM9U5Loplx61XI4nZ58FIrY3C/F6gEF4UdjqKvbusEVw7cQ==",
"requires": {
"cidr-matcher": "^2.1.1",
"debug": "^4.3.4",
@@ -7538,19 +7538,19 @@
}
},
"@jambonz/rtpengine-utils": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/@jambonz/rtpengine-utils/-/rtpengine-utils-0.4.3.tgz",
"integrity": "sha512-Aca2QHINtYfFTORag7I7WCw0IqTzx7ZNnboj99XshVF+kcP+AN7qbzUCMYMVC36QXCkHNP7ExhVaBWB2e3Cahg==",
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/@jambonz/rtpengine-utils/-/rtpengine-utils-0.4.4.tgz",
"integrity": "sha512-Kta5MlCPAnb4Z9yxzbPiRThI3yYbeBEslh00/g0rXSEK3voMNpQFPVMy9d83EAOoMEIMouas7/D2gVwV+2hfHA==",
"requires": {
"debug": "^4.3.1",
"rtpengine-client": "^0.4.11",
"rtpengine-client": "^0.4.12",
"ws": "^8.5.0"
}
},
"@jambonz/siprec-client-utils": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/@jambonz/siprec-client-utils/-/siprec-client-utils-0.2.6.tgz",
"integrity": "sha512-z2x6nghLaCfOBPcr36f+1vxsV557X3z5JW1L1RxZg8YPWkTboNIHyHxtEcAua7V9g6EB4G1qv2KDwA+iE5n5Ww==",
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/@jambonz/siprec-client-utils/-/siprec-client-utils-0.2.7.tgz",
"integrity": "sha512-VztOToBfXnOphg/y6kO+lBYqoDH7X5Ci0daXT5zQ8v5NnaZw+UwxGeFPX3knIR3r5pmzUld0f2giFOuD4ZOF8w==",
"requires": {
"sdp-transform": "^2.14.1",
"uuid": "^8.3.2"
@@ -10022,9 +10022,9 @@
}
},
"rtpengine-client": {
"version": "0.4.11",
"resolved": "https://registry.npmjs.org/rtpengine-client/-/rtpengine-client-0.4.11.tgz",
"integrity": "sha512-upWU+aDQ9SWzyL8gSoCWelInnkELR8c2Qlym9iCupwAmJb6SztZDP/wwZDWVKhusHI4o0ihx077AZrgv3fWwAg==",
"version": "0.4.12",
"resolved": "https://registry.npmjs.org/rtpengine-client/-/rtpengine-client-0.4.12.tgz",
"integrity": "sha512-Ax7Xf0ct4ZsjX+BzW/t3l19Lux4Ia6q+9BwWWcnelqi8IF0vts9OGcC3EV1/oBlVUCKnbjSYyG6kmUU5xC65vw==",
"requires": {
"bencode": "^2.0.3",
"uuid-random": "^1.3.2",
+3 -3
View File
@@ -25,11 +25,11 @@
"jslint": "eslint app.js lib"
},
"dependencies": {
"@jambonz/db-helpers": "^0.9.1",
"@jambonz/db-helpers": "^0.9.3",
"@jambonz/http-health-check": "^0.0.1",
"@jambonz/realtimedb-helpers": "^0.8.7",
"@jambonz/rtpengine-utils": "^0.4.3",
"@jambonz/siprec-client-utils": "^0.2.6",
"@jambonz/rtpengine-utils": "^0.4.4",
"@jambonz/siprec-client-utils": "^0.2.7",
"@jambonz/stats-collector": "^0.1.9",
"@jambonz/time-series": "^0.2.5",
"@jambonz/digest-utils": "^0.0.3",