drachtio-srf 4.5.31 → 4.5.32
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/lib/@types/index.d.ts +116 -43
- package/lib/request.js +1 -0
- package/lib/response.js +1 -0
- package/lib/sip-parser/message.js +11 -5
- package/lib/srf.js +22 -7
- package/package.json +1 -1
- package/test/unit-tests/parser.js +10 -0
package/lib/@types/index.d.ts
CHANGED
|
@@ -1,69 +1,143 @@
|
|
|
1
1
|
declare module 'drachtio-srf' {
|
|
2
2
|
import { Socket } from 'net';
|
|
3
3
|
import { EventEmitter } from 'events';
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
type SipMethod = 'ACK' | 'BYE' | 'CANCEL' | 'INFO' | 'INVITE' | 'MESSAGE' | 'NOTIFY' | 'OPTIONS' | 'PRACK' | 'PUBLISH' | 'REFER' | 'REGISTER' | 'SUBSCRIBE' | 'UPDATE';
|
|
6
|
-
|
|
6
|
+
type SipMessageHeaders = Record<string, string>;
|
|
7
|
+
type AOR = { name: string; uri: string; params?: Record<string, any>; };
|
|
8
|
+
|
|
7
9
|
export interface SrfConfig {
|
|
8
10
|
apiSecret?: string;
|
|
9
11
|
host?: string;
|
|
10
12
|
port?: number;
|
|
11
13
|
secret?: string;
|
|
12
14
|
}
|
|
13
|
-
|
|
15
|
+
|
|
16
|
+
export function parseUri(uri: string): { user: string; host: string; params?: Record<string, any>; };
|
|
17
|
+
export function stringifyUri(uri: object): string;
|
|
18
|
+
|
|
14
19
|
export interface SipMessage {
|
|
15
|
-
|
|
16
|
-
raw: string;
|
|
20
|
+
type: "request" | "response";
|
|
17
21
|
body: string;
|
|
18
|
-
method: SipMethod;
|
|
19
|
-
version: string;
|
|
20
|
-
uri: string;
|
|
21
22
|
payload: object[];
|
|
23
|
+
source: "network" | "application";
|
|
24
|
+
source_address: string;
|
|
25
|
+
source_port: string;
|
|
26
|
+
protocol: string;
|
|
27
|
+
stackTime: string;
|
|
28
|
+
calledNumber: string;
|
|
29
|
+
callingNumber: string;
|
|
30
|
+
raw: string;
|
|
22
31
|
get(name: string): string;
|
|
32
|
+
has(name: string): boolean;
|
|
33
|
+
set(name: string, value: string);
|
|
34
|
+
getParsedHeader(name: "contact" | "Contact"): Array<AOR>;
|
|
35
|
+
getParsedHeader(name: "via" | "Via"): Array<Via>;
|
|
36
|
+
getParsedHeader(name: "To" | "to" | "From" | "from" | "refer-to" | "referred-by" | "p-asserted-identity" | "remote-party-id"): AOR;
|
|
37
|
+
getParsedHeader(name: string): string;
|
|
23
38
|
}
|
|
24
|
-
|
|
25
|
-
export interface SrfRequest {
|
|
26
|
-
headers: {[name: string]: any};
|
|
27
|
-
msg: any;
|
|
39
|
+
|
|
40
|
+
export interface SrfRequest extends SipMessage {
|
|
28
41
|
method: SipMethod;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
to: string;
|
|
32
|
-
callId: string;
|
|
42
|
+
isNewInvite(): boolean
|
|
43
|
+
cancel(callback: () => {})
|
|
33
44
|
branch: string;
|
|
45
|
+
callId: string;
|
|
46
|
+
from: string;
|
|
47
|
+
headers: Record<string, string>;
|
|
48
|
+
msg: any;
|
|
34
49
|
sdp: string;
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
srf: any;
|
|
51
|
+
to: string;
|
|
52
|
+
uri: string;
|
|
53
|
+
registration?: {
|
|
54
|
+
type: "unregister" | "register";
|
|
55
|
+
expires: number;
|
|
56
|
+
contact: Array<AOR>;
|
|
57
|
+
aor: string;
|
|
58
|
+
};
|
|
37
59
|
}
|
|
38
|
-
|
|
39
|
-
export interface SrfResponse {
|
|
40
|
-
headers: {[name: string]: any};
|
|
60
|
+
|
|
61
|
+
export interface SrfResponse extends SipMessage {
|
|
41
62
|
status: number;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
63
|
+
statusCode: number;
|
|
64
|
+
reason: string;
|
|
65
|
+
finalResponseSent: boolean;
|
|
66
|
+
send(status: number);
|
|
67
|
+
send(status: number, opts: object);
|
|
68
|
+
send(status: number, reason: string, opts: object);
|
|
69
|
+
send(status: number, reason: string, opts: object, callback: (err, msg) => {});
|
|
70
|
+
end();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface Dialog {
|
|
74
|
+
sip: { callId: string; localTag: string; remoteTag: string; };
|
|
75
|
+
onHold: boolean;
|
|
76
|
+
other: Dialog;
|
|
77
|
+
type: "uac" | "uas";
|
|
78
|
+
local: { uri: string; sdp: string; };
|
|
79
|
+
remote: { uri: string; sdp: string; };
|
|
80
|
+
req: SrfRequest;
|
|
81
|
+
destroy(opts?: { headers: Record<string, string>; }, callback?: (err, msg) => {}): void;
|
|
82
|
+
modify(sdp, callback?: (err: any, msg: SrfResponse) => void): void;
|
|
83
|
+
modify({ noAck: boolean }, callback?: (err: any, resp: any, resAck: any) => void): void;
|
|
84
|
+
ack(string): void;
|
|
85
|
+
on(messageType: "ack", callback: (msg: SrfResponse) => void): void;
|
|
86
|
+
on(messageType: "destroy", callback: (msg: SrfRequest) => void): void;
|
|
87
|
+
on(messageType: "info", callback: (req: SrfRequest, res: SrfResponse) => void): void;
|
|
88
|
+
on(messageType: "message", callback: (req: SrfRequest, res: SrfResponse) => void): void;
|
|
89
|
+
on(messageType: "modify", callback: (req: SrfRequest, res: SrfResponse) => void): void;
|
|
90
|
+
on(messageType: "notify", callback: (req: SrfRequest, res: SrfResponse) => void): void;
|
|
91
|
+
on(messageType: "options", callback: (req: SrfRequest, res: SrfResponse) => void): void;
|
|
92
|
+
on(messageType: "refer", callback: (req: SrfRequest, res: SrfResponse) => void): void;
|
|
93
|
+
on(messageType: "refresh", callback: (msg: SrfResponse) => void): void;
|
|
94
|
+
on(messageType: "update", callback: (req: SrfRequest, res: SrfResponse) => void): void;
|
|
95
|
+
on(messageType: "modify", callback: (req: SrfRequest, res: SrfResponse) => void): void;
|
|
96
|
+
once(messageType: string, callback: (msg: SrfResponse) => void): void;
|
|
97
|
+
listeners(messageType: string): any[];
|
|
98
|
+
request(opts?: { method: SipMethod; headers?: Record<string, string | number>; body?: string; }, callback?: (err, msg) => {});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface CreateUASOptions {
|
|
102
|
+
localSdp: string;
|
|
103
|
+
headers?: SipMessageHeaders;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface CreateUACOptions {
|
|
107
|
+
headers?: SipMessageHeaders;
|
|
108
|
+
uri?: string;
|
|
109
|
+
noAck?: boolean;
|
|
110
|
+
localSdp?: string;
|
|
111
|
+
proxy?: string;
|
|
112
|
+
auth?: { username: string; password: string; };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface CreateB2BUAOptions {
|
|
116
|
+
headers?: SipMessageHeaders;
|
|
117
|
+
responseHeaders?: SipMessageHeaders | ((uacRes: SipMessageHeaders, headers: SipMessageHeaders) => SipMessageHeaders | null);
|
|
118
|
+
localSdpA?: string | ((sdp: string, res: SrfResponse) => string | Promise<string>);
|
|
119
|
+
localSdpB?: string | ((sdp: string) => string | Promise<string>);
|
|
120
|
+
proxyRequestHeaders?: string[];
|
|
121
|
+
proxyResponseHeaders?: string[];
|
|
122
|
+
passFailure?: boolean;
|
|
123
|
+
passProvisionalResponses?: boolean;
|
|
124
|
+
proxy?: string;
|
|
125
|
+
auth?: { username: string; password: string; };
|
|
46
126
|
}
|
|
47
|
-
|
|
127
|
+
|
|
48
128
|
class Srf extends EventEmitter {
|
|
49
129
|
constructor();
|
|
50
130
|
constructor(tags: string | string[]);
|
|
51
131
|
connect(config?: SrfConfig): Promise<void>;
|
|
52
132
|
disconnect(): void;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
options(request: SrfRequest, options: any): void;
|
|
62
|
-
prack(request: SrfRequest, options: any): void;
|
|
63
|
-
publish(request: SrfRequest, options: any): void;
|
|
64
|
-
refer(request: SrfRequest, target: string, options: any): void;
|
|
65
|
-
subscribe(request: SrfRequest, target: string, options: any): void;
|
|
66
|
-
update(request: SrfRequest, options: any): void;
|
|
133
|
+
use(callback: (req: SrfRequest, res: SrfResponse, next: Function) => void): void;
|
|
134
|
+
use(messageType: string, callback: (req: SrfRequest, res: SrfResponse, next: Function) => void): void;
|
|
135
|
+
invite(callback: (req: SrfRequest, res: SrfResponse) => void): void;
|
|
136
|
+
request(uri: string, opts, method, [body], callback?: (err, requestSent: SrfRequest) => void);
|
|
137
|
+
proxyRequest(req: SrfRequest, destination: string | string[], [opts], callback?: (err, results) => {}): void;
|
|
138
|
+
createUAS(req: SrfRequest, res: SrfResponse, opts: CreateUASOptions, callback?: (err, dialog: Dialog) => void): Promise<Dialog>;
|
|
139
|
+
createUAC(uri: string | CreateUACOptions, opts?: CreateUACOptions, progressCallbacks?: { cbRequest?: (req: SrfRequest) => void; cbProvisional?: (provisionalRes: SrfResponse) => void; }, callback?: (err, dialog: Dialog) => void): Promise<Dialog>;
|
|
140
|
+
createB2BUA(req: SrfRequest, res: SrfResponse, uri: string, opts: CreateB2BUAOptions, progressCallbacks?: { cbRequest?: (req: SrfRequest) => void; cbProvisional?: (provisionalRes: Response) => void; cbFinalizedUac?: (uac: Dialog) => void; }, callback?: (err, dialog: Dialog) => {}): Promise<{ uas: Dialog; uac: Dialog }>;
|
|
67
141
|
on(event: 'connect', listener: (err: Error, hostPort: string) => void): this;
|
|
68
142
|
on(event: 'error', listener: (err: Error) => void): this;
|
|
69
143
|
on(event: 'disconnect', listener: () => void): this;
|
|
@@ -76,7 +150,6 @@ declare module 'drachtio-srf' {
|
|
|
76
150
|
locals: {[name: string]: any};
|
|
77
151
|
socket: Socket;
|
|
78
152
|
}
|
|
79
|
-
|
|
153
|
+
|
|
80
154
|
export default Srf
|
|
81
|
-
|
|
82
|
-
|
|
155
|
+
}
|
package/lib/request.js
CHANGED
package/lib/response.js
CHANGED
|
@@ -53,6 +53,11 @@ class SipMessage {
|
|
|
53
53
|
return ('INVITE' === this.method || 'SUBSCRIBE' === this.method) && !this.get('to').tag ;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
getHeaderName(hdr) {
|
|
57
|
+
const hdrLowerCase = hdr.toLowerCase();
|
|
58
|
+
return Object.keys(this.headers).find((h) => h.toLowerCase() === hdrLowerCase);
|
|
59
|
+
}
|
|
60
|
+
|
|
56
61
|
set(hdr, value) {
|
|
57
62
|
const hdrs = {} ;
|
|
58
63
|
if (typeof hdr === 'string') hdrs[hdr] = value ;
|
|
@@ -76,17 +81,18 @@ class SipMessage {
|
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
get(hdr) {
|
|
79
|
-
|
|
84
|
+
const headerName = this.getHeaderName(parser.getHeaderName(hdr));
|
|
85
|
+
if (headerName) {
|
|
86
|
+
return this.headers[headerName];
|
|
87
|
+
}
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
has(hdr) {
|
|
83
|
-
|
|
84
|
-
return name in this.headers ;
|
|
91
|
+
return !!this.getHeaderName(hdr);
|
|
85
92
|
}
|
|
86
93
|
|
|
87
94
|
getParsedHeader(hdr) {
|
|
88
|
-
const
|
|
89
|
-
const v = this.headers[name];
|
|
95
|
+
const v = this.get(hdr);
|
|
90
96
|
|
|
91
97
|
if (!v) {
|
|
92
98
|
throw new Error('header not available');
|
package/lib/srf.js
CHANGED
|
@@ -814,8 +814,14 @@ class Srf extends Emitter {
|
|
|
814
814
|
const reqHeaders = req.headers;
|
|
815
815
|
possiblyRemoveHeaders(proxyRequestHeaders.slice(1), reqHeaders);
|
|
816
816
|
copyAllHeaders(reqHeaders, opts.headers);
|
|
817
|
+
} else {
|
|
818
|
+
proxyRequestHeaders.forEach((hdr) => {
|
|
819
|
+
const headerName = req.getHeaderName(hdr);
|
|
820
|
+
if (headerName) {
|
|
821
|
+
opts.headers[headerName] = req.get(hdr);
|
|
822
|
+
}
|
|
823
|
+
});
|
|
817
824
|
}
|
|
818
|
-
else proxyRequestHeaders.forEach((hdr) => { if (req.has(hdr)) opts.headers[hdr] = req.get(hdr);}) ;
|
|
819
825
|
|
|
820
826
|
if (!(opts.headers.from || opts.headers.From) && !opts.callingNumber) { opts.callingNumber = req.callingNumber; }
|
|
821
827
|
if (!(opts.headers.from || opts.headers.From) && !opts.callingName) { opts.callingName = req.callingName; }
|
|
@@ -912,9 +918,10 @@ class Srf extends Emitter {
|
|
|
912
918
|
else {
|
|
913
919
|
proxyResponseHeaders.forEach((hdr) => {
|
|
914
920
|
debug(`copyUACHeadersToUAS: hdr ${hdr}`);
|
|
915
|
-
|
|
921
|
+
const headerName = uacRes.getHeaderName(hdr);
|
|
922
|
+
if (headerName) {
|
|
916
923
|
debug(`copyUACHeadersToUAS: adding ${hdr}: uacRes.get(hdr)`);
|
|
917
|
-
headers[
|
|
924
|
+
headers[headerName] = uacRes.get(hdr);
|
|
918
925
|
}
|
|
919
926
|
});
|
|
920
927
|
}
|
|
@@ -1221,8 +1228,11 @@ class Srf extends Emitter {
|
|
|
1221
1228
|
_b2bRequestWithinDialog(dlg, req, res, proxyRequestHeaders, proxyResponseHeaders, callback) {
|
|
1222
1229
|
callback = callback || noop ;
|
|
1223
1230
|
let headers = {} ;
|
|
1224
|
-
proxyRequestHeaders.forEach((
|
|
1225
|
-
|
|
1231
|
+
proxyRequestHeaders.forEach((hdr) => {
|
|
1232
|
+
const headerName = req.getHeaderName(hdr);
|
|
1233
|
+
if (headerName) {
|
|
1234
|
+
headers[headerName] = req.get(hdr);
|
|
1235
|
+
}
|
|
1226
1236
|
}) ;
|
|
1227
1237
|
dlg.request({
|
|
1228
1238
|
method: req.method,
|
|
@@ -1230,8 +1240,13 @@ class Srf extends Emitter {
|
|
|
1230
1240
|
body: req.body
|
|
1231
1241
|
}, (err, response) => {
|
|
1232
1242
|
headers = {} ;
|
|
1233
|
-
proxyResponseHeaders.forEach((
|
|
1234
|
-
if (!!response && response.has(
|
|
1243
|
+
proxyResponseHeaders.forEach((hdr) => {
|
|
1244
|
+
if (!!response && response.has(hdr)) {
|
|
1245
|
+
const headerName = response.getHeaderName(hdr);
|
|
1246
|
+
if (headerName) {
|
|
1247
|
+
headers[headerName] = response.get(hdr);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1235
1250
|
}) ;
|
|
1236
1251
|
|
|
1237
1252
|
if (err) {
|
package/package.json
CHANGED
|
@@ -40,6 +40,16 @@ describe('Parser', function () {
|
|
|
40
40
|
msg.set('From', '<sip:daveh@localhost>;tag=1234');
|
|
41
41
|
msg.get('from').should.eql('<sip:daveh@localhost>;tag=1234');
|
|
42
42
|
});
|
|
43
|
+
it('getting a private header should be case insensitive', function () {
|
|
44
|
+
var msg = new SipMessage();
|
|
45
|
+
msg.set('P-Called-Party-ID', '"Dave" <sip:daveh@localhost>');
|
|
46
|
+
msg.get('p-called-party-id').should.eql('"Dave" <sip:daveh@localhost>');
|
|
47
|
+
});
|
|
48
|
+
it('getting a custom header should be case insensitive', function () {
|
|
49
|
+
var msg = new SipMessage();
|
|
50
|
+
msg.set('X-Foo', 'bar');
|
|
51
|
+
msg.get('x-foo').should.eql('bar');
|
|
52
|
+
});
|
|
43
53
|
it('should not parse a header when not available', function () {
|
|
44
54
|
var msg = new SipMessage();
|
|
45
55
|
should.throws(msg.getParsedHeader.bind(msg, 'contact'));
|