mirror of
https://github.com/jambonz/sbc-inbound.git
synced 2026-10-04 02:04:22 +00:00
Feat/3pcc invite (#187)
* wip * wip * wip * add test for late media / 3pcc invite, which should now work
This commit is contained in:
@@ -244,10 +244,6 @@ srf.invite((req, res) => {
|
|||||||
}
|
}
|
||||||
return session.replaces(req, res);
|
return session.replaces(req, res);
|
||||||
}
|
}
|
||||||
if (req.locals.sdp === '') {
|
|
||||||
logger.info('no sdp in invite');
|
|
||||||
return res.send(488, {headers: {'X-Reason': '3pcc INVITEs without SDP are not currently supported'}});
|
|
||||||
}
|
|
||||||
const session = new CallSession(logger, req, res);
|
const session = new CallSession(logger, req, res);
|
||||||
session.connect();
|
session.connect();
|
||||||
});
|
});
|
||||||
|
|||||||
+26
-10
@@ -108,7 +108,8 @@ class CallSession extends Emitter {
|
|||||||
|
|
||||||
async connect() {
|
async connect() {
|
||||||
const {sdp} = this.req.locals;
|
const {sdp} = this.req.locals;
|
||||||
this.logger.info('inbound call accepted for routing');
|
const is3pcc = this.req.body?.length === 0;
|
||||||
|
this.logger.info(`inbound ${is3pcc ? '3pcc ' : ''}call accepted for routing`);
|
||||||
const engine = this.getRtpEngine();
|
const engine = this.getRtpEngine();
|
||||||
if (!engine) {
|
if (!engine) {
|
||||||
this.logger.info('No available rtpengines, rejecting call!');
|
this.logger.info('No available rtpengines, rejecting call!');
|
||||||
@@ -183,13 +184,13 @@ class CallSession extends Emitter {
|
|||||||
else uri = `${scheme}:${host}`;
|
else uri = `${scheme}:${host}`;
|
||||||
this.logger.info(`uri will be: ${uri}, proxy ${proxy}`);
|
this.logger.info(`uri will be: ${uri}, proxy ${proxy}`);
|
||||||
|
|
||||||
try {
|
const sendOfferToRtpEngine = async(remoteSdp) => {
|
||||||
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: [isPrivateVoipNetwork(this.req.source_address) ? 'private' : 'public', 'private'],
|
direction: [isPrivateVoipNetwork(this.req.source_address) ? 'private' : 'public', 'private'],
|
||||||
sdp
|
sdp: remoteSdp
|
||||||
};
|
};
|
||||||
const startAt = process.hrtime();
|
const startAt = process.hrtime();
|
||||||
const response = await this.offer(opts);
|
const response = await this.offer(opts);
|
||||||
@@ -202,7 +203,11 @@ class CallSession extends Emitter {
|
|||||||
this.logger.error({}, `rtpengine offer failed with ${JSON.stringify(response)}`);
|
this.logger.error({}, `rtpengine offer failed with ${JSON.stringify(response)}`);
|
||||||
throw new Error('rtpengine failed: answer');
|
throw new Error('rtpengine failed: answer');
|
||||||
}
|
}
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await sendOfferToRtpEngine(sdp);
|
||||||
let headers = {
|
let headers = {
|
||||||
'From': createBLegFromHeader(this.req),
|
'From': createBLegFromHeader(this.req),
|
||||||
'To': this.req.get('To'),
|
'To': this.req.get('To'),
|
||||||
@@ -212,9 +217,13 @@ class CallSession extends Emitter {
|
|||||||
};
|
};
|
||||||
if (this.privateSipAddress) headers = {...headers, Contact: `<sip:${this.privateSipAddress}>`};
|
if (this.privateSipAddress) headers = {...headers, Contact: `<sip:${this.privateSipAddress}>`};
|
||||||
|
|
||||||
const spdOfferB = this.siprec && this.xml ?
|
let spdOfferB;
|
||||||
createSiprecBody(headers, response.sdp, this.xml.type, this.xml.content) :
|
if (this.siprec && this.xml) {
|
||||||
response.sdp;
|
spdOfferB = createSiprecBody(headers, response.sdp, this.xml.type, this.xml.content);
|
||||||
|
}
|
||||||
|
else if (!is3pcc) {
|
||||||
|
spdOfferB = response.sdp;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.req.locals.carrier) {
|
if (this.req.locals.carrier) {
|
||||||
Object.assign(headers, {
|
Object.assign(headers, {
|
||||||
@@ -281,7 +290,10 @@ class CallSession extends Emitter {
|
|||||||
'-X-Authenticated-User'
|
'-X-Authenticated-User'
|
||||||
],
|
],
|
||||||
proxyResponseHeaders: ['all', '-X-Trace-ID'],
|
proxyResponseHeaders: ['all', '-X-Trace-ID'],
|
||||||
localSdpB: spdOfferB,
|
localSdpB: spdOfferB ? spdOfferB : async(ackBody) => {
|
||||||
|
const response = await sendOfferToRtpEngine(ackBody);
|
||||||
|
return response.sdp;
|
||||||
|
},
|
||||||
localSdpA: async(sdp, res) => {
|
localSdpA: async(sdp, res) => {
|
||||||
this.rtpEngineOpts.uac.tag = res.getParsedHeader('To').params.tag;
|
this.rtpEngineOpts.uac.tag = res.getParsedHeader('To').params.tag;
|
||||||
const opts = {
|
const opts = {
|
||||||
@@ -292,8 +304,12 @@ class CallSession extends Emitter {
|
|||||||
sdp
|
sdp
|
||||||
};
|
};
|
||||||
const startAt = process.hrtime();
|
const startAt = process.hrtime();
|
||||||
const response = await this.answer(opts);
|
const aOpts = {
|
||||||
this.logger.debug({response, opts}, 'response from rtpengine to answer');
|
...opts,
|
||||||
|
...(is3pcc && {direction: ['private', 'public']})
|
||||||
|
};
|
||||||
|
const response = await this.answer(aOpts);
|
||||||
|
this.logger.debug({response, opts: aOpts}, 'response from rtpengine to answer');
|
||||||
const rtt = roundTripTime(startAt);
|
const rtt = roundTripTime(startAt);
|
||||||
this.stats.histogram('app.rtpengine.response_time', rtt, [
|
this.stats.histogram('app.rtpengine.response_time', rtt, [
|
||||||
'direction:inbound', 'command:answer', `rtpengine:${this.rtpengineIp}`]);
|
'direction:inbound', 'command:answer', `rtpengine:${this.rtpengineIp}`]);
|
||||||
@@ -313,7 +329,7 @@ class CallSession extends Emitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return response.sdp;
|
return response.sdp;
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// successfully connected
|
// successfully connected
|
||||||
|
|||||||
@@ -19,22 +19,21 @@
|
|||||||
<!-- Sipp 'uac' scenario with pcap (rtp) play -->
|
<!-- Sipp 'uac' scenario with pcap (rtp) play -->
|
||||||
<!-- -->
|
<!-- -->
|
||||||
|
|
||||||
<scenario name="UAC with late media">
|
<scenario name="UAC with media">
|
||||||
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
|
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
|
||||||
<!-- generated by sipp. To do so, use [call_id] keyword. -->
|
<!-- generated by sipp. To do so, use [call_id] keyword. -->
|
||||||
<send retrans="500">
|
<send retrans="500">
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
|
|
||||||
INVITE sip:16173333456@[remote_ip]:[remote_port] SIP/2.0
|
INVITE sip:+16173333456@jambonz.org SIP/2.0
|
||||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[call_number]
|
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
|
||||||
To: sut <sip:[service]@[remote_ip]:[remote_port]>
|
To: <sip:16173333456@jambonz.org>
|
||||||
Call-ID: [call_id]
|
Call-ID: [call_id]
|
||||||
CSeq: 1 INVITE
|
CSeq: 1 INVITE
|
||||||
Contact: sip:sipp@[local_ip]:[local_port]
|
Contact: sip:sipp@[local_ip]:[local_port]
|
||||||
Max-Forwards: 70
|
Max-Forwards: 70
|
||||||
Subject: uac-no-3pcc
|
Subject: uac-late-media
|
||||||
Content-Type: application/sdp
|
|
||||||
Content-Length: 0
|
Content-Length: 0
|
||||||
|
|
||||||
]]>
|
]]>
|
||||||
@@ -43,27 +42,79 @@
|
|||||||
<recv response="100" optional="true">
|
<recv response="100" optional="true">
|
||||||
</recv>
|
</recv>
|
||||||
|
|
||||||
<recv response="488">
|
<recv response="180" optional="true">
|
||||||
</recv>
|
</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>
|
<send>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
|
|
||||||
ACK sip:16173333456@[remote_ip]:[remote_port] SIP/2.0
|
ACK sip:16173333456@jambonz.org SIP/2.0
|
||||||
[last_Via]
|
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||||
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
|
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag09[call_number]
|
||||||
To: [service] <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]
|
To: <sip:16173333456@jambonz.org>[peer_tag_param]
|
||||||
Call-ID: [call_id]
|
Call-ID: [call_id]
|
||||||
CSeq: 1 ACK
|
CSeq: 1 ACK
|
||||||
Subject: uac-no-3pcc
|
Max-Forwards: 70
|
||||||
|
Subject: uac-late-media
|
||||||
|
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>
|
||||||
|
|
||||||
|
<!-- 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:16173333456@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:16173333456@jambonz.org>[peer_tag_param]
|
||||||
|
Call-ID: [call_id]
|
||||||
|
CSeq: 2 BYE
|
||||||
|
Subject: uac-late-media
|
||||||
Content-Length: 0
|
Content-Length: 0
|
||||||
|
|
||||||
]]>
|
]]>
|
||||||
</send>
|
</send>
|
||||||
|
|
||||||
<!-- definition of the response time repartition table (unit is ms) -->
|
<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"/>
|
<ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
|
||||||
|
|
||||||
<!-- definition of the call length repartition table (unit is ms) -->
|
<!-- definition of the call length repartition table (unit is ms) -->
|
||||||
<CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
|
<CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
|
||||||
</scenario>
|
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user