mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-19 04:17:44 +00:00
Fix/dial refer (#1264)
* Revert "Update dial.js (#1243)"
This reverts commit 259dedcded.
* add to .gitignore
* when we receive a REFER on the parent leg, after adulting the child the dial task in the parent session should end
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,6 +2,9 @@
|
|||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
.claude/
|
||||||
|
CLAUDE.md
|
||||||
|
|
||||||
# Runtime data
|
# Runtime data
|
||||||
pids
|
pids
|
||||||
*.pid
|
*.pid
|
||||||
|
|||||||
@@ -422,12 +422,14 @@ class TaskDial extends Task {
|
|||||||
}
|
}
|
||||||
//caller and callee legs are briged together, accept refer with 202 will release callee leg endpoint
|
//caller and callee legs are briged together, accept refer with 202 will release callee leg endpoint
|
||||||
//that makes freeswitch release endpoint for caller leg.
|
//that makes freeswitch release endpoint for caller leg.
|
||||||
//if the remaning leg is not a child (the child has now adulted) then hangup the A leg.
|
|
||||||
if (this.ep) this.ep.unbridge();
|
if (this.ep) this.ep.unbridge();
|
||||||
res.send(202);
|
res.send(202);
|
||||||
this.logger.info('DialTask:handleRefer - sent 202 Accepted');
|
this.logger.info('DialTask:handleRefer - sent 202 Accepted');
|
||||||
|
|
||||||
|
/* if we got the REFER on the parent leg, end the dial task after completing the refer */
|
||||||
if (!isChild) {
|
if (!isChild) {
|
||||||
this.cs._jambonzHangup('refer-completed');
|
this.logger.info('DialTask:handleRefer - killing dial task after processing REFER on parent leg');
|
||||||
|
cs.currentTask?.kill(cs, KillReason.ReferComplete);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.info({err}, 'DialTask:handleRefer - error processing incoming REFER');
|
this.logger.info({err}, 'DialTask:handleRefer - error processing incoming REFER');
|
||||||
|
|||||||
@@ -243,6 +243,7 @@
|
|||||||
"KillReason": {
|
"KillReason": {
|
||||||
"Hangup": "hangup",
|
"Hangup": "hangup",
|
||||||
"Replaced": "replaced",
|
"Replaced": "replaced",
|
||||||
|
"ReferComplete": "refer-complete",
|
||||||
"MediaTimeout": "media_timeout"
|
"MediaTimeout": "media_timeout"
|
||||||
},
|
},
|
||||||
"HookMsgTypes": [
|
"HookMsgTypes": [
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ require('./sip-request-tests');
|
|||||||
require('./create-call-test');
|
require('./create-call-test');
|
||||||
require('./play-tests');
|
require('./play-tests');
|
||||||
require('./sip-refer-tests');
|
require('./sip-refer-tests');
|
||||||
|
require('./sip-refer-handler-tests');
|
||||||
require('./listen-tests');
|
require('./listen-tests');
|
||||||
require('./config-test');
|
require('./config-test');
|
||||||
require('./queue-test');
|
require('./queue-test');
|
||||||
|
|||||||
117
test/scenarios/uas-dial-refer.xml
Normal file
117
test/scenarios/uas-dial-refer.xml
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||||
|
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||||
|
|
||||||
|
<scenario name="UAS that accepts call and sends REFER">
|
||||||
|
<!-- Receive incoming INVITE -->
|
||||||
|
<recv request="INVITE" crlf="true">
|
||||||
|
<action>
|
||||||
|
<ereg regexp=".*" search_in="hdr" header="Subject:" assign_to="1" />
|
||||||
|
<ereg regexp=".*" search_in="hdr" header="From:" assign_to="2" />
|
||||||
|
</action>
|
||||||
|
</recv>
|
||||||
|
|
||||||
|
<!-- Send 180 Ringing -->
|
||||||
|
<send>
|
||||||
|
<![CDATA[
|
||||||
|
SIP/2.0 180 Ringing
|
||||||
|
[last_Via:]
|
||||||
|
[last_From:]
|
||||||
|
[last_To:];tag=[pid]SIPpTag01[call_number]
|
||||||
|
[last_Call-ID:]
|
||||||
|
[last_CSeq:]
|
||||||
|
[last_Record-Route:]
|
||||||
|
Subject:[$1]
|
||||||
|
Content-Length: 0
|
||||||
|
]]>
|
||||||
|
</send>
|
||||||
|
|
||||||
|
<!-- Send 200 OK with SDP -->
|
||||||
|
<send>
|
||||||
|
<![CDATA[
|
||||||
|
SIP/2.0 200 OK
|
||||||
|
[last_Via:]
|
||||||
|
[last_From:]
|
||||||
|
[last_To:];tag=[pid]SIPpTag01[call_number]
|
||||||
|
[last_Call-ID:]
|
||||||
|
[last_CSeq:]
|
||||||
|
[last_Record-Route:]
|
||||||
|
Subject:[$1]
|
||||||
|
Contact: <sip:[local_ip]:[local_port];transport=[transport]>
|
||||||
|
Content-Type: application/sdp
|
||||||
|
Content-Length: [len]
|
||||||
|
|
||||||
|
v=0
|
||||||
|
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
|
||||||
|
s=-
|
||||||
|
c=IN IP[media_ip_type] [media_ip]
|
||||||
|
t=0 0
|
||||||
|
m=audio [media_port] RTP/AVP 0
|
||||||
|
a=rtpmap:0 PCMU/8000
|
||||||
|
]]>
|
||||||
|
</send>
|
||||||
|
|
||||||
|
<recv request="ACK" rtd="true" crlf="true">
|
||||||
|
<action>
|
||||||
|
<!-- Check if this is NOT the first call (tag ends with 012 or higher) -->
|
||||||
|
<ereg regexp="tag=1SIPpTag01[2-9]" search_in="hdr" header="To:" assign_to="3" />
|
||||||
|
<log message="Not first call check result: [$3]"/>
|
||||||
|
</action>
|
||||||
|
</recv>
|
||||||
|
|
||||||
|
<!-- Skip REFER if we found a non-first call tag -->
|
||||||
|
<nop next="skip_refer" test="3" value="" compare="not_equal">
|
||||||
|
<action>
|
||||||
|
<log message="Found non-first call tag [$3], skipping REFER"/>
|
||||||
|
</action>
|
||||||
|
</nop>
|
||||||
|
|
||||||
|
<!-- Wait a moment, then send REFER (only on first call) -->
|
||||||
|
<pause milliseconds="1000"/>
|
||||||
|
|
||||||
|
<nop>
|
||||||
|
<action>
|
||||||
|
<log message="Sending REFER for first call"/>
|
||||||
|
</action>
|
||||||
|
</nop>
|
||||||
|
|
||||||
|
<!-- Send REFER (only on first iteration) -->
|
||||||
|
<send retrans="500">
|
||||||
|
<![CDATA[
|
||||||
|
REFER sip:service@[remote_ip]:[remote_port] SIP/2.0
|
||||||
|
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
|
||||||
|
From: <sip:[local_ip]:[local_port]>;tag=[pid]SIPpTag01[call_number]
|
||||||
|
To: [$2]
|
||||||
|
[last_Call-ID:]
|
||||||
|
CSeq: 2 REFER
|
||||||
|
Contact: <sip:[local_ip]:[local_port];transport=[transport]>
|
||||||
|
Max-Forwards: 70
|
||||||
|
X-Call-Number: [call_number]
|
||||||
|
Refer-To: <sip:+15551234567@example.com>
|
||||||
|
Referred-By: <sip:[local_ip]:[local_port]>
|
||||||
|
Content-Length: 0
|
||||||
|
]]>
|
||||||
|
</send>
|
||||||
|
|
||||||
|
<!-- Expect 202 Accepted (only on first iteration) -->
|
||||||
|
<recv response="202"/>
|
||||||
|
|
||||||
|
<label id="skip_refer"/>
|
||||||
|
|
||||||
|
<!-- Wait for BYE from feature server -->
|
||||||
|
<recv request="BYE"/>
|
||||||
|
|
||||||
|
<!-- Send 200 OK to BYE -->
|
||||||
|
<send>
|
||||||
|
<![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>
|
||||||
|
|
||||||
|
</scenario>
|
||||||
90
test/sip-refer-handler-tests.js
Normal file
90
test/sip-refer-handler-tests.js
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
const test = require('tape');
|
||||||
|
const { sippUac } = require('./sipp')('test_fs');
|
||||||
|
const bent = require('bent');
|
||||||
|
const getJSON = bent('json')
|
||||||
|
const clearModule = require('clear-module');
|
||||||
|
const {provisionCallHook} = require('./utils');
|
||||||
|
const { sleepFor } = require('../lib/utils/helpers');
|
||||||
|
|
||||||
|
process.on('unhandledRejection', (reason, p) => {
|
||||||
|
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||||
|
});
|
||||||
|
|
||||||
|
function connect(connectable) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
connectable.on('connect', () => {
|
||||||
|
return resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
test('when parent leg recvs REFER it should end the dial after adulting child leg', async(t) => {
|
||||||
|
clearModule.all();
|
||||||
|
const {srf, disconnect} = require('../app');
|
||||||
|
try {
|
||||||
|
await connect(srf);
|
||||||
|
// wait for fs connected to drachtio server.
|
||||||
|
await sleepFor(1000);
|
||||||
|
|
||||||
|
// GIVEN
|
||||||
|
const from = "dial_refer_handler";
|
||||||
|
let verbs = [
|
||||||
|
{
|
||||||
|
"verb": "dial",
|
||||||
|
"callerId": from,
|
||||||
|
"actionHook": "/actionHook",
|
||||||
|
"referHook": "/referHook",
|
||||||
|
"anchorMedia": true,
|
||||||
|
"target": [
|
||||||
|
{
|
||||||
|
"type": "phone",
|
||||||
|
"number": "15083084809"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
await provisionCallHook(from, verbs);
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
//const p = sippUac('uas-dial.xml', '172.38.0.10', undefined, undefined, 2);
|
||||||
|
const p = sippUac('uas-dial-refer.xml', '172.38.0.10', undefined, undefined, 2);
|
||||||
|
await sleepFor(1000);
|
||||||
|
|
||||||
|
let account_sid = '622f62e4-303a-49f2-bbe0-eb1e1714e37a';
|
||||||
|
|
||||||
|
let post = bent('http://127.0.0.1:3000/', 'POST', 'json', 201);
|
||||||
|
post('v1/createCall', {
|
||||||
|
'account_sid':account_sid,
|
||||||
|
"call_hook": {
|
||||||
|
"url": "http://127.0.0.1:3100/",
|
||||||
|
"method": "POST",
|
||||||
|
},
|
||||||
|
"from": from,
|
||||||
|
"to": {
|
||||||
|
"type": "phone",
|
||||||
|
"number": "15583084808"
|
||||||
|
}});
|
||||||
|
|
||||||
|
await p;
|
||||||
|
|
||||||
|
// Verify that the referHook was called
|
||||||
|
const obj = await getJSON(`http://127.0.0.1:3100/lastRequest/${from}_referHook`);
|
||||||
|
t.ok(obj.body.from === from,
|
||||||
|
'dial-refer-handler: referHook was called with correct from');
|
||||||
|
t.ok(obj.body.refer_details && obj.body.refer_details.sip_refer_to,
|
||||||
|
'dial-refer-handler: refer_details included in referHook');
|
||||||
|
t.ok(obj.body.refer_details.refer_to_user === '+15551234567',
|
||||||
|
'dial-refer-handler: refer_to_user correctly parsed');
|
||||||
|
t.ok(obj.body.refer_details.referring_call_sid,
|
||||||
|
'dial-refer-handler: referring_call_sid included');
|
||||||
|
t.ok(obj.body.refer_details.referred_call_sid,
|
||||||
|
'dial-refer-handler: referred_call_sid included');
|
||||||
|
|
||||||
|
disconnect();
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`error received: ${err}`);
|
||||||
|
disconnect();
|
||||||
|
t.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -99,6 +99,24 @@ app.post('/actionHook', (req, res) => {
|
|||||||
return res.sendStatus(200);
|
return res.sendStatus(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* referHook
|
||||||
|
*/
|
||||||
|
app.post('/referHook', (req, res) => {
|
||||||
|
console.log({payload: req.body}, 'POST /referHook');
|
||||||
|
let key = req.body.from + "_referHook"
|
||||||
|
addRequestToMap(key, req, hook_mapping);
|
||||||
|
return res.json([{"verb": "pause", "length": 2}]);
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* adultingHook
|
||||||
|
*/
|
||||||
|
app.post('/adulting', (req, res) => {
|
||||||
|
console.log({payload: req.body}, 'POST /adulting');
|
||||||
|
return res.sendStatus(200);
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* customHook
|
* customHook
|
||||||
* For the hook to return
|
* For the hook to return
|
||||||
|
|||||||
Reference in New Issue
Block a user