UNPKG

5.14 kBJavaScriptView Raw
1const sipStatus = require('sip-status') ;
2const debug = require('debug')('connect:dispatcher');
3const Agent = require('./drachtio-agent') ;
4const EventEmitter = require('events').EventEmitter ;
5const delegate = require('delegates') ;
6
7const app = module.exports = {};
8
9app._init = function() {
10 const client = new Agent(this);
11 for (const prop in this.params) {
12 client.set(prop, this.params[prop]) ;
13 }
14 for (const method in this.routedMethods) {
15 client.route(method) ;
16 }
17
18 //propogate drachtio-client events to my listeners
19 ['connect', 'close', 'error', 'reconnecting', 'listening'].forEach((event) => {
20 client.on(event, (...args) => {
21 EventEmitter.prototype.emit.apply(this, [event].concat(args)) ;
22 }) ;
23 }) ;
24
25 this._cachedEvents.forEach((event) => {
26 app.on(event);
27 }) ;
28 this._cachedEvents = [] ;
29
30 //delegate some drachtio-client methods and accessors
31 delegate(this, 'client')
32 .method('request')
33 .method('disconnect')
34 .method('close')
35 .method('get')
36 .method('set')
37 .getter('idle') ;
38
39 return client ;
40};
41
42app.connect = function(...args) {
43 const client = this.client = this._init() ;
44 client.connect.apply(client, args);
45 return this ;
46};
47
48app.listen = function(...args) {
49 const client = this.client = this._init() ;
50 const server = client.listen.apply(client, args);
51 return server ;
52};
53
54app.endSession = function(socketHolder) {
55 if (this.client.isListening && socketHolder.socket) {
56 this.client.disconnect(socketHolder.socket);
57 }
58};
59app.request = function() {
60 throw new Error('cannot call app#request in unconnected state') ;
61} ;
62
63app.set = function(prop, value) {
64 this.params[prop] = value ;
65};
66
67app.get = function(prop) {
68 return this.params[prop] ;
69};
70
71
72/**
73 * Utilize the given middleware `handle` to the given `method`,
74 * defaulting to _*_, which means execute for all methods.
75 *
76 * @param {String|Function} method or callback
77 * @param {Function} callback
78 * @return {Server} for chaining
79 * @api public
80 */
81
82app.use = function(fn) {
83 let offset = 0 ;
84 let method = '*' ;
85
86 // disambiguate app.use([fn])
87 if (typeof fn !== 'function') {
88 let arg = fn;
89
90 while (Array.isArray(arg) && arg.length !== 0) {
91 arg = arg[0];
92 }
93
94 // first arg is the method
95 if (typeof arg !== 'function') {
96 offset = 1;
97 method = fn;
98 }
99 }
100
101 // if an array was provided, flatten it
102 const fns = [].concat(...Array.prototype.slice.call(arguments, offset));
103
104 if (fns.length === 0) throw new TypeError('app.use() requires middleware functions');
105
106 fns.forEach((fn) => {
107 // wrap sub-apps
108 if ('function' === typeof fn.handle) {
109 var server = fn;
110 fn.method = method;
111 fn = function(req, res, next) {
112 server.handle(req, res, next);
113 };
114 }
115
116 debug('use %s %s', method || '*', fn.name || 'anonymous');
117 this.stack.push({ method: method, handle: fn });
118 }) ;
119
120 if (typeof method === 'string' && method !== '*' && !(method in this.routedMethods)) {
121 this.routedMethods[method] = true ;
122 if (this.client) { this.client.route(method) ; }
123 }
124
125 return this;
126};
127
128/**
129 * Handle server requests, punting them down
130 * the middleware stack.
131 *
132 * @api private
133 */
134
135app.handle = function(req, res, out) {
136 const self = this;
137 const stack = this.stack ;
138 let index = 0;
139
140 debug(`handling request with method ${req.method}`);
141 req.app = this ;
142
143 function next(err) {
144 var layer;
145
146 // next callback
147 layer = stack[index++];
148
149 // all done
150 if (!layer || res.finalResponseSent) {
151 // delegate to parent
152 if (out) { return out(err); }
153
154 // unhandled error
155 if (err) {
156 // default to 500
157 var finalResponseSent = res.finalResponseSent ;
158
159 console.error('some layer barfed an error: ', err) ;
160 if (res.status < 400 || !req.status) { res.status = 500; }
161 debug(`default ${res.status}`);
162
163 // respect err.status
164 if (err.status) { res.status = err.status; }
165
166 // production gets a basic error message
167 var msg = sipStatus[res.status] ;
168
169 // log to stderr in a non-test env
170 console.error(err.stack || err.toString());
171 if (finalResponseSent) { return ; }
172 res.send(res.status, msg);
173 } else {
174 if (req.method === 'PRACK') {
175 res.send(200);
176 }
177 else if (req.method !== 'ACK') {
178 res.send(404) ;
179 self.endSession(res);
180 }
181 }
182 return;
183 }
184
185 try {
186
187 // skip this layer if the route doesn't match.
188 if (0 !== req.method.toLowerCase().indexOf(layer.method.toLowerCase()) &&
189 layer.method !== '*') { return next(err); }
190
191 debug('%s %s : %s', layer.handle.name || 'anonymous', layer.method, req.uri);
192 var arity = layer.handle.length;
193 if (err) {
194 if (arity === 4) {
195 layer.handle(err, req, res, next);
196 } else {
197 next(err);
198 }
199 } else if (arity < 4) {
200 layer.handle(req, res, next);
201 } else {
202 next();
203 }
204 } catch (e) {
205 console.error(e.stack) ;
206 next(e);
207 }
208 }
209 next();
210};