drachtio-srf 4.4.60 → 4.4.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +1 -3
- package/lib/dialog.js +31 -12
- package/lib/drachtio-agent.js +5 -1
- package/lib/request.js +3 -3
- package/lib/response.js +2 -2
- package/lib/sip-parser/message.js +103 -0
- package/lib/sip-parser/parser.js +426 -0
- package/lib/srf.js +4 -4
- package/lib/wire-protocol.js +1 -1
- package/package.json +6 -3
- package/test/docker-compose-testbed.yaml +11 -0
- package/test/index.js +21 -9
- package/test/parser.js +8 -0
- package/test/scenarios/uas-bye-with-auth.xml +164 -0
- package/test/uac.js +66 -43
- package/test/unit-tests/parser.js +150 -0
|
@@ -3,8 +3,6 @@ name: npm-publish
|
|
|
3
3
|
# run when a tag is pushed or kick off manually
|
|
4
4
|
on:
|
|
5
5
|
push:
|
|
6
|
-
branches:
|
|
7
|
-
- main
|
|
8
6
|
tags:
|
|
9
7
|
- '*'
|
|
10
8
|
workflow_dispatch:
|
|
@@ -17,7 +15,7 @@ jobs:
|
|
|
17
15
|
- uses: actions/checkout@v2
|
|
18
16
|
- uses: actions/setup-node@v1
|
|
19
17
|
with:
|
|
20
|
-
node-version: '
|
|
18
|
+
node-version: '14.x'
|
|
21
19
|
registry-url: 'https://registry.npmjs.org'
|
|
22
20
|
- run: npm install
|
|
23
21
|
- run: npm publish --access public
|
package/lib/dialog.js
CHANGED
|
@@ -4,7 +4,7 @@ const only = require('only') ;
|
|
|
4
4
|
const methods = require('sip-methods') ;
|
|
5
5
|
const debug = require('debug')('drachtio:srf') ;
|
|
6
6
|
const SipError = require('./sip_error');
|
|
7
|
-
const { parseUri } = require('
|
|
7
|
+
const { parseUri } = require('./sip-parser/parser');
|
|
8
8
|
const sdpTransform = require('sdp-transform');
|
|
9
9
|
|
|
10
10
|
|
|
@@ -12,7 +12,7 @@ const sdpTransform = require('sdp-transform');
|
|
|
12
12
|
* Class representing a SIP Dialog.
|
|
13
13
|
*
|
|
14
14
|
* Note that instances of this class are not created directly by your code;
|
|
15
|
-
* rather they are returned from the {@link Srf#createUAC}, {@link Srf#
|
|
15
|
+
* rather they are returned from the {@link Srf#createUAC}, {@link Srf#createUAS}, and {@link Srf#createB2BUA}
|
|
16
16
|
* @class
|
|
17
17
|
* @extends EventEmitter
|
|
18
18
|
*/
|
|
@@ -34,6 +34,7 @@ class Dialog extends Emitter {
|
|
|
34
34
|
this.type = type ;
|
|
35
35
|
this.req = opts.req ;
|
|
36
36
|
this.res = opts.res ;
|
|
37
|
+
this.auth = opts.auth;
|
|
37
38
|
this.agent = this.res.agent ;
|
|
38
39
|
this.onHold = false ;
|
|
39
40
|
this.connected = true ;
|
|
@@ -171,7 +172,12 @@ class Dialog extends Emitter {
|
|
|
171
172
|
* or NOTIFY (in the case of SUBSCRIBE)
|
|
172
173
|
* @param {Object} [opts] configuration options
|
|
173
174
|
* @param {Object} [opts.headers] SIP headers to add to the outgoing BYE or NOTIFY
|
|
174
|
-
* @param
|
|
175
|
+
* @param {Object|Function} [opts.auth] sip credentials to use if challenged,
|
|
176
|
+
* or a function invoked with (req, res) and returning (err, username, password) where req is the
|
|
177
|
+
* request that was sent and res is the response that included the digest challenge
|
|
178
|
+
* @param {string} [opts.auth.username] sip username
|
|
179
|
+
* @param {string} [opts.auth.password] sip password
|
|
180
|
+
* @param {function} [callback] if provided, callback with signature <code>(err, msg)</code>
|
|
175
181
|
* that provides the BYE or NOTIFY message that was sent to terminate the dialog
|
|
176
182
|
* @return {Promise|Dialog} if no callback is supplied, otherwise a reference to the Dialog
|
|
177
183
|
*/
|
|
@@ -183,18 +189,29 @@ class Dialog extends Emitter {
|
|
|
183
189
|
}
|
|
184
190
|
this.queuedRequests = [];
|
|
185
191
|
|
|
192
|
+
const removeDialog = (err, res, callback) => {
|
|
193
|
+
this.connected = false ;
|
|
194
|
+
this.srf.removeDialog(this) ;
|
|
195
|
+
callback(err, res) ;
|
|
196
|
+
this.removeAllListeners();
|
|
197
|
+
};
|
|
198
|
+
|
|
186
199
|
const __x = (callback) => {
|
|
187
200
|
if (this.dialogType === 'INVITE') {
|
|
188
201
|
this.agent.request({
|
|
189
202
|
method: 'BYE',
|
|
190
203
|
headers: opts.headers || {},
|
|
191
204
|
stackDialogId: this.id,
|
|
205
|
+
auth: opts.auth || this.auth,
|
|
192
206
|
_socket: this.socket
|
|
193
207
|
}, (err, bye) => {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
208
|
+
if (err) {
|
|
209
|
+
return removeDialog(err, bye, callback);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
bye.on('response', () => {
|
|
213
|
+
removeDialog(err, bye, callback);
|
|
214
|
+
});
|
|
198
215
|
}) ;
|
|
199
216
|
if (this._emitter) {
|
|
200
217
|
Object.assign(this._state, {state: 'terminated'});
|
|
@@ -212,10 +229,7 @@ class Dialog extends Emitter {
|
|
|
212
229
|
stackDialogId: this.id,
|
|
213
230
|
_socket: this.socket
|
|
214
231
|
}, (err, notify) => {
|
|
215
|
-
|
|
216
|
-
this.srf.removeDialog(this) ;
|
|
217
|
-
callback(err, notify) ;
|
|
218
|
-
this.removeAllListeners();
|
|
232
|
+
removeDialog(err, notify, callback);
|
|
219
233
|
}) ;
|
|
220
234
|
}
|
|
221
235
|
};
|
|
@@ -367,6 +381,11 @@ class Dialog extends Emitter {
|
|
|
367
381
|
* @param {string} opts.method - SIP method to use for the request
|
|
368
382
|
* @param {Object} [opts.headers] - SIP headers to apply to the request
|
|
369
383
|
* @param {string} [opts.body] - body of the SIP request
|
|
384
|
+
* @param {Object|Function} [opts.auth] sip credentials to use if challenged,
|
|
385
|
+
* or a function invoked with (req, res) and returning (err, username, password) where req is the
|
|
386
|
+
* request that was sent and res is the response that included the digest challenge
|
|
387
|
+
* @param {string} [opts.auth.username] sip username
|
|
388
|
+
* @param {string} [opts.auth.password] sip password
|
|
370
389
|
* @param {function} [callback] - callback invoked with signature <code>(err, req)</code>
|
|
371
390
|
* when operation has completed
|
|
372
391
|
* @return {Promise|Dialog} if no callback is supplied a Promise that resolves to the response received,
|
|
@@ -383,7 +402,7 @@ class Dialog extends Emitter {
|
|
|
383
402
|
method: method,
|
|
384
403
|
stackDialogId: this.id,
|
|
385
404
|
headers: opts.headers || {},
|
|
386
|
-
auth: opts.auth,
|
|
405
|
+
auth: opts.auth || this.auth,
|
|
387
406
|
_socket: this.socket,
|
|
388
407
|
body: opts.body
|
|
389
408
|
}, (err, req) => {
|
package/lib/drachtio-agent.js
CHANGED
|
@@ -2,7 +2,7 @@ const Emitter = require('events');
|
|
|
2
2
|
const debug = require('debug')('drachtio:agent');
|
|
3
3
|
const debugSocket = require('debug')('drachtio:socket');
|
|
4
4
|
const WireProtocol = require('./wire-protocol') ;
|
|
5
|
-
const SipMessage = require('
|
|
5
|
+
const SipMessage = require('./sip-parser/message');
|
|
6
6
|
const Request = require('./request') ;
|
|
7
7
|
const Response = require('./response') ;
|
|
8
8
|
const DigestClient = require('./digest-client') ;
|
|
@@ -658,6 +658,10 @@ class DrachtioAgent extends Emitter {
|
|
|
658
658
|
this.pendingSipAuthTxnIdUpdate.set(res.req.stackTxnId, {});
|
|
659
659
|
const client = new DigestClient(res) ;
|
|
660
660
|
client.authenticate((err, req) => {
|
|
661
|
+
if (!req) {
|
|
662
|
+
sr.req.emit('response', res, ack);
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
661
665
|
// move all listeners from the old request to the new one we just generated
|
|
662
666
|
res.req.listeners('response').forEach((l) => { req.on('response', l) ; }) ;
|
|
663
667
|
res.req.emit('authenticate', req) ;
|
package/lib/request.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const Emitter = require('events').EventEmitter ;
|
|
2
|
-
const sip = require('drachtio-sip') ;
|
|
3
2
|
const delegate = require('delegates') ;
|
|
4
3
|
const assert = require('assert') ;
|
|
5
4
|
const noop = require('node-noop').noop;
|
|
6
5
|
const debug = require('debug')('drachtio:request');
|
|
6
|
+
const SipMessage = require('./sip-parser/message');
|
|
7
7
|
|
|
8
8
|
class Request extends Emitter {
|
|
9
9
|
|
|
@@ -11,7 +11,7 @@ class Request extends Emitter {
|
|
|
11
11
|
super() ;
|
|
12
12
|
|
|
13
13
|
if (msg) {
|
|
14
|
-
assert(msg instanceof
|
|
14
|
+
assert(msg instanceof SipMessage) ;
|
|
15
15
|
this.msg = msg ;
|
|
16
16
|
this.meta = meta ;
|
|
17
17
|
}
|
|
@@ -148,7 +148,7 @@ class Request extends Emitter {
|
|
|
148
148
|
//add a new response to the array
|
|
149
149
|
const address = meta.address ;
|
|
150
150
|
const port = +meta.port;
|
|
151
|
-
const msg = new
|
|
151
|
+
const msg = new SipMessage(rawMsg) ;
|
|
152
152
|
const obj = {
|
|
153
153
|
time: meta.time,
|
|
154
154
|
status: msg.status,
|
package/lib/response.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
const Emitter = require('events').EventEmitter ;
|
|
2
|
-
const sip = require('drachtio-sip') ;
|
|
3
2
|
const delegate = require('delegates') ;
|
|
4
3
|
const status_codes = require('sip-status') ;
|
|
5
4
|
const only = require('only') ;
|
|
6
5
|
const noop = require('node-noop').noop;
|
|
7
6
|
const assert = require('assert') ;
|
|
8
7
|
const debug = require('debug')('drachtio:response');
|
|
8
|
+
const SipMessage = require('./sip-parser/message') ;
|
|
9
9
|
class Response extends Emitter {
|
|
10
10
|
|
|
11
11
|
constructor(agent) {
|
|
12
12
|
super() ;
|
|
13
13
|
this._agent = agent ;
|
|
14
|
-
this.msg = new
|
|
14
|
+
this.msg = new SipMessage() ;
|
|
15
15
|
this.finished = false ;
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const only = require('only') ;
|
|
2
|
+
const parser = require('./parser') ;
|
|
3
|
+
|
|
4
|
+
class SipMessage {
|
|
5
|
+
constructor(msg) {
|
|
6
|
+
|
|
7
|
+
this.headers = {};
|
|
8
|
+
|
|
9
|
+
if (msg) {
|
|
10
|
+
if (typeof msg === 'string') {
|
|
11
|
+
this.raw = msg ;
|
|
12
|
+
const obj = parser.parseSipMessage(msg, true) ;
|
|
13
|
+
if (!obj) throw new Error('failed to parse sip message');
|
|
14
|
+
msg = obj;
|
|
15
|
+
}
|
|
16
|
+
Object.assign(this.headers, msg.headers || {});
|
|
17
|
+
Object.assign(this, only(msg, 'body method version status reason uri payload'));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get type() {
|
|
22
|
+
if (this.method) return 'request' ;
|
|
23
|
+
else if (this.status) return 'response' ;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get calledNumber() {
|
|
27
|
+
const user = this.uri.match(/sips?:(.*?)@/) ;
|
|
28
|
+
if (user && user.length > 1) {
|
|
29
|
+
return user[1].split(';')[0] ;
|
|
30
|
+
}
|
|
31
|
+
return '' ;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get callingNumber() {
|
|
35
|
+
const header = this.has('p-asserted-identity') ? this.get('p-asserted-identity') : this.get('from') ;
|
|
36
|
+
const user = header.match(/sips?:(.*?)@/) ;
|
|
37
|
+
if (user && user.length > 1) {
|
|
38
|
+
return user[1].split(';')[0] ;
|
|
39
|
+
}
|
|
40
|
+
return '' ;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get callingName() {
|
|
44
|
+
const header = this.has('p-asserted-identity') ? this.get('p-asserted-identity') : this.get('from') ;
|
|
45
|
+
const user = header.match(/^\"(.+)\"\s*<sips?:.+@/);
|
|
46
|
+
if (user && user.length > 1) {
|
|
47
|
+
return user[1];
|
|
48
|
+
}
|
|
49
|
+
return '';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get canFormDialog() {
|
|
53
|
+
return ('INVITE' === this.method || 'SUBSCRIBE' === this.method) && !this.get('to').tag ;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
set(hdr, value) {
|
|
57
|
+
const hdrs = {} ;
|
|
58
|
+
if (typeof hdr === 'string') hdrs[hdr] = value ;
|
|
59
|
+
else {
|
|
60
|
+
Object.assign(hdrs, hdr);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
Object.keys(hdrs).forEach((key) => {
|
|
64
|
+
const name = parser.getHeaderName(key) ;
|
|
65
|
+
const newValue = hdrs[key] ;
|
|
66
|
+
let v = '' ;
|
|
67
|
+
if (name in this.headers) {
|
|
68
|
+
v += this.headers[name] ;
|
|
69
|
+
v += ',' ;
|
|
70
|
+
}
|
|
71
|
+
v += newValue ;
|
|
72
|
+
this.headers[name] = v;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return this ;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get(hdr) {
|
|
79
|
+
if (this.has(hdr)) { return this.headers[parser.getHeaderName(hdr)] ; }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
has(hdr) {
|
|
83
|
+
const name = parser.getHeaderName(hdr) ;
|
|
84
|
+
return name in this.headers ;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
getParsedHeader(hdr) {
|
|
88
|
+
const name = parser.getHeaderName(hdr) ;
|
|
89
|
+
const v = this.headers[name] ;
|
|
90
|
+
const fn = parser.getParser(hdr.toLowerCase()) ;
|
|
91
|
+
return fn({s:v, i:0}) ;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
toString() {
|
|
95
|
+
return parser.stringifySipMessage(this) ;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
SipMessage.parseUri = parser.parseUri ;
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
exports = module.exports = SipMessage ;
|