UNPKG

51.9 kBJavaScriptView Raw
1// Copyright 2012 Mark Cavage, Inc. All rights reserved.
2
3'use strict';
4
5var domain = require('domain');
6var EventEmitter = require('events').EventEmitter;
7var http = require('http');
8var https = require('https');
9var util = require('util');
10
11var _ = require('lodash');
12var assert = require('assert-plus');
13var errors = require('restify-errors');
14var mime = require('mime');
15var spdy = require('spdy');
16var vasync = require('vasync');
17
18var Chain = require('./chain');
19var dtrace = require('./dtrace');
20var formatters = require('./formatters');
21var shallowCopy = require('./utils').shallowCopy;
22var upgrade = require('./upgrade');
23var deprecationWarnings = require('./deprecationWarnings');
24var customErrorTypes = require('./errorTypes');
25
26// Ensure these are loaded
27var patchRequest = require('./request');
28var patchResponse = require('./response');
29
30var http2;
31
32patchResponse(http.ServerResponse);
33patchRequest(http.IncomingMessage);
34
35///--- Globals
36
37var sprintf = util.format;
38
39///--- API
40
41/**
42 * Creates a new Server.
43 *
44 * @public
45 * @class
46 * @param {Object} options - an options object
47 * @param {String} options.name - Name of the server.
48 * @param {Boolean} [options.dtrace=false] - enable DTrace support
49 * @param {Router} options.router - Router
50 * @param {Object} options.log - [bunyan](https://github.com/trentm/node-bunyan)
51 * instance.
52 * @param {String} [options.url] - Once listen() is called, this will be filled
53 * in with where the server is running.
54 * @param {String|Buffer} [options.certificate] - If you want to create an HTTPS
55 * server, pass in a PEM-encoded certificate and key.
56 * @param {String|Buffer} [options.key] - If you want to create an HTTPS server,
57 * pass in a PEM-encoded certificate and key.
58 * @param {Object} [options.formatters] - Custom response formatters for
59 * `res.send()`.
60 * @param {Boolean} [options.handleUncaughtExceptions=false] - When true restify
61 * will use a domain to catch and respond to any uncaught
62 * exceptions that occur in it's handler stack.
63 * Comes with significant negative performance impact.
64 * [bunyan](https://github.com/trentm/node-bunyan) instance.
65 * response header, default is `restify`. Pass empty string to unset the header.
66 * @param {Object} [options.spdy] - Any options accepted by
67 * [node-spdy](https://github.com/indutny/node-spdy).
68 * @param {Object} [options.http2] - Any options accepted by
69 * [http2.createSecureServer](https://nodejs.org/api/http2.html).
70 * @param {Boolean} [options.handleUpgrades=false] - Hook the `upgrade` event
71 * from the node HTTP server, pushing `Connection: Upgrade` requests through the
72 * regular request handling chain.
73 * @param {Boolean} [options.onceNext=false] - Prevents calling next multiple
74 * times
75 * @param {Boolean} [options.strictNext=false] - Throws error when next() is
76 * called more than once, enabled onceNext option
77 * @param {Object} [options.httpsServerOptions] - Any options accepted by
78 * [node-https Server](http://nodejs.org/api/https.html#https_https).
79 * If provided the following restify server options will be ignored:
80 * spdy, ca, certificate, key, passphrase, rejectUnauthorized, requestCert and
81 * ciphers; however these can all be specified on httpsServerOptions.
82 * @param {Boolean} [options.noWriteContinue=false] - prevents
83 * `res.writeContinue()` in `server.on('checkContinue')` when proxing
84 * @param {Boolean} [options.ignoreTrailingSlash=false] - ignore trailing slash
85 * on paths
86 * @param {Boolean} [options.strictFormatters=true] - enables strict formatters
87 * behavior: a formatter matching the response's content-type is required. If
88 * not found, the response's content-type is automatically set to
89 * 'application/octet-stream'. If a formatter for that content-type is not
90 * found, sending the response errors.
91 * @example
92 * var restify = require('restify');
93 * var server = restify.createServer();
94 *
95 * server.listen(8080, function () {
96 * console.log('ready on %s', server.url);
97 * });
98 */
99function Server(options) {
100 assert.object(options, 'options');
101 assert.object(options.log, 'options.log');
102 assert.object(options.router, 'options.router');
103 assert.string(options.name, 'options.name');
104 assert.optionalBool(
105 options.handleUncaughtExceptions,
106 'options.handleUncaughtExceptions'
107 );
108 assert.optionalBool(options.dtrace, 'options.dtrace');
109 assert.optionalBool(options.socketio, 'options.socketio');
110 assert.optionalBool(options.onceNext, 'options.onceNext');
111 assert.optionalBool(options.strictNext, 'options.strictNext');
112 assert.optionalBool(options.strictFormatters, 'options.strictFormatters');
113
114 var self = this;
115
116 EventEmitter.call(this);
117
118 this.onceNext = !!options.onceNext;
119 this.strictNext = !!options.strictNext;
120 this.firstChain = [];
121 this.preChain = new Chain({
122 onceNext: this.onceNext,
123 strictNext: this.strictNext
124 });
125 this.useChain = new Chain({
126 onceNext: this.onceNext,
127 strictNext: this.strictNext
128 });
129 this.log = options.log;
130 this.name = options.name;
131 this.handleUncaughtExceptions = options.handleUncaughtExceptions || false;
132 this.router = options.router;
133 this.secure = false;
134 this.socketio = options.socketio || false;
135 this.dtrace = options.dtrace || false;
136 this._inflightRequests = 0;
137
138 this.strictFormatters = true;
139 if (options.strictFormatters !== undefined) {
140 this.strictFormatters = options.strictFormatters;
141 }
142
143 var fmt = mergeFormatters(options.formatters);
144 this.acceptable = fmt.acceptable;
145 this.formatters = fmt.formatters;
146 this.proxyEvents = [
147 'clientError',
148 'close',
149 'connection',
150 'error',
151 'listening',
152 'secureConnection'
153 ];
154
155 if (options.spdy) {
156 this.spdy = true;
157 this.server = spdy.createServer(options.spdy);
158 } else if (options.http2) {
159 // http2 module is not available < v8.4.0 (only with flag <= 8.8.0)
160 // load http2 module here to avoid experimental warning in other cases
161 if (!http2) {
162 try {
163 http2 = require('http2');
164 patchResponse(http2.Http2ServerResponse);
165 patchRequest(http2.Http2ServerRequest);
166 // eslint-disable-next-line no-empty
167 } catch (err) {}
168 }
169
170 assert(
171 http2,
172 'http2 module is not available, ' +
173 'upgrade your Node.js version to >= 8.8.0'
174 );
175
176 this.http2 = true;
177 this.server = http2.createSecureServer(options.http2);
178 } else if ((options.cert || options.certificate) && options.key) {
179 this.ca = options.ca;
180 this.certificate = options.certificate || options.cert;
181 this.key = options.key;
182 this.passphrase = options.passphrase || null;
183 this.secure = true;
184
185 this.server = https.createServer({
186 ca: self.ca,
187 cert: self.certificate,
188 key: self.key,
189 passphrase: self.passphrase,
190 rejectUnauthorized: options.rejectUnauthorized,
191 requestCert: options.requestCert,
192 ciphers: options.ciphers,
193 secureOptions: options.secureOptions
194 });
195 } else if (options.httpsServerOptions) {
196 this.server = https.createServer(options.httpsServerOptions);
197 } else {
198 this.server = http.createServer();
199 }
200
201 this.router.on('mount', this.emit.bind(this, 'mount'));
202
203 if (!options.handleUpgrades) {
204 this.proxyEvents.push('upgrade');
205 }
206 this.proxyEvents.forEach(function forEach(e) {
207 self.server.on(e, self.emit.bind(self, e));
208 });
209
210 // Now the things we can't blindly proxy
211 this.server.on('checkContinue', function onCheckContinue(req, res) {
212 if (self.listeners('checkContinue').length > 0) {
213 self.emit('checkContinue', req, res);
214 return;
215 }
216
217 if (!options.noWriteContinue) {
218 res.writeContinue();
219 }
220
221 self._onRequest(req, res);
222 });
223
224 if (options.handleUpgrades) {
225 this.server.on('upgrade', function onUpgrade(req, socket, head) {
226 req._upgradeRequest = true;
227 var res = upgrade.createResponse(req, socket, head);
228 self._onRequest(req, res);
229 });
230 }
231
232 this.server.on('request', this._onRequest.bind(this));
233
234 this.__defineGetter__('maxHeadersCount', function getMaxHeadersCount() {
235 return self.server.maxHeadersCount;
236 });
237
238 this.__defineSetter__('maxHeadersCount', function setMaxHeadersCount(c) {
239 self.server.maxHeadersCount = c;
240 return c;
241 });
242
243 this.__defineGetter__('url', function getUrl() {
244 if (self.socketPath) {
245 return 'http://' + self.socketPath;
246 }
247
248 var addr = self.address();
249 var str = '';
250
251 if (self.spdy) {
252 str += 'spdy://';
253 } else if (self.secure) {
254 str += 'https://';
255 } else {
256 str += 'http://';
257 }
258
259 if (addr) {
260 str +=
261 addr.family === 'IPv6'
262 ? '[' + addr.address + ']'
263 : addr.address;
264 str += ':';
265 str += addr.port;
266 } else {
267 str += '169.254.0.1:0000';
268 }
269
270 return str;
271 });
272
273 // print deprecation messages based on server configuration
274 deprecationWarnings(self);
275}
276util.inherits(Server, EventEmitter);
277
278module.exports = Server;
279
280///--- Server lifecycle methods
281
282// eslint-disable-next-line jsdoc/check-param-names
283/**
284 * Gets the server up and listening.
285 * Wraps node's
286 * [listen()](
287 * http://nodejs.org/docs/latest/api/net.html#net_server_listen_path_callback).
288 *
289 * @public
290 * @memberof Server
291 * @instance
292 * @function listen
293 * @throws {TypeError}
294 * @param {Number} port - Port
295 * @param {Number} [host] - Host
296 * @param {Function} [callback] - optionally get notified when listening.
297 * @returns {undefined} no return value
298 * @example
299 * <caption>You can call like:</caption>
300 * server.listen(80)
301 * server.listen(80, '127.0.0.1')
302 * server.listen('/tmp/server.sock')
303 */
304Server.prototype.listen = function listen() {
305 var args = Array.prototype.slice.call(arguments);
306 return this.server.listen.apply(this.server, args);
307};
308
309/**
310 * Shuts down this server, and invokes callback (optionally) when done.
311 * Wraps node's
312 * [close()](http://nodejs.org/docs/latest/api/net.html#net_event_close).
313 *
314 * @public
315 * @memberof Server
316 * @instance
317 * @function close
318 * @param {Function} [callback] - callback to invoke when done
319 * @returns {undefined} no return value
320 */
321Server.prototype.close = function close(callback) {
322 if (callback) {
323 assert.func(callback, 'callback');
324 }
325
326 this.server.once('close', function onClose() {
327 return callback ? callback() : false;
328 });
329
330 return this.server.close();
331};
332
333///--- Routing methods
334
335/**
336 * Server method opts
337 * @typedef {String|Regexp |Object} Server~methodOpts
338 * @type {Object}
339 * @property {String} name a name for the route
340 * @property {String} path can be any String accepted by
341 * [find-my-way](https://github.com/delvedor/find-my-way)
342 * @example
343 * // a static route
344 * server.get('/foo', function(req, res, next) {});
345 * // a parameterized route
346 * server.get('/foo/:bar', function(req, res, next) {});
347 * // a regular expression
348 * server.get('/example/:file(^\\d+).png', function(req, res, next) {});
349 * // an options object
350 * server.get({
351 * path: '/foo',
352 * }, function(req, res, next) {});
353 */
354
355/**
356 * Mounts a chain on the given path against this HTTP verb
357 *
358 * @public
359 * @memberof Server
360 * @instance
361 * @function get
362 * @param {Server~methodOpts} opts - if string, the URL to handle.
363 * if options, the URL to handle, at minimum.
364 * @returns {Route} the newly created route.
365 * @example
366 * server.get('/', function (req, res, next) {
367 * res.send({ hello: 'world' });
368 * next();
369 * });
370 */
371Server.prototype.get = serverMethodFactory('GET');
372
373/**
374 * Mounts a chain on the given path against this HTTP verb
375 *
376 * @public
377 * @memberof Server
378 * @instance
379 * @function head
380 * @param {Server~methodOpts} opts - if string, the URL to handle.
381 * if options, the URL to handle, at minimum.
382 * @returns {Route} the newly created route.
383 */
384Server.prototype.head = serverMethodFactory('HEAD');
385
386/**
387 * Mounts a chain on the given path against this HTTP verb
388 *
389 * @public
390 * @memberof Server
391 * @instance
392 * @function post
393 * @param {Server~methodOpts} post - if string, the URL to handle.
394 * if options, the URL to handle, at minimum.
395 * @returns {Route} the newly created route.
396 */
397Server.prototype.post = serverMethodFactory('POST');
398
399/**
400 * Mounts a chain on the given path against this HTTP verb
401 *
402 * @public
403 * @memberof Server
404 * @instance
405 * @function put
406 * @param {Server~methodOpts} put - if string, the URL to handle.
407 * if options, the URL to handle, at minimum.
408 * @returns {Route} the newly created route.
409 */
410Server.prototype.put = serverMethodFactory('PUT');
411
412/**
413 * Mounts a chain on the given path against this HTTP verb
414 *
415 * @public
416 * @memberof Server
417 * @instance
418 * @function patch
419 * @param {Server~methodOpts} patch - if string, the URL to handle.
420 * if options, the URL to handle, at minimum.
421 * @returns {Route} the newly created route.
422 */
423Server.prototype.patch = serverMethodFactory('PATCH');
424
425/**
426 * Mounts a chain on the given path against this HTTP verb
427 *
428 * @public
429 * @memberof Server
430 * @instance
431 * @function del
432 * @param {Server~methodOpts} opts - if string, the URL to handle.
433 * if options, the URL to handle, at minimum.
434 * @returns {Route} the newly created route.
435 */
436Server.prototype.del = serverMethodFactory('DELETE');
437
438/**
439 * Mounts a chain on the given path against this HTTP verb
440 *
441 * @public
442 * @memberof Server
443 * @instance
444 * @function opts
445 * @param {Server~methodOpts} opts - if string, the URL to handle.
446 * if options, the URL to handle, at minimum.
447 * @returns {Route} the newly created route.
448 */
449Server.prototype.opts = serverMethodFactory('OPTIONS');
450
451///--- Request lifecycle and middleware methods
452
453// eslint-disable-next-line jsdoc/check-param-names
454/**
455 * Gives you hooks to run _before_ any routes are located. This gives you
456 * a chance to intercept the request and change headers, etc., that routing
457 * depends on. Note that req.params will _not_ be set yet.
458 *
459 * @public
460 * @memberof Server
461 * @instance
462 * @function pre
463 * @param {...Function|Array} handler - Allows you to add handlers that
464 * run for all routes. *before* routing occurs.
465 * This gives you a hook to change request headers and the like if you need to.
466 * Note that `req.params` will be undefined, as that's filled in *after*
467 * routing.
468 * Takes a function, or an array of functions.
469 * variable number of nested arrays of handler functions
470 * @returns {Object} returns self
471 * @example
472 * server.pre(function(req, res, next) {
473 * req.headers.accept = 'application/json';
474 * return next();
475 * });
476 * @example
477 * <caption>For example, `pre()` can be used to deduplicate slashes in
478 * URLs</caption>
479 * server.pre(restify.pre.dedupeSlashes());
480 */
481Server.prototype.pre = function pre() {
482 var self = this;
483 var handlers = Array.prototype.slice.call(arguments);
484
485 argumentsToChain(handlers).forEach(function forEach(handler) {
486 handler._name = handler.name || 'pre-' + self.preChain.count();
487 self.preChain.add(handler);
488 });
489
490 return this;
491};
492
493// eslint-disable-next-line jsdoc/check-param-names
494/**
495 * Gives you hooks that run before restify touches a request. These hooks
496 * allow you to do processing early in the request/response life cycle without
497 * the overhead of the restify framework. You can not yield the event loop in
498 * this handler.
499 *
500 * The function handler accepts two parameters: req, res. If you want restify
501 * to ignore this request, return false from your handler. Return true or
502 * undefined to let restify continue handling the request.
503 *
504 * When false is returned, restify immediately stops handling the request. This
505 * means that no further middleware will be executed for any chain and routing
506 * will not occure. All request/response handling for an incoming request must
507 * be done inside the first handler if you intend to return false. This
508 * includes things like closing the response and returning a status code.
509 *
510 * The only work restify does for a first handler is to increment the number of
511 * inflightRequests prior to calling the chain, and decrement that value if the
512 * handler returns false. Returning anything other than true, false, undefined,
513 * or null will cause an exception to be thrown.
514 *
515 * Since server.first is designed to bypass the restify framework, there are
516 * naturally trade-offs you make when using this API:
517 * * Standard restify lifecycle events such as 'after' are not triggered for
518 * any request that you return false from a handler for
519 * * Invoking any of the restify req/res APIs from within a first handler is
520 * unspecified behavior, as the restify framework hasn't built up state for
521 * the request yet.
522 * * There are no request timers available at the time that the first chain
523 * runs.
524 * * And more! Beware doing anything with restify in these handlers. They are
525 * designed to give you similar access to the req/res as you would have if
526 * you were directly using node.js' http module, they are outside of the
527 * restify framework!
528 *
529 * @public
530 * @memberof Server
531 * @instance
532 * @function first
533 * @param {...Function} handler - Allows you to add handlers that
534 * run for all requests, *before* restify touches the request.
535 * This gives you a hook to change request headers and the like if you need to.
536 * Note that `req.params` will be undefined, as that's filled in *after*
537 * routing.
538
539 * Takes one or more functions.
540 * @returns {Object} returns self
541 * @example
542 * server.first(function(req, res) {
543 * if(server.inflightRequests() > 100) {
544 * res.statusCode = 503;
545 * res.end();
546 * return false
547 * }
548 * return true;
549 * })
550 */
551Server.prototype.first = function first() {
552 var args = Array.prototype.slice.call(arguments);
553 for (var i = 0; i < args.length; i++) {
554 assert.func(args[i]);
555 this.firstChain.push(args[i]);
556 }
557 return this;
558};
559
560// eslint-disable-next-line jsdoc/check-param-names
561/**
562 * Allows you to add in handlers that run for all routes. Note that handlers
563 * added
564 * via `use()` will run only after the router has found a matching route. If no
565 * match is found, these handlers will never run. Takes a function, or an array
566 * of functions.
567 *
568 * You can pass in any combination of functions or array of functions.
569 *
570 * @public
571 * @memberof Server
572 * @instance
573 * @function use
574 * @param {...Function|Array} handler - A variable number of handler functions
575 * * and/or a
576 * variable number of nested arrays of handler functions
577 * @returns {Object} returns self
578 */
579Server.prototype.use = function use() {
580 var self = this;
581 var handlers = Array.prototype.slice.call(arguments);
582
583 argumentsToChain(handlers).forEach(function forEach(handler) {
584 handler._name = handler.name || 'use-' + self.useChain.count();
585 self.useChain.add(handler);
586 });
587
588 return this;
589};
590
591/**
592 * Minimal port of the functionality offered by Express.js Route Param
593 * Pre-conditions
594 *
595 * This basically piggy-backs on the `server.use` method. It attaches a
596 * new middleware function that only fires if the specified parameter exists
597 * in req.params
598 *
599 * Exposes an API:
600 * server.param("user", function (req, res, next) {
601 * // load the user's information here, always making sure to call next()
602 * });
603 *
604 * @see http://expressjs.com/guide.html#route-param%20pre-conditions
605 * @public
606 * @memberof Server
607 * @instance
608 * @function param
609 * @param {String} name - The name of the URL param to respond to
610 * @param {Function} fn - The middleware function to execute
611 * @returns {Object} returns self
612 */
613Server.prototype.param = function param(name, fn) {
614 this.use(function _param(req, res, next) {
615 if (req.params && req.params.hasOwnProperty(name)) {
616 fn.call(this, req, res, next, req.params[name], name);
617 } else {
618 next();
619 }
620 });
621
622 return this;
623};
624
625/**
626 * Removes a route from the server.
627 * You pass in the route 'blob' you got from a mount call.
628 *
629 * @public
630 * @memberof Server
631 * @instance
632 * @function rm
633 * @throws {TypeError} on bad input.
634 * @param {String} routeName - the route name.
635 * @returns {Boolean} true if route was removed, false if not.
636 */
637Server.prototype.rm = function rm(routeName) {
638 var route = this.router.unmount(routeName);
639 return !!route;
640};
641
642///--- Info and debug methods
643
644/**
645 * Returns the server address.
646 * Wraps node's
647 * [address()](http://nodejs.org/docs/latest/api/net.html#net_server_address).
648 *
649 * @public
650 * @memberof Server
651 * @instance
652 * @function address
653 * @returns {Object} Address of server
654 * @example
655 * server.address()
656 * @example
657 * <caption>Output:</caption>
658 * { address: '::', family: 'IPv6', port: 8080 }
659 */
660Server.prototype.address = function address() {
661 return this.server.address();
662};
663
664/**
665 * Returns the number of inflight requests currently being handled by the server
666 *
667 * @public
668 * @memberof Server
669 * @instance
670 * @function inflightRequests
671 * @returns {number} number of inflight requests
672 */
673Server.prototype.inflightRequests = function inflightRequests() {
674 var self = this;
675 return self._inflightRequests;
676};
677
678/**
679 * Return debug information about the server.
680 *
681 * @public
682 * @memberof Server
683 * @instance
684 * @function debugInfo
685 * @returns {Object} debug info
686 * @example
687 * server.getDebugInfo()
688 * @example
689 * <caption>Output:</caption>
690 * {
691 * routes: [
692 * {
693 * name: 'get',
694 * method: 'get',
695 * input: '/',
696 * compiledRegex: /^[\/]*$/,
697 * compiledUrlParams: null,
698 * handlers: [Array]
699 * }
700 * ],
701 * server: {
702 * formatters: {
703 * 'application/javascript': [Function: formatJSONP],
704 * 'application/json': [Function: formatJSON],
705 * 'text/plain': [Function: formatText],
706 * 'application/octet-stream': [Function: formatBinary]
707 * },
708 * address: '::',
709 * port: 8080,
710 * inflightRequests: 0,
711 * pre: [],
712 * use: [ 'parseQueryString', '_jsonp' ],
713 * after: []
714 * }
715 * }
716 */
717Server.prototype.getDebugInfo = function getDebugInfo() {
718 var self = this;
719
720 // map an array of function to an array of function names
721 var funcNameMapper = function funcNameMapper(handler) {
722 if (handler.name === '') {
723 return 'anonymous';
724 } else {
725 return handler.name;
726 }
727 };
728
729 if (!self._debugInfo) {
730 var addressInfo = self.server.address();
731
732 // output the actual routes registered with restify
733 var routeInfo = self.router.getDebugInfo();
734
735 var preHandlers = self.preChain.getHandlers().map(funcNameMapper);
736 var useHandlers = self.useChain.getHandlers().map(funcNameMapper);
737
738 // get each route's handler chain
739 var routes = _.map(routeInfo, function mapValues(route) {
740 route.handlers = Array.prototype.concat.call(
741 // TODO: should it contain use handlers?
742 useHandlers,
743 route.handlers.map(funcNameMapper)
744 );
745 return route;
746 });
747
748 self._debugInfo = {
749 routes: routes,
750 server: {
751 formatters: self.formatters,
752 // if server is not yet listening, addressInfo may be null
753 address: addressInfo && addressInfo.address,
754 port: addressInfo && addressInfo.port,
755 inflightRequests: self.inflightRequests(),
756 pre: preHandlers,
757 use: useHandlers,
758 after: self.listeners('after').map(funcNameMapper)
759 }
760 };
761 }
762
763 return self._debugInfo;
764};
765
766/**
767 * toString() the server for easy reading/output.
768 *
769 * @public
770 * @memberof Server
771 * @instance
772 * @function toString
773 * @returns {String} stringified server
774 * @example
775 * server.toString()
776 * @example
777 * <caption>Output:</caption>
778 * Accepts: application/json, text/plain, application/octet-stream,
779 * application/javascript
780 * Name: restify
781 * Pre: []
782 * Router: RestifyRouter:
783 * DELETE: []
784 * GET: [get]
785 * HEAD: []
786 * OPTIONS: []
787 * PATCH: []
788 * POST: []
789 * PUT: []
790 *
791 * Routes:
792 * get: [parseQueryString, _jsonp, function]
793 * Secure: false
794 * Url: http://[::]:8080
795 * Version:
796 */
797Server.prototype.toString = function toString() {
798 var LINE_FMT = '\t%s: %s\n';
799 var SUB_LINE_FMT = '\t\t%s: %s\n';
800 var str = '';
801
802 function handlersToString(arr) {
803 var s =
804 '[' +
805 arr
806 .map(function map(b) {
807 return b.name || 'function';
808 })
809 .join(', ') +
810 ']';
811
812 return s;
813 }
814
815 str += sprintf(LINE_FMT, 'Accepts', this.acceptable.join(', '));
816 str += sprintf(LINE_FMT, 'Name', this.name);
817 str += sprintf(
818 LINE_FMT,
819 'Pre',
820 handlersToString(this.preChain.getHandlers())
821 );
822 str += sprintf(LINE_FMT, 'Router', '');
823 this.router
824 .toString()
825 .split('\n')
826 .forEach(function forEach(line) {
827 str += sprintf('\t\t%s\n', line);
828 });
829 str += sprintf(LINE_FMT, 'Routes', '');
830 _.forEach(this.router.getRoutes(), function forEach(route, routeName) {
831 var handlers = handlersToString(route.chain.getHandlers());
832 str += sprintf(SUB_LINE_FMT, routeName, handlers);
833 });
834 str += sprintf(LINE_FMT, 'Secure', this.secure);
835 str += sprintf(LINE_FMT, 'Url', this.url);
836
837 return str;
838};
839
840///--- Private methods
841
842// Lifecycle:
843//
844// 1. _onRequest (handle new request, setup request and triggers pre)
845// 2. _runPre
846// 3. _afterPre (runs after pre handlers are finisehd, triggers route)
847// 4. _runRoute (route lookup)
848// 5. _runUse (runs use handlers, if route exists)
849// 6. Runs route handlers
850// 7. _afterRoute (runs after route handlers are finised,
851// triggers use)
852// 8. _finishReqResCycle (on response "finish" and "error" events)
853//
854// Events:
855// e.1 after (triggered when response is flushed)
856//
857// Errors:
858// e.1 _onHandlerError (runs when next was called with an Error)
859// e.2 _routeErrorResponse
860// e.1 _onHandlerError (when, next('string') called, trigger route by name)
861// e.2 _afterRoute
862
863/**
864 * Setup request and calls _onRequest to run middlewares and call router
865 *
866 * @private
867 * @memberof Server
868 * @instance
869 * @function _onRequest
870 * @param {Object} req - the request object
871 * @param {Object} res - the response object
872 * @returns {undefined} no return value
873 * @fires Request,Response#request
874 */
875Server.prototype._onRequest = function _onRequest(req, res) {
876 var self = this;
877
878 // Increment the number of inflight requests prior to calling the firstChain
879 // handlers. This accomplishes two things. First, it gives earliest an
880 // accurate count of how many inflight requests there would be including
881 // this new request. Second, it intentionally winds up the inflight request
882 // accounting with the implementation of firstChain. Note how we increment
883 // here, but decrement down inside the for loop below. It's easy to end up
884 // with race conditions betwen inflight request accounting and inflight
885 // request load shedding, causing load shedding to reject/allow too many
886 // requests. The current implementation of firstChain is designed to
887 // remove those race conditions. By winding these implementations up with
888 // one another, it makes it clear that moving around the inflight request
889 // accounting will change the behavior of earliest.
890 self._inflightRequests++;
891
892 // Give the first chain the earliest possible opportunity to process
893 // this request before we do any work on it.
894 var firstChain = self.firstChain;
895 for (var i = 0; i < firstChain.length; i++) {
896 var handle = firstChain[i](req, res);
897 // Limit the range of values we will accept as return results of
898 // first handlers. This helps us maintain forward compatibility by
899 // ensuring users don't rely on undocumented/unspecified behavior.
900 assert.ok(
901 handle === true ||
902 handle === false ||
903 handle === undefined ||
904 handle === null,
905 'Return value of first[' +
906 i +
907 '] must be: ' +
908 'boolean, undefined, or null'
909 );
910 // If the first handler returns false, stop handling the request
911 // immediately.
912 if (handle === false) {
913 self._inflightRequests--;
914 return;
915 }
916 }
917
918 this.emit('request', req, res);
919
920 // Skip Socket.io endpoints
921 if (this.socketio && /^\/socket\.io.*/.test(req.url)) {
922 self._inflightRequests--;
923 return;
924 }
925
926 // Decorate req and res objects
927 self._setupRequest(req, res);
928
929 // Run in domain to catch async errors
930 // It has significant negative performance impact
931 // Warning: this feature depends on the deprecated domains module
932 if (self.handleUncaughtExceptions) {
933 var handlerDomain = domain.create();
934 handlerDomain.add(req);
935 handlerDomain.add(res);
936 handlerDomain.on('error', function onError(err) {
937 self._onHandlerError(err, req, res, true);
938 });
939 handlerDomain.run(function run() {
940 self._runPre(req, res);
941 });
942 } else {
943 self._runPre(req, res);
944 }
945};
946
947/**
948 * Run pre handlers
949 *
950 * @private
951 * @memberof Server
952 * @instance
953 * @function _runPre
954 * @param {Object} req - the request object
955 * @param {Object} res - the response object
956 * @returns {undefined} no return value
957 * @fires Request,Response#request
958 */
959Server.prototype._runPre = function _runPre(req, res) {
960 var self = this;
961
962 // emit 'pre' event before we run the pre handlers
963 self.emit('pre', req, res);
964
965 // Run "pre"
966 req._currentHandler = 'pre';
967 req._timePreStart = process.hrtime();
968
969 self.preChain.run(req, res, function preChainDone(err) {
970 // Execution time of a handler with error can be significantly lower
971 req._timePreEnd = process.hrtime();
972 self._afterPre(err, req, res);
973 });
974};
975
976/**
977 * After pre handlers finished
978 *
979 * @private
980 * @memberof Server
981 * @instance
982 * @function _afterPre
983 * @param {Error|false|undefined} err - pre handler error
984 * @param {Request} req - request
985 * @param {Response} res - response
986 * @returns {undefined} no return value
987 */
988Server.prototype._afterPre = function _afterPre(err, req, res) {
989 var self = this;
990
991 // Handle error
992 if (err) {
993 self._onHandlerError(err, req, res);
994 self._finishReqResCycle(req, res, err);
995 return;
996 }
997
998 // Stop
999 if (err === false) {
1000 self._onHandlerStop(req, res);
1001 return;
1002 }
1003
1004 self._runRoute(req, res);
1005};
1006
1007/**
1008 * Find route and run handlers
1009 *
1010 * @private
1011 * @memberof Server
1012 * @instance
1013 * @function _runRoute
1014 * @param {Object} req - the request object
1015 * @param {Object} res - the response object
1016 * @returns {undefined} no return value
1017 * @fires Request,Response#request
1018 */
1019Server.prototype._runRoute = function _runRoute(req, res) {
1020 var self = this;
1021
1022 var routeHandler = self.router.lookup(req, res);
1023
1024 if (!routeHandler) {
1025 self.router.defaultRoute(req, res, function afterRouter(err) {
1026 self._afterRoute(err, req, res);
1027 });
1028 return;
1029 }
1030
1031 // Emit routed
1032 self.emit('routed', req, res, req.route);
1033
1034 self._runUse(req, res, function afterUse() {
1035 // DTrace
1036 if (self.dtrace) {
1037 dtrace._rstfy_probes['route-start'].fire(function fire() {
1038 return [
1039 self.name,
1040 req.route.name,
1041 req._dtraceId,
1042 req.method,
1043 req.href(),
1044 req.headers
1045 ];
1046 });
1047 }
1048
1049 req._timeRouteStart = process.hrtime();
1050 routeHandler(req, res, function afterRouter(err) {
1051 // Execution time of a handler with error can be significantly lower
1052 req._timeRouteEnd = process.hrtime();
1053
1054 // DTrace
1055 if (self.dtrace) {
1056 dtrace._rstfy_probes['route-done'].fire(function fire() {
1057 return [
1058 self.name,
1059 req.route.name,
1060 req._dtraceId,
1061 res.statusCode || 200,
1062 res.headers
1063 ];
1064 });
1065 }
1066
1067 self._afterRoute(err, req, res);
1068 });
1069 });
1070};
1071
1072/**
1073 * After use handlers finished
1074 *
1075 * @private
1076 * @memberof Server
1077 * @instance
1078 * @function _afterRoute
1079 * @param {Error|false|undefined} err - use handler error
1080 * @param {Request} req - request
1081 * @param {Response} res - response
1082 * @returns {undefined} no return value
1083 */
1084Server.prototype._afterRoute = function _afterRoute(err, req, res) {
1085 var self = this;
1086
1087 res._handlersFinished = true;
1088
1089 // Handle error
1090 if (err) {
1091 self._onHandlerError(err, req, res);
1092 self._finishReqResCycle(req, res, err);
1093 return;
1094 }
1095
1096 // Trigger finish
1097 self._finishReqResCycle(req, res, err);
1098};
1099
1100/**
1101 * Run use handlers
1102 *
1103 * @private
1104 * @memberof Server
1105 * @instance
1106 * @function _runUse
1107 * @param {Object} req - the request object
1108 * @param {Object} res - the response object
1109 * @param {Function} next - next
1110 * @returns {undefined} no return value
1111 * @fires Request,Response#request
1112 */
1113Server.prototype._runUse = function _runUse(req, res, next) {
1114 var self = this;
1115
1116 // Run "use"
1117 req._currentHandler = 'use';
1118 req._timeUseStart = process.hrtime();
1119
1120 self.useChain.run(req, res, function useChainDone(err) {
1121 // Execution time of a handler with error can be significantly lower
1122 req._timeUseEnd = process.hrtime();
1123 self._afterUse(err, req, res, next);
1124 });
1125};
1126
1127/**
1128 * After use handlers finished
1129 *
1130 * @private
1131 * @memberof Server
1132 * @instance
1133 * @function _afterUse
1134 * @param {Error|false|undefined} err - use handler error
1135 * @param {Request} req - request
1136 * @param {Response} res - response
1137 * @param {Function} next - next
1138 * @returns {undefined} no return value
1139 */
1140Server.prototype._afterUse = function _afterUse(err, req, res, next) {
1141 var self = this;
1142
1143 // Handle error
1144 if (err) {
1145 self._onHandlerError(err, req, res);
1146 self._finishReqResCycle(req, res, err);
1147 return;
1148 }
1149
1150 // Stop
1151 if (err === false) {
1152 self._onHandlerStop(req, res);
1153 return;
1154 }
1155
1156 next();
1157};
1158
1159/**
1160 * Runs after next(false) is called
1161 *
1162 * @private
1163 * @memberof Server
1164 * @instance
1165 * @function _onHandlerStop
1166 * @param {Request} req - request
1167 * @param {Response} res - response
1168 * @returns {undefined} no return value
1169 */
1170Server.prototype._onHandlerStop = function _onHandlerStop(req, res) {
1171 res._handlersFinished = true;
1172 this._finishReqResCycle(req, res);
1173};
1174
1175/**
1176 * After route handlers finished
1177 * NOTE: only called when last handler calls next([err])
1178 *
1179 * @private
1180 * @memberof Server
1181 * @instance
1182 * @function _onHandlerError
1183 * @param {Error|String|undefined} err - router handler error or route name
1184 * @param {Request} req - request
1185 * @param {Response} res - response
1186 * @param {boolean} isUncaught - whether the error is uncaught
1187 * @returns {undefined} no return value
1188 */
1189Server.prototype._onHandlerError = function _onHandlerError(
1190 err,
1191 req,
1192 res,
1193 isUncaught
1194) {
1195 var self = this;
1196
1197 // Call route by name
1198 if (!isUncaught && _.isString(err)) {
1199 var routeName = err;
1200 var routeHandler = self.router.lookupByName(routeName, req, res);
1201
1202 // Cannot find route by name, called when next('route-name') doesn't
1203 // find any route, it's a 5xx error as it's a programatic error
1204 if (!routeHandler) {
1205 var routeByNameErr = new customErrorTypes.RouteMissingError(
1206 "Route by name doesn't exist"
1207 );
1208 routeByNameErr.code = 'ENOEXIST';
1209 self._afterRoute(routeByNameErr, req, res);
1210 return;
1211 }
1212 routeHandler(req, res, function afterRouter(routeErr) {
1213 self._afterRoute(routeErr, req, res);
1214 });
1215 return;
1216 }
1217
1218 // Handlers don't continue when error happen
1219 res._handlersFinished = true;
1220
1221 // Preserve handler err for finish event
1222 res.err = res.err || err;
1223
1224 // Error happened in router handlers
1225 self._routeErrorResponse(req, res, err, isUncaught);
1226};
1227
1228/**
1229 * Set up the request before routing and execution of handler chain functions.
1230 *
1231 * @private
1232 * @memberof Server
1233 * @instance
1234 * @function _setupRequest
1235 * @param {Object} req - the request object
1236 * @param {Object} res - the response object
1237 * @returns {undefined} no return value
1238 */
1239Server.prototype._setupRequest = function _setupRequest(req, res) {
1240 var self = this;
1241
1242 // Extend request
1243 req._dtraceId = dtrace.nextId();
1244 req.log = res.log = self.log;
1245 req._date = new Date();
1246 req._timeStart = process.hrtime();
1247 req.serverName = self.name;
1248 req.params = {};
1249 req.timers = [];
1250 req.dtrace = self.dtrace;
1251
1252 // Extend response
1253 res.acceptable = self.acceptable;
1254 res.formatters = self.formatters;
1255 res.req = req;
1256 res.serverName = self.name;
1257 res._handlersFinished = false;
1258 res._flushed = false;
1259 res._strictFormatters = this.strictFormatters;
1260
1261 // set header only if name isn't empty string
1262 if (self.name !== '') {
1263 res.setHeader('Server', self.name);
1264 }
1265
1266 // Request lifecycle events
1267
1268 // attach a listener for 'aborted' events, this will let us set
1269 // a flag so that we can stop processing the request if the client aborts
1270 // the connection (or we lose the connection).
1271 // we consider a closed request as flushed from metrics point of view
1272 function onReqAborted() {
1273 // Request was aborted, override the status code
1274 var err = new customErrorTypes.RequestCloseError();
1275 err.statusCode = 444;
1276
1277 // For backward compatibility we only set connection state to "close"
1278 // for RequestCloseError, also aborted is always immediatly followed
1279 // by a "close" event.
1280 // We don't set _connectionState to "close" in the happy path
1281 req._connectionState = 'close';
1282
1283 // Set status code and err for audit as req is already closed connection
1284 res.statusCode = err.statusCode;
1285 res.err = err;
1286 }
1287
1288 // Response lifecycle events
1289 function onResFinish() {
1290 var processHrTime = process.hrtime();
1291
1292 res._flushed = true;
1293 req._timeFlushed = processHrTime;
1294
1295 // Response may get flushed before handler callback is triggered
1296 req._timeFlushed = processHrTime;
1297 req._timePreEnd = req._timePreEnd || processHrTime;
1298 req._timeUseEnd = req._timeUseEnd || processHrTime;
1299 req._timeRouteEnd = req._timeRouteEnd || processHrTime;
1300
1301 // In Node < 10 "close" event dont fire always
1302 // https://github.com/nodejs/node/pull/20611
1303 self._finishReqResCycle(req, res);
1304 }
1305
1306 // We are handling when connection is being closed prematurely outside of
1307 // restify. It's not because the req is aborted.
1308 function onResClose() {
1309 res._flushed = true;
1310
1311 // Finish may already set the req._timeFlushed
1312 req._timeFlushed = req._timeFlushed || process.hrtime();
1313
1314 self._finishReqResCycle(req, res, res.err);
1315 }
1316
1317 // Request events
1318 req.once('aborted', onReqAborted);
1319
1320 // Response events
1321 res.once('finish', onResFinish);
1322 res.once('close', onResClose);
1323
1324 // attach a listener for the response's 'redirect' event
1325 res.on('redirect', function onRedirect(redirectLocation) {
1326 self.emit('redirect', redirectLocation);
1327 });
1328};
1329
1330/**
1331 * Maintaining the end of the request-response cycle:
1332 * - emitting after event
1333 * - updating inflight requests metrics
1334 * Check if the response is finished, and if not, wait for it before firing the
1335 * response object.
1336 *
1337 * @private
1338 * @memberof Server
1339 * @instance
1340 * @function _finishReqResCycle
1341 * @param {Object} req - the request object
1342 * @param {Object} res - the response object
1343 * @param {Object} [err] - a possible error as a result of failed route matching
1344 * or failed execution of the handler array.
1345 * @returns {undefined} no return value
1346 */
1347Server.prototype._finishReqResCycle = function _finishReqResCycle(
1348 req,
1349 res,
1350 err
1351) {
1352 var self = this;
1353 var route = req.route; // can be undefined when 404 or error
1354
1355 if (res._finished) {
1356 return;
1357 }
1358
1359 if (res._flushed && res._handlersFinished) {
1360 // decrement number of requests
1361 self._inflightRequests--;
1362 res._finished = true;
1363 req._timeFinished = process.hrtime();
1364
1365 // after event has signature of function(req, res, route, err) {...}
1366 var finalErr = err || res.err;
1367 req.emit('restifyDone', route, finalErr);
1368 self.emit('after', req, res, route, finalErr);
1369 } else {
1370 // Store error for when the response is flushed and we actually emit the
1371 // 'after' event. The "err" object passed to this method takes
1372 // precedence, but in case it's not set, "res.err" may have been already
1373 // set by another code path and we want to preserve it. The caveat thus
1374 // is that the 'after' event will be emitted with the latest error that
1375 // was set before the response is fully flushed. While not ideal, this
1376 // is on purpose and accepted as a reasonable trade-off for now.
1377 res.err = err || res.err;
1378 }
1379};
1380
1381/**
1382 * Helper function to, when on router error, emit error events and then
1383 * flush the err.
1384 *
1385 * @private
1386 * @memberof Server
1387 * @instance
1388 * @function _routeErrorResponse
1389 * @param {Request} req - the request object
1390 * @param {Response} res - the response object
1391 * @param {Error} err - error
1392 * @param {boolean} isUncaught - whether the error is uncaught
1393 * @returns {undefined} no return value
1394 */
1395Server.prototype._routeErrorResponse = function _routeErrorResponse(
1396 req,
1397 res,
1398 err,
1399 isUncaught
1400) {
1401 var self = this;
1402
1403 if (
1404 isUncaught &&
1405 self.handleUncaughtExceptions &&
1406 self.listenerCount('uncaughtException') > 1
1407 ) {
1408 self.emit(
1409 'uncaughtException',
1410 req,
1411 res,
1412 req.route,
1413 err,
1414 function uncaughtExceptionCompleted() {
1415 // We provide a callback to listeners of the 'uncaughtException'
1416 // event and we call _finishReqResCycle when that callback is
1417 // called so that, in case the actual request/response lifecycle
1418 // was completed _before_ the error was thrown or emitted, and
1419 // thus _before_ route handlers were marked as "finished", we
1420 // can still mark the req/res lifecycle as complete.
1421 // This edge case can occur when e.g. a client aborts a request
1422 // and the route handler that handles that request throws an
1423 // uncaught exception _after_ the request was aborted and the
1424 // response was closed.
1425 self._finishReqResCycle(req, res, err);
1426 }
1427 );
1428 return;
1429 }
1430
1431 self._emitErrorEvents(req, res, null, err, function emitError() {
1432 // Prevent double handling
1433 if (res._sent) {
1434 return;
1435 }
1436
1437 // only automatically send errors that are known (e.g., restify-errors)
1438 if (err instanceof Error && _.isNumber(err.statusCode)) {
1439 res.send(err);
1440 return;
1441 }
1442
1443 // if the thrown exception is not really an Error object, e.g.,
1444 // "throw 'foo';"
1445 // try to do best effort here to pass on that value by casting it to a
1446 // string. This should work even for falsy values like 0, false, null,
1447 // or undefined.
1448 res.send(new errors.InternalError(String(err)));
1449 });
1450};
1451
1452/**
1453 * Emit error events when errors are encountered either while attempting to
1454 * route the request (via router) or while executing the handler chain.
1455 *
1456 * @private
1457 * @memberof Server
1458 * @instance
1459 * @function _emitErrorEvents
1460 * @param {Object} req - the request object
1461 * @param {Object} res - the response object
1462 * @param {Object} route - the current route, if applicable
1463 * @param {Object} err - an error object
1464 * @param {Object} cb - callback function
1465 * @returns {undefined} no return value
1466 * @fires Error#restifyError
1467 */
1468Server.prototype._emitErrorEvents = function _emitErrorEvents(
1469 req,
1470 res,
1471 route,
1472 err,
1473 cb
1474) {
1475 var self = this;
1476 // Error can be of any type: undefined, Error, Number, etc. so we need
1477 // to protect ourselves from trying to resolve names from non Error objects
1478 var errName = err && err.name;
1479 var normalizedErrName = errName && errEvtNameFromError(err);
1480
1481 req.log.trace(
1482 {
1483 err: err,
1484 errName: normalizedErrName
1485 },
1486 'entering emitErrorEvents',
1487 errName
1488 );
1489
1490 var errEvtNames = [];
1491
1492 // if we have listeners for the specific error, fire those first.
1493 // if there's no error name, we should not emit an event
1494 if (normalizedErrName && self.listeners(normalizedErrName).length > 0) {
1495 errEvtNames.push(normalizedErrName);
1496 }
1497
1498 // or if we have a generic error listener. always fire generic error event
1499 // listener afterwards.
1500 if (self.listeners('restifyError').length > 0) {
1501 errEvtNames.push('restifyError');
1502 }
1503
1504 // kick off the async listeners
1505 return vasync.forEachPipeline(
1506 {
1507 inputs: errEvtNames,
1508 func: function emitError(errEvtName, vasyncCb) {
1509 self.emit(errEvtName, req, res, err, function emitErrDone() {
1510 // the error listener may return arbitrary objects, throw
1511 // them away and continue on. don't want vasync to take
1512 // that error and stop, we want to emit every single event.
1513 return vasyncCb();
1514 });
1515 }
1516 },
1517 // eslint-disable-next-line handle-callback-err
1518 function onResult(__, results) {
1519 // vasync will never return error here since we throw them away.
1520 return cb();
1521 }
1522 );
1523};
1524
1525///--- Helpers
1526
1527/**
1528 * Verify and flatten a nested array of request handlers.
1529 *
1530 * @private
1531 * @function argumentsToChain
1532 * @throws {TypeError}
1533 * @param {Function[]} handlers - pass through of funcs from server.[method]
1534 * @returns {Array} request handlers
1535 */
1536function argumentsToChain(handlers) {
1537 assert.array(handlers, 'handlers');
1538
1539 var chain = [];
1540
1541 // A recursive function for unwinding a nested array of handlers into a
1542 // single chain.
1543 function process(array) {
1544 for (var i = 0; i < array.length; i++) {
1545 if (Array.isArray(array[i])) {
1546 // Recursively call on nested arrays
1547 process(array[i]);
1548 continue;
1549 }
1550 // If an element of the array isn't an array, ensure it is a
1551 // handler function and then push it onto the chain of handlers
1552 assert.func(array[i], 'handler');
1553 chain.push(array[i]);
1554 }
1555
1556 return chain;
1557 }
1558
1559 // Return the chain, note that if `handlers` is an empty array, this will
1560 // return an empty array.
1561 return process(handlers);
1562}
1563
1564/**
1565 * merge optional formatters with the default formatters to create a single
1566 * formatters object. the passed in optional formatters object looks like:
1567 * formatters: {
1568 * 'application/foo': function formatFoo(req, res, body) {...}
1569 * }
1570 * @private
1571 * @function mergeFormatters
1572 * @param {Object} fmt user specified formatters object
1573 * @returns {Object}
1574 */
1575
1576function mergeFormatters(fmt) {
1577 var arr = [];
1578 var obj = {};
1579
1580 function addFormatter(src, k) {
1581 assert.func(src[k], 'formatter');
1582
1583 var q = 1.0; // RFC 2616 sec14 - The default value is q=1
1584 var t = k;
1585
1586 if (k.indexOf(';') !== -1) {
1587 var tmp = k.split(/\s*;\s*/);
1588 t = tmp[0];
1589
1590 if (tmp[1].indexOf('q=') !== -1) {
1591 q = parseFloat(tmp[1].split('=')[1]);
1592 }
1593 }
1594
1595 if (k.indexOf('/') === -1) {
1596 k = mime.getType(k);
1597 }
1598
1599 obj[t] = src[k];
1600 arr.push({
1601 q: q,
1602 t: t
1603 });
1604 }
1605
1606 Object.keys(formatters).forEach(addFormatter.bind(this, formatters));
1607 Object.keys(fmt || {}).forEach(addFormatter.bind(this, fmt || {}));
1608
1609 arr = arr
1610 .sort(function sort(a, b) {
1611 return b.q - a.q;
1612 })
1613 .map(function map(a) {
1614 return a.t;
1615 });
1616
1617 return {
1618 formatters: obj,
1619 acceptable: arr
1620 };
1621}
1622
1623/**
1624 * Map an Error's .name property into the actual event name that is emitted
1625 * by the restify server object.
1626 *
1627 * @function
1628 * @private errEvtNameFromError
1629 * @param {Object} err - an error object
1630 * @returns {String} an event name to emit
1631 */
1632function errEvtNameFromError(err) {
1633 if (err.name === 'ResourceNotFoundError') {
1634 // remap the name for router errors
1635 return 'NotFound';
1636 } else if (err.name === 'InvalidVersionError') {
1637 // remap the name for router errors
1638 return 'VersionNotAllowed';
1639 } else if (err.name) {
1640 return err.name.replace(/Error$/, '');
1641 }
1642 // If the err is not an Error, then just return an empty string
1643 return '';
1644}
1645
1646/**
1647 * Mounts a chain on the given path against this HTTP verb
1648 *
1649 * @private
1650 * @function serverMethodFactory
1651 * @param {String} method - name of the HTTP method
1652 * @returns {Function} factory
1653 */
1654function serverMethodFactory(method) {
1655 return function serverMethod(opts) {
1656 if (opts instanceof RegExp || typeof opts === 'string') {
1657 opts = {
1658 path: opts
1659 };
1660 } else if (typeof opts === 'object') {
1661 opts = shallowCopy(opts);
1662 } else {
1663 throw new TypeError('path (string) required');
1664 }
1665
1666 if (arguments.length < 2) {
1667 throw new TypeError('handler (function) required');
1668 }
1669
1670 opts.method = method;
1671 opts.path = opts.path || opts.url;
1672
1673 // We accept both a variable number of handler functions, a
1674 // variable number of nested arrays of handler functions, or a mix
1675 // of both
1676 var handlers = Array.prototype.slice.call(arguments, 1);
1677 var chain = argumentsToChain(handlers);
1678 var route = this.router.mount(opts, chain);
1679
1680 return route.name;
1681 };
1682}