The drachtio signaling resource framework module
- Source:
The drachtio signaling resource framework module
Applications create an instance of Srf in order to create and manage SIP Dialogs and SIP transactions. An application may have one or more Srf instances, although for most cases a single instance is sufficient.
make an inbound connection to a drachtio server
Name | Type | Description | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
opts |
Object | connection options Properties
|
const Srf = require('drachtio-srf');
const srf = new Srf();
srf.connect({host: '127.0.0.1', port: 9022, secret: 'cymru'});
srf.on('connect', (hostport) => {
console.log(`connected to drachtio server offering sip endpoints: ${hostport}`);
})
.on('error', (err) => {
console.error(`error connecting: ${err}`);
});
srf.invite((req, res) => {..});
listen for outbound connections from a drachtio server
Name | Type | Description | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
opts |
Object | listen options Properties
|
const Srf = require('drachtio-srf');
const srf = new Srf();
srf.listen({port: 9023, secret: 'cymru'});
srf.invite((req, res) => {..});
create a SIP dialog, acting as a UAS (user agent server); i.e. respond to an incoming SIP INVITE with a 200 OK (or to a SUBSCRIBE request with a 202 Accepted).
Note that the Dialog is generated (i.e. the callback invoked / the Promise resolved) at the moment that the 200 OK is sent back towards the requestor, not when the ACK is subsequently received.
Name | Type | Description | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
req |
Object | the incoming sip request object |
|||||||||
res |
Object | the sip response object |
|||||||||
opts |
Object | configuration options Properties
|
|||||||||
callback |
function <optional> | if provided, callback with signature |
if a callback is supplied, a reference to the Srf instance.
If no callback is supplied, then a Promise that is resolved
with the sip dialog that is created.
returning a Promise
const Srf = require('drachtio-srf');
const srf = new Srf();
srf.invite((req, res) => {
const mySdp; // populated somehow with SDP we want to answer in 200 OK
srf.createUas(req, res, {localSdp: mySdp})
.then((uas) => {
console.log(`dialog established, remote uri is ${uas.remote.uri}`);
uas.on('destroy', () => {
console.log('caller hung up');
});
})
.catch((err) => {
console.log(`Error establishing dialog: ${err}`);
});
});
using callback
const Srf = require('drachtio-srf');
const srf = new Srf();
srf.invite((req, res) => {
const mySdp; // populated somehow with SDP we want to offer in 200 OK
srf.createUas(req, res, {localSdp: mySdp},
(err, uas) => {
if (err) {
return console.log(`Error establishing dialog: ${err}`);
}
console.log(`dialog established, local tag is ${uas.sip.localTag}`);
uas.on('destroy', () => {
console.log('caller hung up');
});
});
});
specifying standard or custom headers
srf.createUas(req, res, {
localSdp: mySdp,
headers: {
'User-Agent': 'drachtio/iechyd-da',
'X-Linked-UUID': '1e2587c'
}
}).then((uas) => { ..});
create a SIP dialog, acting as a UAC (user agent client)
Name | Type | Description | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
uri |
string | request uri to send to |
|||||||||
opts |
Object | configuration options Properties
|
|||||||||
progressCallbacks |
Object <optional> | callbacks providing call progress notification Properties
|
|||||||||
callback |
function <optional> | if provided, callback with signature |
if a callback is supplied, a reference to the Srf instance.
If no callback is supplied, then a Promise that is resolved
with the sip dialog that is created.
returning a Promise
const Srf = require('drachtio-srf');
const srf = new Srf();
const mySdp; // populated somehow with SDP we want to offer
srf.createUac('sip:1234@10.10.100.1', {localSdp: mySdp})
.then((uac) => {
console.log(`dialog established, call-id is ${uac.sip.callId}`);
uac.on('destroy', () => {
console.log('called party hung up');
});
})
.catch((err) => {
console.log(`INVITE rejected with status: ${err.status}`);
});
});
Using a callback
const Srf = require('drachtio-srf');
const srf = new Srf();
const mySdp; // populated somehow with SDP we want to offer
srf.createUac('sip:1234@10.10.100.1', {localSdp: mySdp},
(err, uac) => {
if (err) {
return console.log(`INVITE rejected with status: ${err.status}`);
}
uac.on('destroy', () => {
console.log('called party hung up');
});
});
Canceling a request by using a progress callback
const Srf = require('drachtio-srf');
const srf = new Srf();
const mySdp; // populated somehow with SDP we want to offer
let inviteSent;
srf.createUac('sip:1234@10.10.100.1', {localSdp: mySdp},
{
cbRequest: (reqSent) => { inviteSent = req; }
})
.then((uac) => {
// unexpected, in this case
console.log('dialog established before we could cancel');
})
.catch((err) => {
assert(err.status === 487); // expected sip response to a CANCEL
});
});
// cancel the request after 0.5s
setTimeout(() => {
inviteSent.cancel();
}, 500);
create back-to-back dialogs; i.e. act as a back-to-back user agent (B2BUA), creating a pair of dialogs {uas, uac} -- a UAS dialog facing the caller or A party, and a UAC dialog facing the callee or B party such that media flows between them
Name | Type | Description | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
req |
Object | incoming sip request object |
||||||||||||||||||||||||||||
res |
Object | incoming sip response object |
||||||||||||||||||||||||||||
uri |
string | sip uri or IP address[:port] to send the UAC INVITE to |
||||||||||||||||||||||||||||
opts |
Object | configuration options Properties
|
||||||||||||||||||||||||||||
progressCallbacks |
Object <optional> | callbacks providing call progress notification Properties
|
||||||||||||||||||||||||||||
callback |
function <optional> | if provided, callback with signature |
if a callback is supplied, a reference to the Srf instance.
If no callback is supplied, then a Promise that is resolved
with the sip dialog that is created.
simple B2BUA
const Srf = require('drachtio-srf');
const srf = new Srf();
srf.invite((req, res) => {
srf.createB2BUA('sip:1234@10.10.100.1', req, res, {localSdpB: req.body})
.then({uas, uac} => {
console.log('call connected');
// when one side terminates, hang up the other
uas.on('destroy', () => { uac.destroy(); });
uac.on('destroy', () => { uas.destroy(); });
})
.catch((err) => {
console.log(`call failed to connect: ${err}`);
});
});
use opts.passFailure to attempt a fallback URI on failure
const Srf = require('drachtio-srf');
const srf = new Srf();
function endCall(dlg1, dlg2) {
dlg1.on('destroy', () => {dlg2.destroy();})
dlg2.on('destroy', () => {dlg1.destroy();})
}
srf.invite((req, res) => {
srf.createB2BUA('sip:1234@10.10.100.1', req, res, {localSdpB: req.body, passFailure: false})
.then({uas, uac} => {
console.log('call connected to primary destination');
endcall(uas, uac);
})
.catch((err) => {
// try backup if we got a sip non-success response and the caller did not hang up
if (err instanceof Srf.SipError && err.status !== 487) {
console.log(`failed connecting to primary, will try backup: ${err}`);
srf.createB2BUA('sip:1234@10.10.100.2', req, res, {
localSdpB: req.body}
})
.then({uas, uac} => {
console.log('call connected to backup destination');
endcall(uas.uac);
})
catch((err) => {
console.log(`failed connecting to backup uri: ${err}`);
});
}
});
});
B2BUA with media proxy using rtpengine
const Srf = require('drachtio-srf');
const srf = new Srf();
const rtpengine = require('rtpengine-client').Client
// helper functions
// clean up and free rtpengine resources when either side hangs up
function endCall(dlg1, dlg2, details) {
[dlg1, dlg2].each((dlg) => {
dlg.on('destroy', () => {(dlg === dlg1 ? dlg2 : dlg1).destroy();});
rtpengine.delete(details);
});
}
// function returning a Promise that resolves with the SDP to offer A leg in 18x/200 answer
function getSdpA(details, remoteSdp, res) {
return rtpengine.answer(Object.assign(details, {
'sdp': remoteSdp,
'to-tag': res.getParsedHeader('To').params.tag
}))
.then((response) => {
if (response.result !== 'ok') throw new Error(`Error calling answer: ${response['error-reason']}`);
return response.sdp;
})
}
// handle incoming invite
srf.invite((req, res) => {
const from = req.getParsedHeader('From');
const details = {'call-id': req.get('Call-Id'), 'from-tag': from.params.tag};
rtpengine.offer(Object.assign(details, {'sdp': req.body})
.then((rtpResponse) => {
if (rtpResponse && rtpResponse.result === 'ok') return rtpResponse.sdp;
throw new Error('rtpengine failure');
})
.then((sdpB) => {
return srf.createB2BUA('sip:1234@10.10.100.1', req, res, {
localSdpB: sdpB,
localSdpA: getSdpA.bind(null, details)
});
})
.then({uas, uac} => {
console.log('call connected with media proxy');
endcall(uas, uac, details);
})
.catch((err) => {
console.log(`Error proxying call with media: ${err}`);
});
});
proxy an incoming request
Name | Type | Description | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
req |
Request | drachtio request object representing an incoming SIP request |
||||||||||||||||||||||||
destination |
String | Array <optional> | an IP address[:port], or list of same, to proxy the request to |
||||||||||||||||||||||||
opts |
Object <optional> | configuration options for the proxy operation Properties
|
||||||||||||||||||||||||
callback |
function <optional> | callback invoked when proxy operation completes, signature (err, results)
where |
returns a Promise if no callback is supplied, otherwise the Srf object
simple proxy
const Srf = require('drachtio-srf');
const srf = new Srf();
srf.invite((req, res) => {
srf.proxyRequest(req, 'sip.example.com');
});
proxy with options
const Srf = require('drachtio-srf');
const srf = new Srf();
srf.invite((req, res) => {
srf.proxyRequest(req, ['sip.example1.com', 'sip.example2.com'], {
recordRoute: true,
followRedirects: true,
provisionalTimeout: '2s'
}).then((results) => {
console.log(JSON.stringify(result)); // {finalStatus: 200, finalResponse:{..}, responses: [..]}
});
});
send a SIP request outside of a dialog
Name | Type | Description | ||||||
---|---|---|---|---|---|---|---|---|
uri |
string | sip request-uri to send request to |
||||||
opts |
Object | configuration options Properties
|
||||||
headers |
Object <optional> | SIP headers to apply to the outbound request |
||||||
body |
String <optional> | body to send with the SIP request |
||||||
callback |
function <optional> | callback invoked when sip request has been sent, invoked with
signature (err, request) where |
sending OPTIONS request
srf.request('sip.example.com', {
method: 'OPTIONS',
headers: {
'User-Agent': 'drachtio'
}
}, (err, req) => {
req.on('response', (res) => {
console.log(`received ${res.statusCode} response`);
});
});
a cdr:attempt
event is emitted by an Srf instance when a call attempt has been
received (inbound) or initiated (outbound)
Name | Type | Description |
---|---|---|
source |
String | 'network'|'application', depending on whether the INVITE is \inbound (received), or outbound (sent), respectively |
time |
String | the time (UTC) recorded by the SIP stack corresponding to the attempt |
msg |
Object | the actual message that was sent or received |
a cdr:start
event is emitted by an Srf instance when a call attempt has been connected successfully
Name | Type | Description |
---|---|---|
source |
String | 'network'|'application', depending on whether the INVITE is inbound (received), or outbound (sent), respectively |
time |
String | the time (UTC) recorded by the SIP stack corresponding to the attempt |
role |
String | 'uac'|'uas'|'uac-proxy'|'uas-proxy' indicating whether the application is acting as a user agent client, user agent server, proxy (sending message), or proxy (receiving message) for this cdr |
msg |
Object | the actual message that was sent or received |
a cdr:stop
event is emitted by an Srf instance when a connected call has ended
Name | Type | Description |
---|---|---|
source |
String | 'network'|'application', depending on whether the INVITE is inbound (received), or outbound (sent), respectively |
time |
String | the time (UTC) recorded by the SIP stack corresponding to the attempt |
reason |
String | the reason the call was ended |
msg |
Object | the actual message that was sent or received |
a connect
event is emitted by an Srf instance when a connect method completes
with either success or failure
Name | Type | Description |
---|---|---|
err |
Object | error encountered when attempting to connect |
hostport |
String | the SIP address[:port] drachtio server is listening on for incoming SIP messages |
Class representing a SIP Dialog.
Note that instances of this class are not created directly by your code; rather they are returned from the Srf#createUAC, Srf#createUAC, and Srf#createB2BUA
Constructor that is called internally by Srf when generating a Dialog instance.
Name | Type | Description |
---|---|---|
srf |
Srf | Srf instance that created this dialog |
type |
string | type of SIP dialog: 'uac', or 'uas' |
opts |
Dialog~Options |
local side of the Dialog
local side of the Dialog
sip properties that uniquely identify this Dialog
destroy the sip dialog by generating a BYE request (in the case of INVITE dialog), or NOTIFY (in the case of SUBSCRIBE)
Name | Type | Description |
---|---|---|
callback |
Dialog~requestCallback <optional> | callback that returns the generated BYE or NOTIFY message |
if no callback is supplied, otherwise the function returns a reference to the Dialog
modify the dialog session by changing attributes of the media connection
Name | Type | Description |
---|---|---|
sdp |
string | 'hold', 'unhold', or a session description protocol |
callback |
Dialog~modifySessionCallback <optional> | callback invoked when operation has completed |
if no callback is supplied, otherwise the function returns a reference to the Dialog
send a request within a dialog.
Note that you may also call request.info(..)
as a shortcut
to send an INFO message, request.notify(..)
to send a NOTIFY, etc..
Name | Type | Description |
---|---|---|
opts |
Dialog~requestOptions | configuration options |
callback |
Dialog~requestCallback <optional> | callback invoked when operation has completed |
if no callback is supplied a Promise that resolves to the response received, otherwise the function returns a reference to the Dialog
This callback provides the response a modifySession request.
Name | Type | Description |
---|---|---|
err |
Error | non-success sip response code received from far end |
Arguments provided to Dialog constructor.
Name | Type | Attributes | Description |
---|---|---|---|
req |
Object | drachtio Request (only provided when creating a 'uas' dialogs) |
|
res |
Object | drachtio Response |
|
sent |
Object |
<optional> |
uas only: actual Response sent when confirming the dialog |
This callback provides the SIP request that was generated
Name | Type | Description |
---|---|---|
err |
Error | error returned on non-success |
req |
Request | Request that was generated |
This callback provides the response to a sip request sent within the dialog.
Name | Type | Description |
---|---|---|
err |
Error | error, if any |
response |
Response | response received from the far end |
Name | Type | Attributes | Description |
---|---|---|---|
method |
String | SIP method to use for the request |
|
headers |
Object |
<optional> |
SIP headers to apply to the request |
body |
String |
<optional> |
body of the SIP request |
SIP Endpoint identifiers
Name | Type | Description |
---|---|---|
uri |
String | sip |
sdp |
String | session description protocol |
SIP Dialog identifiers
Name | Type | Description |
---|---|---|
callId |
String | SIP Call-ID |
localTag |
String | tag generated by local side of the Dialog |
remoteTag |
String | tag generated by the remote side of the Dialog |
a destroy
event is triggered when the Dialog is torn down from the far end
Name | Type | Description |
---|---|---|
msg |
Object | incoming BYE request message |
an info
event is triggered when the far end sends an INFO message.
When an application adds a handler for this event it must generate
the SIP response by calling res.send
on the provided drachtio response object.
When no handler is found for this event a 200 OK will be automatically generated.
Name | Type | Description |
---|---|---|
req |
Object | drachtio request object |
res |
Object | drachtio response object |
a modify
event is triggered when the far end modifies the session by sending a re-INVITE.
When an application adds a handler for this event it must generate
the SIP response by calling res.send
on the provided drachtio response object.
When no handler is found for this event a 200 OK with the current local SDP
will be automatically generated.
Name | Type | Description |
---|---|---|
req |
Object | drachtio request object |
res |
Object | drachtio response object |
a notify
event is triggered when the far end sends a NOTIFY message.
When an application adds a handler for this event it must generate
the SIP response by calling res.send
on the provided drachtio response object.
When no handler is found for this event a 200 OK will be automatically generated.
Name | Type | Description |
---|---|---|
req |
Object | drachtio request object |
res |
Object | drachtio response object |
a refer
event is triggered when the far end sends a REFER message.
When an application adds a handler for this event it must generate
the SIP response by calling res.send
on the provided drachtio response object.
When no handler is found for this event a 200 OK will be automatically generated.
Name | Type | Description |
---|---|---|
req |
Object | drachtio request object |
res |
Object | drachtio response object |
a refresh
event is triggered when the far end sends a session refresh.
There is no need for the application to respond to this event; this is purely
a notification.
Name | Type | Description |
---|---|---|
msg |
Object | incoming re-INVITE request message |
an update
event is triggered when the far end sends an UPDATE message.
When an application adds a handler for this event it must generate
the SIP response by calling res.send
on the provided drachtio response object.
When no handler is found for this event a 200 OK will be automatically generated.
Name | Type | Description |
---|---|---|
req |
Object | drachtio request object |
res |
Object | drachtio response object |
Class representing a SIP non-success response to a transaction
Create a SipError object
Name | Type | Description |
---|---|---|
status |
number | SIP final status |
reason |
string <optional> | reason for failure; if not provided the standard reason associated with the provided SIP status is used |