mirror of
https://github.com/jambonz/sbc-outbound.git
synced 2026-10-04 02:04:19 +00:00
Compare commits
18
Commits
v0.7.2-rc1
...
v0.7.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2266b80e73 | ||
|
|
4c3d6ddf0c | ||
|
|
3d1bcb23f4 | ||
|
|
98ecfa20aa | ||
|
|
56efe50aec | ||
|
|
891d0ff38b | ||
|
|
78d6cb5f22 | ||
|
|
23255a71db | ||
|
|
4cb34ad784 | ||
|
|
f096a31a95 | ||
|
|
f671ef56bb | ||
|
|
3a2fcf5976 | ||
|
|
dd85ec762d | ||
|
|
61ec935ab8 | ||
|
|
cd7c425f7e | ||
|
|
e9b8cd595a | ||
|
|
fc7cdae22a | ||
|
|
2ca44c4f5a |
@@ -11,7 +11,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12
|
||||
node-version: 14.x
|
||||
- run: npm ci
|
||||
- run: npm run jslint
|
||||
- run: npm test
|
||||
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npm run jslint
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM node:current-slim
|
||||
FROM node:17.7.1-slim
|
||||
WORKDIR /opt/app/
|
||||
COPY package.json ./
|
||||
RUN npm install
|
||||
|
||||
@@ -11,6 +11,7 @@ assert.ok(process.env.JAMBONES_NETWORK_CIDR || process.env.K8S, 'missing JAMBONE
|
||||
const Srf = require('drachtio-srf');
|
||||
const srf = new Srf('sbc-outbound');
|
||||
const CIDRMatcher = require('cidr-matcher');
|
||||
const {pingMsTeamsGateways, equalsIgnoreOrder} = require('./lib/utils');
|
||||
const opts = Object.assign({
|
||||
timestamp: () => {return `, "time": "${new Date().toISOString()}"`;}
|
||||
}, {level: process.env.JAMBONES_LOGLEVEL || 'info'});
|
||||
@@ -90,7 +91,7 @@ const {initLocals, checkLimits, route} = require('./lib/middleware')(srf, logger
|
||||
const {getRtpEngine, setRtpEngines} = require('@jambonz/rtpengine-utils')([], logger, {
|
||||
emitter: stats,
|
||||
dtmfListenPort: process.env.DTMF_LISTEN_PORT || 22225,
|
||||
protocol: process.env.RTPENGINE_NG_PROTOCOL || (process.env.K8S ? 'ws' : 'udp')
|
||||
protocol: 'udp'
|
||||
});
|
||||
srf.locals.getRtpEngine = getRtpEngine;
|
||||
|
||||
@@ -136,40 +137,59 @@ srf.invite((req, res) => {
|
||||
session.connect();
|
||||
});
|
||||
|
||||
const PORT = process.env.HTTP_PORT || 3000;
|
||||
const getCount = () => activeCallIds.size;
|
||||
const healthCheck = require('@jambonz/http-health-check');
|
||||
healthCheck({port: PORT, logger, path: '/', fn: getCount});
|
||||
if (process.env.K8S) {
|
||||
const PORT = process.env.HTTP_PORT || 3000;
|
||||
const getCount = () => activeCallIds.size;
|
||||
const healthCheck = require('@jambonz/http-health-check');
|
||||
healthCheck({port: PORT, logger, path: '/', fn: getCount});
|
||||
}
|
||||
|
||||
/* update call stats periodically */
|
||||
setInterval(() => {
|
||||
stats.gauge('sbc.sip.calls.count', activeCallIds.size, ['direction:outbound']);
|
||||
}, 5000);
|
||||
if ('test' !== process.env.NODE_ENV) {
|
||||
/* update call stats periodically */
|
||||
setInterval(() => {
|
||||
stats.gauge('sbc.sip.calls.count', activeCallIds.size, ['direction:outbound']);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
const arrayCompare = (a, b) => {
|
||||
if (a.length !== b.length) return false;
|
||||
const uniqueValues = new Set([...a, ...b]);
|
||||
for (const v of uniqueValues) {
|
||||
const aCount = a.filter((e) => e === v).length;
|
||||
const bCount = b.filter((e) => e === v).length;
|
||||
if (aCount !== bCount) return false;
|
||||
}
|
||||
return true;
|
||||
const lookupRtpServiceEndpoints = (lookup, serviceName) => {
|
||||
logger.debug(`dns lookup for ${serviceName}..`);
|
||||
lookup(serviceName, {family: 4, all: true}, (err, addresses) => {
|
||||
if (err) {
|
||||
logger.error({err}, `Error looking up ${serviceName}`);
|
||||
return;
|
||||
}
|
||||
logger.debug({addresses, rtpServers}, `dns lookup for ${serviceName} returned`);
|
||||
const addrs = addresses.map((a) => a.address);
|
||||
if (!equalsIgnoreOrder(addrs, rtpServers)) {
|
||||
rtpServers.length = 0;
|
||||
Array.prototype.push.apply(rtpServers, addrs);
|
||||
logger.info({rtpServers}, 'rtpserver endpoints have been updated');
|
||||
setRtpEngines(rtpServers.map((a) => `${a}:${process.env.RTPENGINE_PORT || 22222}`));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const serviceName = process.env.JAMBONES_RTPENGINES || process.env.K8S_RTPENGINE_SERVICE_NAME;
|
||||
if (serviceName) {
|
||||
logger.info(`rtpengine(s) will be found at: ${serviceName}`);
|
||||
setRtpEngines([serviceName]);
|
||||
if (process.env.K8S_RTPENGINE_SERVICE_NAME) {
|
||||
/* poll dns for endpoints every so often */
|
||||
const arr = /^(.*):(\d+)$/.exec(process.env.K8S_RTPENGINE_SERVICE_NAME);
|
||||
const svc = arr[1];
|
||||
logger.info(`rtpengine(s) will be found at dns name: ${svc}`);
|
||||
const {lookup} = require('dns');
|
||||
lookupRtpServiceEndpoints(lookup, svc);
|
||||
setInterval(lookupRtpServiceEndpoints.bind(null, lookup, svc), process.env.RTPENGINE_DNS_POLL_INTERVAL || 10000);
|
||||
}
|
||||
else if (process.env.JAMBONES_RTPENGINES) {
|
||||
/* static list of rtpengines */
|
||||
setRtpEngines([process.env.JAMBONES_RTPENGINES]);
|
||||
}
|
||||
else {
|
||||
/* update rtpengines periodically */
|
||||
/* poll redis periodically for rtpengines that have registered via OPTIONS ping */
|
||||
const getActiveRtpServers = async() => {
|
||||
try {
|
||||
const set = await retrieveSet(setNameRtp);
|
||||
const newArray = Array.from(set);
|
||||
logger.debug({newArray, rtpServers}, 'getActiveRtpServers');
|
||||
if (!arrayCompare(newArray, rtpServers)) {
|
||||
if (!equalsIgnoreOrder(newArray, rtpServers)) {
|
||||
logger.info({newArray}, 'resetting active rtpengines');
|
||||
setRtpEngines(newArray.map((a) => `${a}:${process.env.RTPENGINE_PORT || 22222}`));
|
||||
rtpServers.length = 0;
|
||||
@@ -179,14 +199,12 @@ else {
|
||||
logger.error({err}, 'Error setting new rtpengines');
|
||||
}
|
||||
};
|
||||
|
||||
setInterval(() => {
|
||||
getActiveRtpServers();
|
||||
}, 30000);
|
||||
getActiveRtpServers();
|
||||
}
|
||||
|
||||
const {pingMsTeamsGateways} = require('./lib/utils');
|
||||
pingMsTeamsGateways(logger, srf);
|
||||
|
||||
module.exports = {srf};
|
||||
|
||||
+33
-6
@@ -124,14 +124,19 @@ class CallSession extends Emitter {
|
||||
};
|
||||
|
||||
if (this.req.locals.registration) {
|
||||
debug(`sending call to user ${JSON.stringify(this.req.locals.registration)}`);
|
||||
debug(`sending call to registered user ${JSON.stringify(this.req.locals.registration)}`);
|
||||
const contact = this.req.locals.registration.contact;
|
||||
if (contact.includes('transport=ws')) {
|
||||
uris = [contact];
|
||||
let destUri = contact;
|
||||
if (this.req.has('X-Override-To')) {
|
||||
const dest = this.req.get('X-Override-To');
|
||||
const uri = parseUri(contact);
|
||||
uri.user = dest;
|
||||
destUri = stringifyUri(uri);
|
||||
this.logger.info(`overriding destination user with ${dest}, so final uri is ${destUri}`);
|
||||
}
|
||||
else {
|
||||
uris = [destUri];
|
||||
if (!contact.includes('transport=ws')) {
|
||||
proxy = this.req.locals.registration.proxy;
|
||||
uris = [this.req.uri];
|
||||
}
|
||||
}
|
||||
else if (this.req.locals.target === 'forward') {
|
||||
@@ -372,7 +377,9 @@ class CallSession extends Emitter {
|
||||
debug(`got final outdial error: ${err}`);
|
||||
if (!passFailure) this.res.send(status);
|
||||
this.emit('failed');
|
||||
this.rtpEngineResource.destroy();
|
||||
this.rtpEngineResource.destroy()
|
||||
.catch((err) => this.logger.info({err}, 'Error destroying rtpe after failure'));
|
||||
this.srf.endSession(this.req);
|
||||
const tags = ['accepted:no', `sipStatus:${status}`];
|
||||
this.stats.increment('sbc.originations', tags);
|
||||
|
||||
@@ -449,6 +456,7 @@ class CallSession extends Emitter {
|
||||
uac.on('modify', this._onReinvite.bind(this, uac));
|
||||
|
||||
uas.on('refer', this._onFeatureServerTransfer.bind(this, uas));
|
||||
uac.on('refer', this._onRefer.bind(this, uac));
|
||||
|
||||
uas.on('info', this._onInfo.bind(this, uas));
|
||||
uac.on('info', this._onInfo.bind(this, uac));
|
||||
@@ -457,6 +465,23 @@ class CallSession extends Emitter {
|
||||
forwardInDialogRequests(uac, ['notify', 'options', 'message']);
|
||||
}
|
||||
|
||||
async _onRefer(dlg, req, res) {
|
||||
/* REFER coming in from a sip device, forward to feature server */
|
||||
try {
|
||||
const response = await dlg.other.request({
|
||||
method: 'REFER',
|
||||
headers: {
|
||||
'Refer-To': req.get('Refer-To'),
|
||||
'Referred-By': req.get('Referred-By'),
|
||||
'User-Agent': req.get('User-Agent')
|
||||
}
|
||||
});
|
||||
res.send(response.status, response.reason);
|
||||
} catch (err) {
|
||||
this.logger.error({err}, 'CallSession:_onRefer: error handling incoming REFER');
|
||||
}
|
||||
}
|
||||
|
||||
async _onDTMF(dlg, payload) {
|
||||
this.logger.info({payload}, '_onDTMF');
|
||||
try {
|
||||
@@ -518,6 +543,7 @@ Duration=${payload.duration} `
|
||||
}
|
||||
else {
|
||||
sdp = await dlg.other.modify(response.sdp);
|
||||
this.logger.info({sdp}, 'CallSession:_onReinvite: got sdp from 200 OK to invite we sent');
|
||||
}
|
||||
opts = {
|
||||
...this.rtpEngineOpts.common,
|
||||
@@ -531,6 +557,7 @@ Duration=${payload.duration} `
|
||||
res.send(488);
|
||||
throw new Error(`_onReinvite: rtpengine failed: ${JSON.stringify(response)}`);
|
||||
}
|
||||
this.logger.info({sdp: response.sdp}, 'CallSession:_onReinvite: sending back upstream');
|
||||
res.send(200, {body: response.sdp});
|
||||
} catch (err) {
|
||||
this.logger.error(err, 'Error handling reinvite');
|
||||
|
||||
+13
-1
@@ -73,9 +73,21 @@ const pingMsTeamsGateways = (logger, srf) => {
|
||||
|
||||
const makeCallCountKey = (sid) => `${sid}:outcalls`;
|
||||
|
||||
const equalsIgnoreOrder = (a, b) => {
|
||||
if (a.length !== b.length) return false;
|
||||
const uniqueValues = new Set([...a, ...b]);
|
||||
for (const v of uniqueValues) {
|
||||
const aCount = a.filter((e) => e === v).length;
|
||||
const bCount = b.filter((e) => e === v).length;
|
||||
if (aCount !== bCount) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
makeRtpEngineOpts,
|
||||
selectHostPort,
|
||||
pingMsTeamsGateways,
|
||||
makeCallCountKey
|
||||
makeCallCountKey,
|
||||
equalsIgnoreOrder
|
||||
};
|
||||
|
||||
Generated
+1943
-322
File diff suppressed because it is too large
Load Diff
+7
-6
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "sbc-outbound",
|
||||
"version": "v0.7.1",
|
||||
"version": "v0.7.4",
|
||||
"main": "app.js",
|
||||
"engines": {
|
||||
"node": ">= 8.10.0"
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"keywords": [
|
||||
"sip",
|
||||
@@ -27,17 +27,18 @@
|
||||
"jslint": "eslint app.js lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jambonz/db-helpers": "^0.6.16",
|
||||
"@jambonz/db-helpers": "^0.6.17",
|
||||
"@jambonz/http-health-check": "^0.0.1",
|
||||
"@jambonz/mw-registrar": "0.2.1",
|
||||
"@jambonz/realtimedb-helpers": "^0.4.9",
|
||||
"@jambonz/rtpengine-utils": "^0.2.2",
|
||||
"@jambonz/mw-registrar": "0.2.2",
|
||||
"@jambonz/realtimedb-helpers": "^0.4.24",
|
||||
"@jambonz/rtpengine-utils": "^0.3.1",
|
||||
"@jambonz/stats-collector": "^0.1.6",
|
||||
"@jambonz/time-series": "^0.1.6",
|
||||
"cidr-matcher": "^2.1.1",
|
||||
"debug": "^4.3.3",
|
||||
"drachtio-fn-b2b-sugar": "^0.0.12",
|
||||
"drachtio-srf": "^4.4.59",
|
||||
"husky": "^7.0.4",
|
||||
"pino": "^7.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
Reference in New Issue
Block a user