Compare commits

..
Author SHA1 Message Date
Hoan Luu Huu 591b8f2f8a respond to reinvite request incase error (#126) 2024-03-07 07:49:05 -05:00
Hoan Luu Huu 16ac56e50e start/stop/pause/resume recording success when one of siprec server success. (#125)
* start/stop/pause/resume recording success when one of siprec server success.

* wip

* allow send custom header on pause, resume recording

* allow timeout for siprec action

* wip

* wip

* update siprec-client-utils
2024-03-05 13:06:04 -05:00
Markus FrindtandMarkus Frindt 9412790366 add fix to lint script (#124)
Co-authored-by: Markus Frindt <m.frindt@cognigy.com>
2024-02-15 09:44:56 -05:00
3 changed files with 70 additions and 24 deletions
+61 -15
View File
@@ -103,6 +103,7 @@ class CallSession extends Emitter {
this.lookupCarrierByAccountLcr = lookupCarrierByAccountLcr;
this._mediaReleased = false;
this.recordingNoAnswerTimeout = (process.env.JAMBONES_RECORDING_NO_ANSWER_TIMEOUT || 2) * 1000;
}
get service_provider_sid() {
@@ -652,6 +653,22 @@ class CallSession extends Emitter {
forwardInDialogRequests(uac, ['notify', 'options', 'message']);
}
_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());
@@ -765,6 +782,7 @@ Duration=${payload.duration} `
this.logger.debug({opts, sdp: response.sdp}, 'CallSession:_onReinvite: (answer) sending back upstream');
res.send(200, {body: response.sdp});
} catch (err) {
res.send(err.status || 500);
this.logger.error(err, 'Error handling reinvite');
}
}
@@ -792,12 +810,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 an outbound call');
const srsUrl = req.get('X-Srs-Url');
@@ -840,11 +858,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') {
@@ -854,11 +875,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 = [];
}
@@ -868,9 +890,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)) {
@@ -878,11 +905,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) {
@@ -958,6 +993,16 @@ Duration=${payload.duration} `
async _onFeatureServerTransfer(dlg, req, res) {
try {
// the following properties are ignored in the REFER headers
// eslint-disable-next-line no-unused-vars
const { via, from, to, 'call-id': callid, cseq, 'max-forwards': maxforwards,
// eslint-disable-next-line no-unused-vars
'content-length': _contentlength, 'refer-to': _referto, 'referred-by': _referredby,
// eslint-disable-next-line no-unused-vars
'X-Refer-To-Leave-Untouched': _leave,
...customHeaders
} = req.headers;
const referTo = req.getParsedHeader('Refer-To');
const uri = parseUri(referTo.uri);
this.logger.info({uri, referTo}, 'received REFER from feature server');
@@ -975,7 +1020,8 @@ Duration=${payload.duration} `
method: 'REFER',
headers: {
'Refer-To': stringifyUri(uri),
'Referred-By': stringifyUri(u)
'Referred-By': stringifyUri(u),
...customHeaders
}
});
return res.send(response.status);
+7 -7
View File
@@ -14,7 +14,7 @@
"@jambonz/mw-registrar": "0.2.4",
"@jambonz/realtimedb-helpers": "^0.8.7",
"@jambonz/rtpengine-utils": "^0.4.3",
"@jambonz/siprec-client-utils": "^0.2.6",
"@jambonz/siprec-client-utils": "^0.2.7",
"@jambonz/stats-collector": "^0.1.9",
"@jambonz/time-series": "^0.2.5",
"cidr-matcher": "^2.1.1",
@@ -773,9 +773,9 @@
}
},
"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"
@@ -5553,9 +5553,9 @@
}
},
"@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"
+2 -2
View File
@@ -24,7 +24,7 @@
"start": "node app",
"test": "NODE_ENV=test HTTP_PORT=3050 JAMBONES_HOSTING=1 JAMBONES_NETWORK_CIDR=127.0.0.1/32 JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_TIME_SERIES_HOST=127.0.0.1 JAMBONES_LOGLEVEL=error DRACHTIO_SECRET=cymru DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 JAMBONES_RTPENGINES=127.0.0.1:12222 node test/ ",
"coverage": "./node_modules/.bin/nyc --reporter html --report-dir ./coverage npm run test",
"jslint": "eslint app.js lib"
"jslint": "eslint app.js lib --fix"
},
"dependencies": {
"@jambonz/db-helpers": "^0.9.3",
@@ -32,7 +32,7 @@
"@jambonz/http-health-check": "^0.0.1",
"@jambonz/mw-registrar": "0.2.4",
"@jambonz/rtpengine-utils": "^0.4.3",
"@jambonz/siprec-client-utils": "^0.2.6",
"@jambonz/siprec-client-utils": "^0.2.7",
"@jambonz/stats-collector": "^0.1.9",
"@jambonz/time-series": "^0.2.5",
"cidr-matcher": "^2.1.1",