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.
Param | Type | Description |
---|---|---|
tag | string | Array |
a string or array of strings, representing tag values for this application. Tags can be used in conjunction with a call routing web callback to direct requests to particular applications. |
Creates an instance of an signaling resource framework.
Param | Type | Description |
---|---|---|
req | Object |
the incoming sip request object |
res | Object |
the sip response object |
opts | Object |
configuration options |
opts.localSdp | string |
the local session description protocol to include in the SIP response |
[opts.headers] | Object |
SIP headers to include on the SIP response to the INVITE |
[callback] | function |
if provided, callback with signature (err, dialog) |
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.
Returns: Srf
| Promise
- 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.
Example (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}`);
});
});
Example (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');
});
});
});
Example (specifying standard or custom headers)
srf.createUas(req, res, {
localSdp: mySdp,
headers: {
'User-Agent': 'drachtio/iechyd-da',
'X-Linked-UUID': '1e2587c'
}
}).then((uas) => { ..});
Param | Type | Description |
---|---|---|
uri | string |
request uri to send to |
opts | Object |
configuration options |
[opts.headers] | Object |
SIP headers to include on the SIP INVITE request |
opts.localSdp | string |
the local session description protocol to include in the SIP INVITE request |
[opts.proxy] | string |
send the request through an outbound proxy, specified as full sip uri or address[:port] |
opts.auth | Object | function |
sip credentials to use if challenged, or a function invoked with (req, res) and returning (err, username, password) where req is the request that was sent and res is the response that included the digest challenge |
opts.auth.username | string |
sip username |
opts.auth.password | string |
sip password |
[progressCallbacks] | Object |
callbacks providing call progress notification |
[progressCallbacks.cbRequest] | function |
callback that provides request sent over the wire, with signature (req) |
[progressCallbacks.cbProvisional] | function |
callback that provides a provisional response with signature (provisionalRes) |
[callback] | function |
if provided, callback with signature (err, dialog) |
create a SIP dialog, acting as a UAC (user agent client)
Returns: Srf
| Promise
- 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.
Example (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}`);
});
});
Example (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');
});
});
Example (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);
Param | Type | Default | 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 | |
[opts.headers] | Object |
SIP headers to include on the SIP INVITE request to the B party | |
[opts.responseHeaders] | Object |
SIP headers to include on responses to the A party. Either an object containing SIP headers, or a function returning an object may be provided. If a function is provided, it will be called with the signature (uacRes, headers), where 'uacRes' is the response received from the B party, and 'headers' are the SIP headers that have currently been set for the response. | |
[opts.localSdpA] | string | function |
the local session description protocol to offer in the response to the SIP INVITE request on the A leg; either a string or a function may be provided. If a function is provided, it will be invoked with two parameters (sdp, res) correspnding to the SDP received from the B party, and the sip response object received on the response from B. The function must return either the SDP (as a string) or a Promise that resolves to the SDP. If no value is provided (neither string nor function), then the SDP returned by the B party in the provisional/final response on the UAC leg will be sent back to the A party in the answer. | |
[opts.localSdpB] | string |
the local session description protocol to offer in the SIP INVITE request on the B leg | |
[opts.proxyRequestHeaders] | Array |
an array of header names which, if they appear in the INVITE request on the A leg, should be included unchanged on the generated B leg INVITE | |
[opts.proxyResponseHeaders] | Array |
an array of header names which, if they appear in the response to the outgoing INVITE, should be included unchanged on the generated response to the A leg | |
[opts.passFailure] | Boolean |
true |
specifies whether to pass a failure returned from B leg back to the A leg |
[opts.passProvisionalResponses] | Boolean |
true |
specifies whether to pass provisional responses from B leg back to the A leg |
[opts.proxy] | string |
send the request through an outbound proxy, specified as full sip uri or address[:port] | |
opts.auth | Object | function |
sip credentials to use if challenged, or a function invoked with (req, res) and returning (err, username, password) where req is the request that was sent and res is the response that included the digest challenge | |
opts.auth.username | string |
sip username | |
opts.auth.password | string |
sip password | |
[progressCallbacks] | Object |
callbacks providing call progress notification | |
[progressCallbacks.cbRequest] | function |
callback that provides request sent over the wire, with signature (req) | |
[progressCallbacks.cbProvisional] | function |
callback that provides a provisional response with signature (provisionalRes) | |
[progressCallbacks.cbFinalizedUac] | function |
callback that provides the UAC dialog as soon as the 200 OK is received from the B party. Since the UAC dialog is also returned when the B2B has been completely constructed, this is mainly useful if there is some need to be notified as soon as the B party answers. The callback signature is (uac). | |
[callback] | function |
if provided, callback with signature (err, {uas, uac}) |
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
Returns: Srf
| Promise
- 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.
Example (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}`);
});
});
Example (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}`);
});
}
});
});
Example (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}`);
});
});
Param | Type | Default | Description |
---|---|---|---|
req | Request |
drachtio request object representing an incoming SIP request | |
[destination] | String | Array |
an IP address[:port], or list of same, to proxy the request to | |
[opts] | Object |
configuration options for the proxy operation | |
[opts.forking] | String |
sequential |
when multiple destinations are provided, this option governs whether they are attempted sequentially or in parallel. Valid values are 'sequential' or 'parallel' |
[opts.remainInDialog] | Boolean |
false |
if true, add Record-Route header and remain in the SIP dialog (i.e. receiving futher SIP messaging for the dialog, including the terminating BYE request). Alias: recordRoute . |
[opts.provisionalTimeout] | String |
timeout after which to attempt the next destination if no 100 Trying response has been received. Examples of valid syntax for this property is '1500ms', or '2s' | |
[opts.finalTimeout] | String |
timeout, in milliseconds, after which to cancel the current request and attempt the next destination if no final response has been received. Syntax is the same as for the provisionalTimeout property. | |
[opts.followRedirects] | Boolean |
false |
if true, handle 3XX redirect responses by generating a new request as per the Contact header; otherwise, proxy the 3XX response back upstream without generating a new response |
[callback] | function |
callback invoked when proxy operation completes, signature (err, results) where results is a JSON object describing the individual sip call attempts and results |
proxy an incoming request
Returns: Srf
| Promise
- returns a Promise if no callback is supplied, otherwise the Srf object
Example (simple proxy)
const Srf = require('drachtio-srf');
const srf = new Srf();
srf.invite((req, res) => {
srf.proxyRequest(req, 'sip.example.com');
});
Example (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: [..]}
});
});
Param | Type | Description |
---|---|---|
uri | String |
request-uri |
opts | Object |
options |
method | String |
SIP method for the request |
[opts.headers] | Object |
SIP headers to include on the request |
[body] | String |
body to include with the request |
[opts.auth] | Object |
authentication to use if challenged |
[opts.auth.username] | String |
sip username |
[opts.auth.password] | String |
sip password |
[callback] | function |
callback invoked when request is sent, signature (err, requestSent) where requestSent is a SipRequest sent out over the wire |
Send an outbound request outside of a Dialog.
Returns: Srf
| Promise
- returns a Promise if no callback is supplied, otherwise the Srf object
Param | Type | Description |
---|---|---|
stackDialogId | String |
dialog id |
Returns an existing dialog for a given dialog id, if it exists
Param | Type | Description |
---|---|---|
callId | String |
SIP Call-ID |
tag | String |
SIP From tag |
Returns an existing dialog for a given sip call-id and from tag, if it exists
Param | Type | Description |
---|---|---|
uri | string |
sip request-uri to send request to |
opts | Object |
configuration options |
opts.method | String |
SIP method to send (lower-case) |
[headers] | Object |
SIP headers to apply to the outbound request |
[body] | String |
body to send with the SIP request |
[opts.proxy] | string |
send the request through an outbound proxy, specified as full sip uri or address[:port] |
[callback] | function |
callback invoked when sip request has been sent, invoked with signature (err, request) where request is a sip request object representing the sip message that was sent. |
send a SIP request outside of a dialog
Example (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`);
});
});
Param | Type | Default | Description |
---|---|---|---|
opts | Object |
connection options | |
[opts.host] | string |
"127.0.0.1" |
address drachtio server is listening on for client connections |
[opts.port] | Number |
9022 |
address drachtio server is listening on for client connections |
opts.secret | String |
shared secret used to authenticate connections |
make an inbound connection to a drachtio server
Example
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) => {..});
Param | Type | Default | Description |
---|---|---|---|
opts | Object |
listen options | |
[opts.host] | number |
0.0.0.0 |
address to bind listening socket to |
opts.port | number |
tcp port to listen on | |
opts.secret | string |
shared secret used to authenticate connections |
listen for outbound connections from a drachtio server
Example
const Srf = require('drachtio-srf');
const srf = new Srf();
srf.listen({port: 3001, secret: 'cymru'});
srf.invite((req, res) => {..});
Param | Type | Description |
---|---|---|
msg | req | res |
SIP request or response object |
terminate the tcp socket connection associated with the request or response object, if the underlying socket was established as part of an outbound connection. If the underlying socket was established as part of an inbound connection, this method call is a no-op (does nothing).
Example
const Srf = require('drachtio-srf');
const srf = new Srf();
srf.listen({port: 3001, secret: 'cymru'});
srf.invite((req, res) => {
srf.createUas(req, res, {localSdp: mySdp})
.then((uas) => {
uas.on('destroy', () => {
console.log('caller hung up');
srf.endSession(req);
});
});
});
Param | Type | Description |
---|---|---|
err | Error |
error encountered when attempting to authorize after connecting |
hostport | Array |
an Array of SIP endpoints that the connected drachtio server is listening on for incoming SIP messages. The format of each endpoint is protcocol/adress:port. |
a connect
event is emitted by an Srf instance when a connect method completes
with either success or failure
Param | Type | Description |
---|---|---|
err | Error |
specific error information |
an error
event is emitted by an Srf instance when an inbound connection is lost
Param | 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:attempt
event is emitted by an Srf instance when a call attempt has been
received (inbound) or initiated (outbound)
Param | 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:start
event is emitted by an Srf instance when a call attempt has been connected successfully
Param | 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 cdr:stop
event is emitted by an Srf instance when a connected call has ended
a SIP Dialog
inherits from Error and represents a non-success final SIP response to a request; status and reason properties provide the numeric sip status code and the reason for the failure.
parses a SIP uri string
Returns: function
- a function that takes a SIP uri and returns an object
Example
const Srf = require('drachtio-srf');
const srf = new Srf();
const parseUri = Srf.parseUri;
// connect, etc..
srf.invite((req, res) => {
const uri = parseUri(req.get('From'));
console.log(`parsed From header: ${JSON.stringify(uri)}`);
// {
// "scheme": "sip",
// "family": "ipv4",
// "user": "+15083084807",
// "host": "192.168.1.100",
// "port": 5080,
// "params": {
// "tag": "3yid87"
// }
// }
});
Extends: EventEmitter
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
Param | Type | Description |
---|---|---|
srf | Srf |
Srf instance that created this dialog |
type | string |
type of SIP dialog: 'uac', or 'uas' |
opts | Dialog~Options |
Constructor that is called internally by Srf when generating a Dialog instance.
Properties
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 |
sip properties that uniquely identify this Dialog
Properties
Name | Type | Description |
---|---|---|
uri | String |
sip |
sdp | String |
session description protocol |
local side of the Dialog
Properties
Name | Type | Description |
---|---|---|
uri | String |
sip |
sdp | String |
session description protocol |
remote side of the Dialog
Param | Type | Description |
---|---|---|
[opts] | Object |
configuration options |
[opts.headers] | Object |
SIP headers to add to the outgoing BYE or NOTIFY |
[callback] | function |
if provided, callback with signature (err, msg) that provides the BYE or NOTIFY message that was sent to terminate the dialog |
destroy the sip dialog by generating a BYE request (in the case of INVITE dialog), or NOTIFY (in the case of SUBSCRIBE)
Returns: Promise
| Dialog
- if no callback is supplied, otherwise a reference to the Dialog
Param | Type | Description |
---|---|---|
sdp | string |
'hold', 'unhold', or a session description protocol |
[callback] | function |
callback invoked with signature (err) when operation has completed |
modify the dialog session by changing attributes of the media connection
Returns: Promise
| Dialog
- if no callback is supplied, otherwise the function returns a reference to the Dialog
Param | Type | Description |
---|---|---|
[opts] | Object |
|
opts.method | string |
SIP method to use for the request |
[opts.headers] | Object |
SIP headers to apply to the request |
[opts.body] | string |
body of the SIP request |
[callback] | function |
callback invoked with signature (err, req) when operation has completed |
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..
Returns: Promise
| Dialog
- if no callback is supplied a Promise that resolves to the response received,
otherwise the function returns a reference to the Dialog
Param | Type | Description |
---|---|---|
msg | Object |
incoming BYE request message |
a destroy
event is triggered when the Dialog is torn down from the far end
Param | 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.
Param | Type | Description |
---|---|---|
msg | Object |
incoming re-INVITE request message |
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.
Param | Type | Description |
---|---|---|
req | Object |
drachtio request object |
res | Object |
drachtio response object |
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.
Param | 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.
Param | Type | Description |
---|---|---|
req | Object |
drachtio request object |
res | Object |
drachtio response object |
an options
event is triggered when the far end sends an OPTIONS 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.
Param | Type | Description |
---|---|---|
req | Object |
drachtio request object |
res | Object |
drachtio response object |
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.
Param | 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.
Param | Type | Description |
---|---|---|
req | Object |
drachtio request object |
res | Object |
drachtio response object |
a message
event is triggered when the far end sends a MESSAGE 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.
Extends: Error
Class representing a SIP non-success response to a transaction
Param | Type | Description |
---|---|---|
status | number |
SIP final status |
[reason] | string |
reason for failure; if not provided the standard reason associated with the provided SIP status is used |
Create a SipError object