mirror of
https://github.com/jambonz/sbc-inbound.git
synced 2026-10-04 02:04:22 +00:00
Compare commits
23
Commits
0.7.2
...
v0.7.6-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9401ab3c8 | ||
|
|
cc5c712a5b | ||
|
|
a923227e4a | ||
|
|
5f7df4d135 | ||
|
|
7dd4d4a045 | ||
|
|
7382bf6bd6 | ||
|
|
abfce38150 | ||
|
|
d8035c978e | ||
|
|
91cd677ad8 | ||
|
|
17b53438e2 | ||
|
|
f1e2f192b7 | ||
|
|
0af8bcc348 | ||
|
|
e067125974 | ||
|
|
2dadde64f4 | ||
|
|
d5a1337811 | ||
|
|
a6571134cd | ||
|
|
e0e5c75496 | ||
|
|
c08e35c261 | ||
|
|
883c63723c | ||
|
|
13f78bf8d9 | ||
|
|
55056c1771 | ||
|
|
87ec5f8e09 | ||
|
|
317280befc |
+1
-1
@@ -8,7 +8,7 @@
|
||||
"jsx": false,
|
||||
"modules": false
|
||||
},
|
||||
"ecmaVersion": 2018
|
||||
"ecmaVersion": 2020
|
||||
},
|
||||
"plugins": ["promise"],
|
||||
"rules": {
|
||||
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npm run jslint
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
FROM node:17.4-slim
|
||||
FROM node:slim
|
||||
WORKDIR /opt/app/
|
||||
COPY package.json ./
|
||||
RUN npm install
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci
|
||||
RUN npm prune
|
||||
COPY . /opt/app
|
||||
ARG NODE_ENV
|
||||
|
||||
@@ -14,6 +14,7 @@ const opts = Object.assign({
|
||||
}, {level: process.env.JAMBONES_LOGLEVEL || 'info'});
|
||||
const logger = require('pino')(opts);
|
||||
const {
|
||||
writeCallCount,
|
||||
queryCdrs,
|
||||
writeCdrs,
|
||||
writeAlerts,
|
||||
@@ -26,7 +27,7 @@ const {
|
||||
});
|
||||
const StatsCollector = require('@jambonz/stats-collector');
|
||||
const stats = new StatsCollector(logger);
|
||||
const {equalsIgnoreOrder} = require('./lib/utils');
|
||||
const {equalsIgnoreOrder, createHealthCheckApp, systemHealth} = require('./lib/utils');
|
||||
const {LifeCycleEvents} = require('./lib/constants');
|
||||
const setNameRtp = `${(process.env.JAMBONES_CLUSTER_ID || 'default')}:active-rtp`;
|
||||
const rtpServers = [];
|
||||
@@ -34,6 +35,7 @@ const setName = `${(process.env.JAMBONES_CLUSTER_ID || 'default')}:active-sip`;
|
||||
|
||||
const {
|
||||
pool,
|
||||
ping,
|
||||
lookupAuthHook,
|
||||
lookupSipGatewayBySignalingAddress,
|
||||
addSbcAddress,
|
||||
@@ -49,7 +51,14 @@ const {
|
||||
database: process.env.JAMBONES_MYSQL_DATABASE,
|
||||
connectionLimit: process.env.JAMBONES_MYSQL_CONNECTION_LIMIT || 10
|
||||
}, logger);
|
||||
const {createSet, retrieveSet, addToSet, removeFromSet, incrKey, decrKey} = require('@jambonz/realtimedb-helpers')({
|
||||
const {
|
||||
client: redisClient,
|
||||
createSet,
|
||||
retrieveSet,
|
||||
addToSet,
|
||||
removeFromSet,
|
||||
incrKey,
|
||||
decrKey} = require('@jambonz/realtimedb-helpers')({
|
||||
host: process.env.JAMBONES_REDIS_HOST || 'localhost',
|
||||
port: process.env.JAMBONES_REDIS_PORT || 6379
|
||||
}, logger);
|
||||
@@ -61,6 +70,7 @@ const {getRtpEngine, setRtpEngines} = require('@jambonz/rtpengine-utils')([], lo
|
||||
});
|
||||
srf.locals = {...srf.locals,
|
||||
stats,
|
||||
writeCallCount,
|
||||
queryCdrs,
|
||||
writeCdrs,
|
||||
writeAlerts,
|
||||
@@ -69,6 +79,7 @@ srf.locals = {...srf.locals,
|
||||
getRtpEngine,
|
||||
dbHelpers: {
|
||||
pool,
|
||||
ping,
|
||||
lookupAuthHook,
|
||||
lookupSipGatewayBySignalingAddress,
|
||||
lookupAccountByPhoneNumber,
|
||||
@@ -138,7 +149,9 @@ if (process.env.DRACHTIO_HOST && !process.env.K8S) {
|
||||
});
|
||||
}
|
||||
else {
|
||||
logger.info(`listening in outbound mode on port ${process.env.DRACHTIO_PORT}`);
|
||||
srf.on('listening', () => {
|
||||
logger.info(`listening in outbound mode on port ${process.env.DRACHTIO_PORT}`);
|
||||
});
|
||||
srf.listen({port: process.env.DRACHTIO_PORT, secret: process.env.DRACHTIO_SECRET});
|
||||
}
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
@@ -171,11 +184,31 @@ srf.use((req, res, next, err) => {
|
||||
res.send(500);
|
||||
});
|
||||
|
||||
if (process.env.K8S) {
|
||||
if (process.env.K8S || process.env.HTTP_PORT) {
|
||||
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});
|
||||
|
||||
const getCount = () => srf.locals.activeCallIds.size;
|
||||
|
||||
createHealthCheckApp(PORT, logger)
|
||||
.then((app) => {
|
||||
healthCheck({
|
||||
app,
|
||||
logger,
|
||||
path: '/',
|
||||
fn: getCount
|
||||
});
|
||||
healthCheck({
|
||||
app,
|
||||
logger,
|
||||
path: '/system-health',
|
||||
fn: systemHealth.bind(null, redisClient, ping, getCount)
|
||||
});
|
||||
return;
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error({err}, 'Error creating health check server');
|
||||
});
|
||||
}
|
||||
if ('test' !== process.env.NODE_ENV) {
|
||||
/* update call stats periodically */
|
||||
|
||||
+38
-12
@@ -171,7 +171,7 @@ class CallSession extends Emitter {
|
||||
'-Session-Expires',
|
||||
'-X-Subspace-Forwarded-For'
|
||||
],
|
||||
proxyResponseHeaders: ['all'],
|
||||
proxyResponseHeaders: ['all', '-X-Trace-ID'],
|
||||
localSdpB: response.sdp,
|
||||
localSdpA: async(sdp, res) => {
|
||||
this.rtpEngineOpts.uac.tag = res.getParsedHeader('To').params.tag;
|
||||
@@ -204,7 +204,7 @@ class CallSession extends Emitter {
|
||||
const tags = ['accepted:no', `sipStatus:${err.status}`, `originator:${this.req.locals.originator}`];
|
||||
this.stats.increment('sbc.terminations', tags);
|
||||
this.logger.info(`call failed to connect to feature server with ${err.status}`);
|
||||
return this.emit('failed');
|
||||
this.emit('failed');
|
||||
}
|
||||
else if (err.message !== 'call canceled') {
|
||||
this.logger.error(err, 'unexpected error routing inbound call');
|
||||
@@ -241,22 +241,30 @@ class CallSession extends Emitter {
|
||||
this.req.locals.cdr = {
|
||||
...this.req.locals.cdr,
|
||||
answered: true,
|
||||
answered_at: callStart
|
||||
answered_at: callStart,
|
||||
trace_id: uac.res?.get('X-Trace-ID') || '00000000000000000000000000000000'
|
||||
};
|
||||
}
|
||||
this.uas = uas;
|
||||
this.uac = uac;
|
||||
[uas, uac].forEach((dlg) => {
|
||||
dlg.on('destroy', () => {
|
||||
this.logger.info('call ended with normal termination');
|
||||
dlg.on('destroy', async() => {
|
||||
const other = dlg.other;
|
||||
this.rtpEngineResource.destroy().catch((err) => {});
|
||||
this.activeCallIds.delete(this.req.get('Call-ID'));
|
||||
dlg.other.destroy().catch((e) => {});
|
||||
try {
|
||||
await other.destroy();
|
||||
} catch (err) {}
|
||||
this.unsubscribeDTMF(this.logger, this.req.get('Call-ID'), this.rtpEngineOpts.uas.tag);
|
||||
if (process.env.JAMBONES_HOSTING) {
|
||||
if (process.env.JAMBONES_HOSTING || process.env.JAMBONES_TRACK_ACCOUNT_CALLS) {
|
||||
const {account_sid} = this.req.locals;
|
||||
this.decrKey(this.callCountKey)
|
||||
.then((count) => this.logger.debug({key: this.callCountKey},
|
||||
`after hangup there are ${count} active calls for this account`))
|
||||
.then((count) => {
|
||||
this.logger.info(
|
||||
{key: this.callCountKey},
|
||||
`after hangup there are ${count} active calls for this account`);
|
||||
return this.req.srf.locals.writeCallCount({account_sid, calls_in_progress: count});
|
||||
})
|
||||
.catch((err) => this.logger.error({err}, 'Error decrementing call count'));
|
||||
}
|
||||
|
||||
@@ -274,6 +282,13 @@ class CallSession extends Emitter {
|
||||
trunk
|
||||
}).catch((err) => this.logger.error({err}, 'Error writing cdr for completed call'));
|
||||
}
|
||||
/* de-link the 2 Dialogs for GC */
|
||||
dlg.removeAllListeners();
|
||||
other.removeAllListeners();
|
||||
dlg.other = null;
|
||||
other.other = null;
|
||||
|
||||
this.logger.info(`call ended with normal termination, there are ${this.activeCallIds.size} active`);
|
||||
this.srf.endSession(this.req);
|
||||
});
|
||||
});
|
||||
@@ -398,7 +413,6 @@ Duration=${payload.duration} `
|
||||
direction,
|
||||
sdp: req.body,
|
||||
};
|
||||
//if (reason && opts.flags && !opts.flags.includes('reset')) opts.flags.push('reset');
|
||||
|
||||
let response = await this.offer(opts);
|
||||
if ('ok' !== response.result) {
|
||||
@@ -618,8 +632,20 @@ Duration=${payload.duration} `
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO: forward on to feature server
|
||||
res.send(501);
|
||||
/* 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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-2
@@ -44,6 +44,10 @@ SELECT * FROM phone_numbers
|
||||
WHERE number = ?
|
||||
AND voip_carrier_sid = ?`;
|
||||
|
||||
const sqlQueryAllDidsForCarrier = `
|
||||
SELECT * FROM phone_numbers
|
||||
WHERE voip_carrier_sid = ?`;
|
||||
|
||||
const sqlSelectOutboundGatewayForCarrier = `
|
||||
SELECT ipv4, port, e164_leading_plus
|
||||
FROM sip_gateways sg, voip_carriers vc
|
||||
@@ -81,9 +85,18 @@ module.exports = (srf, logger) => {
|
||||
const did = normalizeDID(req.calledNumber);
|
||||
|
||||
try {
|
||||
/* straight DID match */
|
||||
const [r] = await pp.query(sqlQueryApplicationByDid, [did, voip_carrier_sid]);
|
||||
if (0 === r.length) return null;
|
||||
return r[0].application_sid;
|
||||
if (r.length) return r[0].application_sid;
|
||||
|
||||
/* wildcard / regex match */
|
||||
const [r2] = await pp.query(sqlQueryAllDidsForCarrier, [voip_carrier_sid]);
|
||||
const match = r2
|
||||
.filter((o) => o.number.match(/\D/)) // look at anything with non-digit characters
|
||||
.sort((a, b) => b.number.length - a.number.length) // prefer longest match
|
||||
.find((o) => did.match(new RegExp(o.number.endsWith('*') ? `${o.number.slice(0, -1)}\\d*` : o.number)));
|
||||
if (match) return match.application_sid;
|
||||
return null;
|
||||
} catch (err) {
|
||||
logger.error({err}, 'getApplicationForDidAndCarrier');
|
||||
}
|
||||
|
||||
+9
-4
@@ -220,11 +220,11 @@ module.exports = function(srf, logger) {
|
||||
};
|
||||
|
||||
const checkLimits = async(req, res, next) => {
|
||||
if (!process.env.JAMBONES_HOSTING) return next(); // skip
|
||||
if (!process.env.JAMBONES_HOSTING && !process.env.JAMBONES_TRACK_ACCOUNT_CALLS) return next(); // skip
|
||||
|
||||
const {incrKey, decrKey} = req.srf.locals.realtimeDbHelpers;
|
||||
const {logger, account_sid} = req.locals;
|
||||
const {writeAlerts, AlertType} = req.srf.locals;
|
||||
const {writeCallCount, writeAlerts, AlertType} = req.srf.locals;
|
||||
assert(account_sid);
|
||||
const key = makeCallCountKey(account_sid);
|
||||
|
||||
@@ -233,10 +233,11 @@ module.exports = function(srf, logger) {
|
||||
if (status > 200) {
|
||||
decrKey(key)
|
||||
.then((count) => {
|
||||
logger.debug({key}, `after rejection there are ${count} active calls for this account`);
|
||||
logger.info({key}, `after rejection there are ${count} active calls for this account`);
|
||||
debug({key}, `after rejection there are ${count} active calls for this account`);
|
||||
return;
|
||||
return count;
|
||||
})
|
||||
.then((count) => writeCallCount({account_sid, calls_in_progress: count}))
|
||||
.catch((err) => logger.error({err}, 'checkLimits: decrKey err'));
|
||||
}
|
||||
});
|
||||
@@ -244,6 +245,10 @@ module.exports = function(srf, logger) {
|
||||
try {
|
||||
/* increment the call count */
|
||||
const calls = await incrKey(key);
|
||||
writeCallCount({account_sid, calls_in_progress: calls})
|
||||
.then(() => logger.info(`checkLimits: after incrementing there are ${calls} active calls for this account`))
|
||||
.catch((err) => logger.error({err}, 'checkLimits: error writing call count'));
|
||||
if (!process.env.JAMBONES_HOSTING) return next();
|
||||
|
||||
/* compare to account's limit, though avoid db hit when call count is low */
|
||||
const minLimit = process.env.MIN_CALL_LIMIT ?
|
||||
|
||||
+23
-1
@@ -56,6 +56,26 @@ const equalsIgnoreOrder = (a, b) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
const systemHealth = async(redisClient, ping, getCount) => {
|
||||
await Promise.all([redisClient.ping(), ping()]);
|
||||
return getCount();
|
||||
};
|
||||
|
||||
const createHealthCheckApp = (port, logger) => {
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use(express.json());
|
||||
|
||||
return new Promise((resolve) => {
|
||||
app.listen(port, () => {
|
||||
logger.info(`Health check server started at http://localhost:${port}`);
|
||||
resolve(app);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
isWSS,
|
||||
SdpWantsSrtp,
|
||||
@@ -63,5 +83,7 @@ module.exports = {
|
||||
makeRtpEngineOpts,
|
||||
makeCallCountKey,
|
||||
normalizeDID,
|
||||
equalsIgnoreOrder
|
||||
equalsIgnoreOrder,
|
||||
systemHealth,
|
||||
createHealthCheckApp
|
||||
};
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
|
||||
TCP_SERVER_PORT="${DRACHTIO_PORT:-4000}"
|
||||
nc -v -z localhost $TCP_SERVER_PORT
|
||||
|
||||
# if last command exited with non zero
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
HTTP_SERVER_PORT="${HTTP_PORT:-3000}"
|
||||
|
||||
printf 'GET /system-health HTTP/1.1\r\nHost: localhost\r\n\r\n' | nc -v localhost 3000 | grep calls
|
||||
|
||||
# grep will automatically exit with 1 if string is not matched, however, will leave that call there in case
|
||||
# we pivot to pipe to dev/null
|
||||
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Generated
+776
-5693
File diff suppressed because it is too large
Load Diff
+13
-15
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sbc-inbound",
|
||||
"version": "v0.7.2",
|
||||
"version": "v0.7.5",
|
||||
"main": "app.js",
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
@@ -20,35 +20,33 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node app",
|
||||
"test": "NODE_ENV=test JAMBONES_NETWORK_CIDR='127.0.0.1/32' JAMBONES_HOSTING=1 SBC_ACCOUNT_SID=ed649e33-e771-403a-8c99-1780eabbc803 JAMBONES_TIME_SERIES_HOST=127.0.0.1 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_LOGLEVEL=error DRACHTIO_SECRET=cymru DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 JAMBONES_RTPENGINES=127.0.0.1:12222 JAMBONES_FEATURE_SERVERS=172.38.0.11 node test/ ",
|
||||
"test": "NODE_ENV=test HTTP_PORT=3050 JAMBONES_NETWORK_CIDR='127.0.0.1/32' JAMBONES_HOSTING=1 SBC_ACCOUNT_SID=ed649e33-e771-403a-8c99-1780eabbc803 JAMBONES_TIME_SERIES_HOST=127.0.0.1 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_LOGLEVEL=info DRACHTIO_SECRET=cymru DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 JAMBONES_RTPENGINES=127.0.0.1:12222 JAMBONES_FEATURE_SERVERS=172.38.0.11 node test/ ",
|
||||
"coverage": "./node_modules/.bin/nyc --reporter html --report-dir ./coverage npm run test",
|
||||
"jslint": "eslint app.js lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jambonz/db-helpers": "^0.6.16",
|
||||
"@jambonz/http-authenticator": "^0.2.0",
|
||||
"@jambonz/db-helpers": "^0.6.18",
|
||||
"@jambonz/http-authenticator": "^0.2.1",
|
||||
"@jambonz/http-health-check": "^0.0.1",
|
||||
"@jambonz/realtimedb-helpers": "^0.4.9",
|
||||
"@jambonz/rtpengine-utils": "^0.3.0-beta.3",
|
||||
"@jambonz/realtimedb-helpers": "^0.4.29",
|
||||
"@jambonz/rtpengine-utils": "^0.3.1",
|
||||
"@jambonz/stats-collector": "^0.1.6",
|
||||
"@jambonz/time-series": "^0.1.6",
|
||||
"aws-sdk": "^2.1036.0",
|
||||
"@jambonz/time-series": "^0.1.9",
|
||||
"aws-sdk": "^2.1152.0",
|
||||
"bent": "^7.3.12",
|
||||
"cidr-matcher": "^2.1.1",
|
||||
"debug": "^4.3.3",
|
||||
"debug": "^4.3.4",
|
||||
"drachtio-fn-b2b-sugar": "0.0.12",
|
||||
"drachtio-srf": "^4.4.59",
|
||||
"express": "^4.17.1",
|
||||
"pino": "^7.4.1",
|
||||
"rtpengine-client": "^0.2.0",
|
||||
"drachtio-srf": "^4.5.0",
|
||||
"express": "^4.18.1",
|
||||
"pino": "^7.11.0",
|
||||
"verify-aws-sns-signature": "^0.0.6",
|
||||
"xml2js": "^0.4.23"
|
||||
},
|
||||
"devDependencies": {
|
||||
"clear-module": "^4.1.1",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-promise": "^4.3.1",
|
||||
"nyc": "^15.1.0",
|
||||
"tape": "^4.13.3"
|
||||
"tape": "^4.15.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,5 +56,12 @@ values ('888a5339-c62c-4075-9e19-f4de70a96597', '999c1452-620d-4195-9f19-c9814ef
|
||||
insert into phone_numbers (phone_number_sid, number, voip_carrier_sid, account_sid)
|
||||
values ('999a5339-c62c-4075-9e19-f4de70a96597', '16173333456', '287c1452-620d-4195-9f19-c9814ef90d78', 'ed649e33-e771-403a-8c99-1780eabbc803');
|
||||
|
||||
insert into phone_numbers (phone_number_sid, number, voip_carrier_sid, account_sid)
|
||||
values ('29543d4e-d959-4a25-836a-cde7161cd7d5', '1508222*', '287c1452-620d-4195-9f19-c9814ef90d78', 'ed649e33-e771-403a-8c99-1780eabbc803');
|
||||
insert into phone_numbers (phone_number_sid, number, voip_carrier_sid, account_sid)
|
||||
values ('dddd5c34-feae-4d70-98af-bb4d1f8dc965', '1508*', '287c1452-620d-4195-9f19-c9814ef90d78', 'ed649e33-e771-403a-8c99-1780eabbc803');
|
||||
insert into phone_numbers (phone_number_sid, number, voip_carrier_sid, account_sid)
|
||||
values ('d458bf7a-bcea-47b2-ac96-66dfc9c5c220', '150822233*', '287c1452-620d-4195-9f19-c9814ef90d78', 'ed649e33-e771-403a-8c99-1780eabbc803');
|
||||
|
||||
insert into phone_numbers (phone_number_sid, number, voip_carrier_sid, account_sid)
|
||||
values ('f7ad205d-b92f-4363-8160-f8b5216b40d3', '15083871234', '287c1452-620d-4195-9f19-c9814ef90d78', 'd7cc37cb-d152-49ef-a51b-485f6e917089');
|
||||
@@ -0,0 +1,118 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
<!-- This program is free software; you can redistribute it and/or -->
|
||||
<!-- modify it under the terms of the GNU General Public License as -->
|
||||
<!-- published by the Free Software Foundation; either version 2 of the -->
|
||||
<!-- License, or (at your option) any later version. -->
|
||||
<!-- -->
|
||||
<!-- This program is distributed in the hope that it will be useful, -->
|
||||
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
|
||||
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
|
||||
<!-- GNU General Public License for more details. -->
|
||||
<!-- -->
|
||||
<!-- You should have received a copy of the GNU General Public License -->
|
||||
<!-- along with this program; if not, write to the -->
|
||||
<!-- Free Software Foundation, Inc., -->
|
||||
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
|
||||
<!-- -->
|
||||
<!-- Sipp 'uac' scenario with pcap (rtp) play -->
|
||||
<!-- -->
|
||||
|
||||
<scenario name="UAC with media">
|
||||
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
|
||||
<!-- generated by sipp. To do so, use [call_id] keyword. -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
INVITE sip:+15082223333@jambonz.org SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
|
||||
To: <sip:15082223333@jambonz.org>
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 INVITE
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Subject: uac-pcap-carrier-success
|
||||
Content-Type: application/sdp
|
||||
Content-Length: [len]
|
||||
|
||||
v=0
|
||||
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||
s=-
|
||||
c=IN IP[local_ip_type] [local_ip]
|
||||
t=0 0
|
||||
m=audio [auto_media_port] RTP/AVP 8 101
|
||||
a=rtpmap:8 PCMA/8000
|
||||
a=rtpmap:101 telephone-event/8000
|
||||
a=fmtp:101 0-11,16
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="100" optional="true">
|
||||
</recv>
|
||||
|
||||
<recv response="180" optional="true">
|
||||
</recv>
|
||||
|
||||
<!-- By adding rrs="true" (Record Route Sets), the route sets -->
|
||||
<!-- are saved and used for following messages sent. Useful to test -->
|
||||
<!-- against stateful SIP proxies/B2BUAs. -->
|
||||
<recv response="200" rtd="true" crlf="true">
|
||||
</recv>
|
||||
|
||||
<!-- Packet lost can be simulated in any send/recv message by -->
|
||||
<!-- by adding the 'lost = "10"'. Value can be [1-100] percent. -->
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
ACK sip:15082223333@jambonz.org SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
|
||||
To: <sip:15082223333@jambonz.org>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 1 ACK
|
||||
Max-Forwards: 70
|
||||
Subject: uac-pcap-carrier-success
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<!-- Play a pre-recorded PCAP file (RTP stream) -->
|
||||
<nop>
|
||||
<action>
|
||||
<exec play_pcap_audio="pcap/g711a.pcap"/>
|
||||
</action>
|
||||
</nop>
|
||||
|
||||
<!-- Pause briefly -->
|
||||
<pause milliseconds="3000"/>
|
||||
|
||||
<!-- The 'crlf' option inserts a blank line in the statistics report. -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
|
||||
BYE sip:15082223333@jambonz.org SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
|
||||
To: <sip:15082223333@jambonz.org>[peer_tag_param]
|
||||
Call-ID: [call_id]
|
||||
CSeq: 2 BYE
|
||||
Subject: uac-pcap-carrier-success
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="200" crlf="true">
|
||||
</recv>
|
||||
|
||||
<!-- definition of the response time repartition table (unit is ms) -->
|
||||
<ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
|
||||
|
||||
<!-- definition of the call length repartition table (unit is ms) -->
|
||||
<CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
|
||||
|
||||
</scenario>
|
||||
+18
-1
@@ -124,7 +124,24 @@
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv request="BYE">
|
||||
</recv>
|
||||
|
||||
<send next="2">
|
||||
<![CDATA[
|
||||
|
||||
SIP/2.0 200 OK
|
||||
[last_Via:]
|
||||
[last_From:]
|
||||
[last_To:]
|
||||
[last_Call-ID:]
|
||||
[last_CSeq:]
|
||||
Contact: <sip:[local_ip]:[local_port];transport=[transport]>
|
||||
Content-Length: 0
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<label id="2"/>
|
||||
|
||||
</scenario>
|
||||
|
||||
|
||||
+13
-5
@@ -1,8 +1,7 @@
|
||||
const test = require('tape');
|
||||
const { output, sippUac } = require('./sipp')('test_sbc-inbound');
|
||||
const debug = require('debug')('drachtio:sbc-inbound');
|
||||
const clearModule = require('clear-module');
|
||||
const consoleLogger = {error: console.error, info: console.log, debug: console.log};
|
||||
const { sippUac } = require('./sipp')('test_sbc-inbound');
|
||||
const bent = require('bent');
|
||||
const getJSON = bent('json');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
@@ -28,12 +27,21 @@ test('incoming call tests', async(t) => {
|
||||
|
||||
try {
|
||||
await connect(srf);
|
||||
|
||||
let obj = await getJSON('http://127.0.0.1:3050/');
|
||||
t.ok(obj.calls === 0, 'HTTP GET / works (current call count)')
|
||||
obj = await getJSON('http://127.0.0.1:3050/system-health');
|
||||
t.ok(obj.calls === 0, 'HTTP GET /system-health works (health check)')
|
||||
|
||||
await sippUac('uac-pcap-carrier-success.xml', '172.38.0.20');
|
||||
t.pass('incoming call from carrier completed successfully');
|
||||
|
||||
await sippUac('uac-pcap-pbx-success.xml', '172.38.0.21');
|
||||
t.pass('incoming call from account-level carrier completed successfully');
|
||||
|
||||
await sippUac('uac-did-regex-match.xml', '172.38.0.20');
|
||||
t.pass('incoming call matched by trailing wildcard *');
|
||||
|
||||
await sippUac('uac-pcap-device-success.xml', '172.38.0.30');
|
||||
t.pass('incoming call from authenticated device completed successfully');
|
||||
|
||||
@@ -55,7 +63,7 @@ test('incoming call tests', async(t) => {
|
||||
await waitFor(10);
|
||||
const res = await queryCdrs({account_sid: 'ed649e33-e771-403a-8c99-1780eabbc803'});
|
||||
console.log(`cdrs: ${JSON.stringify(res)}`);
|
||||
t.ok(6 === res.total, 'successfully wrote 6 cdrs for calls');
|
||||
t.ok(7 === res.total, 'successfully wrote 7 cdrs for calls');
|
||||
|
||||
srf.disconnect();
|
||||
t.end();
|
||||
|
||||
Reference in New Issue
Block a user