mirror of
https://github.com/jambonz/sbc-inbound.git
synced 2026-10-04 18:44:43 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24c2225758 |
+123
-114
@@ -148,128 +148,137 @@ class CallSession extends Emitter {
|
|||||||
else uri = `${obj.scheme}:${host}`;
|
else uri = `${obj.scheme}:${host}`;
|
||||||
this.logger.info(`uri will be: ${uri}, proxy ${proxy}`);
|
this.logger.info(`uri will be: ${uri}, proxy ${proxy}`);
|
||||||
|
|
||||||
try {
|
const opts = {
|
||||||
const opts = {
|
...this.rtpEngineOpts.common,
|
||||||
...this.rtpEngineOpts.common,
|
...this.rtpEngineOpts.uac.mediaOpts,
|
||||||
...this.rtpEngineOpts.uac.mediaOpts,
|
'from-tag': this.rtpEngineOpts.uas.tag,
|
||||||
'from-tag': this.rtpEngineOpts.uas.tag,
|
direction: ['public', 'private'],
|
||||||
direction: ['public', 'private'],
|
sdp
|
||||||
sdp
|
};
|
||||||
};
|
const startAt = process.hrtime();
|
||||||
const startAt = process.hrtime();
|
this.offer(opts, async(err, response) => {
|
||||||
const response = await this.offer(opts);
|
try {
|
||||||
this.rtpengineIp = opts.sdp ? parseConnectionIp(opts.sdp) : 'undefined';
|
if (err || 'ok' !== response.result) {
|
||||||
const rtt = roundTripTime(startAt);
|
this.logger.error({}, `rtpengine offer failed with ${JSON.stringify(response)}`);
|
||||||
this.stats.histogram('app.rtpengine.response_time', rtt, [
|
return this.res.send(480, {
|
||||||
'direction:inbound', 'command:offer', `rtpengine:${this.rtpengineIp}`]);
|
headers: {
|
||||||
this.logger.debug({opts, response, rtt, rtpengine: this.rtpengineIp}, 'response from rtpengine to offer');
|
'X-Reason': 'RTPENGINE_OFFER_FAILED'
|
||||||
if ('ok' !== response.result) {
|
}
|
||||||
this.logger.error({}, `rtpengine offer failed with ${JSON.stringify(response)}`);
|
});
|
||||||
throw new Error('rtpengine failed: answer');
|
|
||||||
}
|
|
||||||
|
|
||||||
let headers = {
|
|
||||||
'From': createBLegFromHeader(this.req),
|
|
||||||
'To': this.req.get('To'),
|
|
||||||
'X-Account-Sid': this.req.locals.account_sid,
|
|
||||||
'X-CID': this.req.get('Call-ID'),
|
|
||||||
'X-Forwarded-For': `${this.req.source_address}`
|
|
||||||
};
|
|
||||||
if (this.privateSipAddress) headers = {...headers, Contact: `<sip:${this.privateSipAddress}>`};
|
|
||||||
|
|
||||||
const spdOfferB = this.siprec && this.xml ?
|
|
||||||
createSiprecBody(headers, response.sdp, this.xml.type, this.xml.content) :
|
|
||||||
response.sdp;
|
|
||||||
|
|
||||||
const responseHeaders = {};
|
|
||||||
if (this.req.locals.carrier) {
|
|
||||||
Object.assign(headers, {
|
|
||||||
'X-Originating-Carrier': this.req.locals.carrier,
|
|
||||||
'X-Voip-Carrier-Sid': this.req.locals.voip_carrier_sid
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (this.req.locals.msTeamsTenantFqdn) {
|
|
||||||
Object.assign(headers, {'X-MS-Teams-Tenant-FQDN': this.req.locals.msTeamsTenantFqdn});
|
|
||||||
|
|
||||||
// for Microsoft Teams the Contact header must include the tenant FQDN
|
|
||||||
Object.assign(responseHeaders, {
|
|
||||||
Allow: 'INVITE, ACK, OPTIONS, CANCEL, BYE, NOTIFY, UPDATE, PRACK',
|
|
||||||
Contact: `sip:${this.req.locals.msTeamsTenantFqdn}`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (this.req.locals.application_sid) {
|
|
||||||
Object.assign(headers, {'X-Application-Sid': this.req.locals.application_sid});
|
|
||||||
}
|
|
||||||
else if (this.req.authorization) {
|
|
||||||
if (this.req.authorization.grant && this.req.authorization.grant.application_sid) {
|
|
||||||
Object.assign(headers, {'X-Application-Sid': this.req.authorization.grant.application_sid});
|
|
||||||
}
|
}
|
||||||
if (this.req.authorization.challengeResponse) {
|
this.rtpengineIp = opts.sdp ? parseConnectionIp(opts.sdp) : 'undefined';
|
||||||
const {username, realm} = this.req.authorization.challengeResponse;
|
const rtt = roundTripTime(startAt);
|
||||||
Object.assign(headers, {'X-Authenticated-User': `${username}@${realm}`});
|
this.stats.histogram('app.rtpengine.response_time', rtt, [
|
||||||
|
'direction:inbound', 'command:offer', `rtpengine:${this.rtpengineIp}`]);
|
||||||
|
this.logger.debug({opts, response, rtt, rtpengine: this.rtpengineIp}, 'response from rtpengine to offer');
|
||||||
|
if ('ok' !== response.result) {
|
||||||
}
|
}
|
||||||
}
|
let headers = {
|
||||||
|
'From': createBLegFromHeader(this.req),
|
||||||
|
'To': this.req.get('To'),
|
||||||
|
'X-Account-Sid': this.req.locals.account_sid,
|
||||||
|
'X-CID': this.req.get('Call-ID'),
|
||||||
|
'X-Forwarded-For': `${this.req.source_address}`
|
||||||
|
};
|
||||||
|
if (this.privateSipAddress) headers = {...headers, Contact: `<sip:${this.privateSipAddress}>`};
|
||||||
|
|
||||||
if (this.req.canceled) throw new Error('call canceled');
|
const spdOfferB = this.siprec && this.xml ?
|
||||||
|
createSiprecBody(headers, response.sdp, this.xml.type, this.xml.content) :
|
||||||
|
response.sdp;
|
||||||
|
|
||||||
// now send the INVITE in towards the feature servers
|
const responseHeaders = {};
|
||||||
debug(`sending INVITE to ${proxy} with ${uri}`);
|
if (this.req.locals.carrier) {
|
||||||
const {uas, uac} = await this.srf.createB2BUA(this.req, this.res, uri, {
|
Object.assign(headers, {
|
||||||
proxy,
|
'X-Originating-Carrier': this.req.locals.carrier,
|
||||||
headers,
|
'X-Voip-Carrier-Sid': this.req.locals.voip_carrier_sid
|
||||||
responseHeaders,
|
});
|
||||||
proxyRequestHeaders: [
|
}
|
||||||
'all',
|
if (this.req.locals.msTeamsTenantFqdn) {
|
||||||
'-Authorization',
|
Object.assign(headers, {'X-MS-Teams-Tenant-FQDN': this.req.locals.msTeamsTenantFqdn});
|
||||||
'-Max-Forwards',
|
|
||||||
'-Record-Route',
|
// for Microsoft Teams the Contact header must include the tenant FQDN
|
||||||
'-Session-Expires',
|
Object.assign(responseHeaders, {
|
||||||
'-X-Subspace-Forwarded-For'
|
Allow: 'INVITE, ACK, OPTIONS, CANCEL, BYE, NOTIFY, UPDATE, PRACK',
|
||||||
],
|
Contact: `sip:${this.req.locals.msTeamsTenantFqdn}`
|
||||||
proxyResponseHeaders: ['all', '-X-Trace-ID'],
|
});
|
||||||
localSdpB: spdOfferB,
|
}
|
||||||
localSdpA: async(sdp, res) => {
|
if (this.req.locals.application_sid) {
|
||||||
this.rtpEngineOpts.uac.tag = res.getParsedHeader('To').params.tag;
|
Object.assign(headers, {'X-Application-Sid': this.req.locals.application_sid});
|
||||||
const opts = {
|
}
|
||||||
...this.rtpEngineOpts.common,
|
else if (this.req.authorization) {
|
||||||
...this.rtpEngineOpts.uas.mediaOpts,
|
if (this.req.authorization.grant && this.req.authorization.grant.application_sid) {
|
||||||
'from-tag': this.rtpEngineOpts.uas.tag,
|
Object.assign(headers, {'X-Application-Sid': this.req.authorization.grant.application_sid});
|
||||||
'to-tag': this.rtpEngineOpts.uac.tag,
|
}
|
||||||
sdp
|
if (this.req.authorization.challengeResponse) {
|
||||||
};
|
const {username, realm} = this.req.authorization.challengeResponse;
|
||||||
const startAt = process.hrtime();
|
Object.assign(headers, {'X-Authenticated-User': `${username}@${realm}`});
|
||||||
const response = await this.answer(opts);
|
|
||||||
const rtt = roundTripTime(startAt);
|
|
||||||
this.stats.histogram('app.rtpengine.response_time', rtt, [
|
|
||||||
'direction:inbound', 'command:answer', `rtpengine:${this.rtpengineIp}`]);
|
|
||||||
if ('ok' !== response.result) {
|
|
||||||
this.logger.error(`rtpengine answer failed with ${JSON.stringify(response)}`);
|
|
||||||
throw new Error('rtpengine failed: answer');
|
|
||||||
}
|
}
|
||||||
return response.sdp;
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// successfully connected
|
if (this.req.canceled) throw new Error('call canceled');
|
||||||
this.logger.info('call connected successfully to feature server');
|
|
||||||
debug('call connected successfully to feature server');
|
// now send the INVITE in towards the feature servers
|
||||||
this._setHandlers({uas, uac});
|
debug(`sending INVITE to ${proxy} with ${uri}`);
|
||||||
return;
|
const {uas, uac} = await this.srf.createB2BUA(this.req, this.res, uri, {
|
||||||
} catch (err) {
|
proxy,
|
||||||
this.rtpEngineResource.destroy().catch((err) => this.logger.info({err}, 'Error destroying rtpe after failure'));
|
headers,
|
||||||
this.activeCallIds.delete(this.req.get('Call-ID'));
|
responseHeaders,
|
||||||
this.stats.gauge('sbc.sip.calls.count', this.activeCallIds.size);
|
proxyRequestHeaders: [
|
||||||
if (err instanceof SipError) {
|
'all',
|
||||||
const tags = ['accepted:no', `sipStatus:${err.status}`, `originator:${this.req.locals.originator}`];
|
'-Authorization',
|
||||||
this.stats.increment('sbc.terminations', tags);
|
'-Max-Forwards',
|
||||||
this.logger.info(`call failed to connect to feature server with ${err.status}`);
|
'-Record-Route',
|
||||||
this.emit('failed');
|
'-Session-Expires',
|
||||||
|
'-X-Subspace-Forwarded-For'
|
||||||
|
],
|
||||||
|
proxyResponseHeaders: ['all', '-X-Trace-ID'],
|
||||||
|
localSdpB: spdOfferB,
|
||||||
|
localSdpA: async(sdp, res) => {
|
||||||
|
this.rtpEngineOpts.uac.tag = res.getParsedHeader('To').params.tag;
|
||||||
|
const opts = {
|
||||||
|
...this.rtpEngineOpts.common,
|
||||||
|
...this.rtpEngineOpts.uas.mediaOpts,
|
||||||
|
'from-tag': this.rtpEngineOpts.uas.tag,
|
||||||
|
'to-tag': this.rtpEngineOpts.uac.tag,
|
||||||
|
sdp
|
||||||
|
};
|
||||||
|
const startAt = process.hrtime();
|
||||||
|
const response = await this.answer(opts);
|
||||||
|
const rtt = roundTripTime(startAt);
|
||||||
|
this.stats.histogram('app.rtpengine.response_time', rtt, [
|
||||||
|
'direction:inbound', 'command:answer', `rtpengine:${this.rtpengineIp}`]);
|
||||||
|
if ('ok' !== response.result) {
|
||||||
|
this.logger.error(`rtpengine answer failed with ${JSON.stringify(response)}`);
|
||||||
|
throw new Error('rtpengine failed: answer');
|
||||||
|
}
|
||||||
|
return response.sdp;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// successfully connected
|
||||||
|
this.logger.info('call connected successfully to feature server');
|
||||||
|
debug('call connected successfully to feature server');
|
||||||
|
this._setHandlers({uas, uac});
|
||||||
|
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line promise/no-promise-in-callback
|
||||||
|
this.rtpEngineResource.destroy()
|
||||||
|
.catch((err) => this.logger.info({err}, 'Error destroying rtpe after failure'));
|
||||||
|
this.activeCallIds.delete(this.req.get('Call-ID'));
|
||||||
|
this.stats.gauge('sbc.sip.calls.count', this.activeCallIds.size);
|
||||||
|
if (err instanceof SipError) {
|
||||||
|
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}`);
|
||||||
|
this.emit('failed');
|
||||||
|
}
|
||||||
|
else if (err.message !== 'call canceled') {
|
||||||
|
this.logger.error(err, 'unexpected error routing inbound call');
|
||||||
|
}
|
||||||
|
this.srf.endSession(this.req);
|
||||||
}
|
}
|
||||||
else if (err.message !== 'call canceled') {
|
});
|
||||||
this.logger.error(err, 'unexpected error routing inbound call');
|
|
||||||
}
|
|
||||||
this.srf.endSession(this.req);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_setDlgHandlers(dlg) {
|
_setDlgHandlers(dlg) {
|
||||||
|
|||||||
Reference in New Issue
Block a user