Compare commits

...
Author SHA1 Message Date
Dave Horton 24c2225758 call rtpengine offer using callback 2022-09-27 16:03:46 +01:00
+123 -114
View File
@@ -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) {