UNPKG

53.1 kBJavaScriptView Raw
1// Copyright 2012 Mark Cavage, Inc. All rights reserved.
2
3'use strict';
4
5var EventEmitter = require('events').EventEmitter;
6var http = require('http');
7var https = require('https');
8var util = require('util');
9
10var _ = require('lodash');
11var assert = require('assert-plus');
12var errors = require('restify-errors');
13var mime = require('mime');
14var spdy = require('spdy');
15var vasync = require('vasync');
16
17var Chain = require('./chain');
18var dtrace = require('./dtrace');
19var formatters = require('./formatters');
20var shallowCopy = require('./utils').shallowCopy;
21var upgrade = require('./upgrade');
22var deprecationWarnings = require('./deprecationWarnings');
23var customErrorTypes = require('./errorTypes');
24
25// Ensure these are loaded
26var patchRequest = require('./request');
27var patchResponse = require('./response');
28
29var domain;
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 // In Node v12.x requiring the domain module has a negative performance
934 // impact. As using domains in restify is optional and only required
935 // with the `handleUncaughtExceptions` options, we apply a singleton
936 // pattern to avoid any performance regression in the default scenario.
937 if (!domain) {
938 domain = require('domain');
939 }
940
941 var handlerDomain = domain.create();
942 handlerDomain.add(req);
943 handlerDomain.add(res);
944 handlerDomain.on('error', function onError(err) {
945 self._onHandlerError(err, req, res, true);
946 });
947 handlerDomain.run(function run() {
948 self._runPre(req, res);
949 });
950 } else {
951 self._runPre(req, res);
952 }
953};
954
955/**
956 * Run pre handlers
957 *
958 * @private
959 * @memberof Server
960 * @instance
961 * @function _runPre
962 * @param {Object} req - the request object
963 * @param {Object} res - the response object
964 * @returns {undefined} no return value
965 * @fires Request,Response#request
966 */
967Server.prototype._runPre = function _runPre(req, res) {
968 var self = this;
969
970 // emit 'pre' event before we run the pre handlers
971 self.emit('pre', req, res);
972
973 // Run "pre"
974 req._currentHandler = 'pre';
975 req._timePreStart = process.hrtime();
976
977 self.preChain.run(req, res, function preChainDone(err) {
978 // Execution time of a handler with error can be significantly lower
979 req._timePreEnd = process.hrtime();
980 self._afterPre(err, req, res);
981 });
982};
983
984/**
985 * After pre handlers finished
986 *
987 * @private
988 * @memberof Server
989 * @instance
990 * @function _afterPre
991 * @param {Error|false|undefined} err - pre handler error
992 * @param {Request} req - request
993 * @param {Response} res - response
994 * @returns {undefined} no return value
995 */
996Server.prototype._afterPre = function _afterPre(err, req, res) {
997 var self = this;
998
999 // Handle error
1000 if (err) {
1001 self._onHandlerError(err, req, res);
1002 self._finishReqResCycle(req, res, err);
1003 return;
1004 }
1005
1006 // Stop
1007 if (err === false) {
1008 self._onHandlerStop(req, res);
1009 return;
1010 }
1011
1012 self._runRoute(req, res);
1013};
1014
1015/**
1016 * Find route and run handlers
1017 *
1018 * @private
1019 * @memberof Server
1020 * @instance
1021 * @function _runRoute
1022 * @param {Object} req - the request object
1023 * @param {Object} res - the response object
1024 * @returns {undefined} no return value
1025 * @fires Request,Response#request
1026 */
1027Server.prototype._runRoute = function _runRoute(req, res) {
1028 var self = this;
1029
1030 var routeHandler = self.router.lookup(req, res);
1031
1032 if (!routeHandler) {
1033 self.router.defaultRoute(req, res, function afterRouter(err) {
1034 self._afterRoute(err, req, res);
1035 });
1036 return;
1037 }
1038
1039 // Emit routed
1040 self.emit('routed', req, res, req.route);
1041
1042 self._runUse(req, res, function afterUse() {
1043 // DTrace
1044 if (self.dtrace) {
1045 dtrace._rstfy_probes['route-start'].fire(function fire() {
1046 return [
1047 self.name,
1048 req.route.name,
1049 req._dtraceId,
1050 req.method,
1051 req.href(),
1052 req.headers
1053 ];
1054 });
1055 }
1056
1057 req._timeRouteStart = process.hrtime();
1058 routeHandler(req, res, function afterRouter(err) {
1059 // Execution time of a handler with error can be significantly lower
1060 req._timeRouteEnd = process.hrtime();
1061
1062 // DTrace
1063 if (self.dtrace) {
1064 dtrace._rstfy_probes['route-done'].fire(function fire() {
1065 return [
1066 self.name,
1067 req.route.name,
1068 req._dtraceId,
1069 res.statusCode || 200,
1070 res.headers
1071 ];
1072 });
1073 }
1074
1075 self._afterRoute(err, req, res);
1076 });
1077 });
1078};
1079
1080/**
1081 * After use handlers finished
1082 *
1083 * @private
1084 * @memberof Server
1085 * @instance
1086 * @function _afterRoute
1087 * @param {Error|false|undefined} err - use handler error
1088 * @param {Request} req - request
1089 * @param {Response} res - response
1090 * @returns {undefined} no return value
1091 */
1092Server.prototype._afterRoute = function _afterRoute(err, req, res) {
1093 var self = this;
1094
1095 res._handlersFinished = true;
1096
1097 // Handle error
1098 if (err) {
1099 self._onHandlerError(err, req, res);
1100 self._finishReqResCycle(req, res, err);
1101 return;
1102 }
1103
1104 // Trigger finish
1105 self._finishReqResCycle(req, res, err);
1106};
1107
1108/**
1109 * Run use handlers
1110 *
1111 * @private
1112 * @memberof Server
1113 * @instance
1114 * @function _runUse
1115 * @param {Object} req - the request object
1116 * @param {Object} res - the response object
1117 * @param {Function} next - next
1118 * @returns {undefined} no return value
1119 * @fires Request,Response#request
1120 */
1121Server.prototype._runUse = function _runUse(req, res, next) {
1122 var self = this;
1123
1124 // Run "use"
1125 req._currentHandler = 'use';
1126 req._timeUseStart = process.hrtime();
1127
1128 self.useChain.run(req, res, function useChainDone(err) {
1129 // Execution time of a handler with error can be significantly lower
1130 req._timeUseEnd = process.hrtime();
1131 self._afterUse(err, req, res, next);
1132 });
1133};
1134
1135/**
1136 * After use handlers finished
1137 *
1138 * @private
1139 * @memberof Server
1140 * @instance
1141 * @function _afterUse
1142 * @param {Error|false|undefined} err - use handler error
1143 * @param {Request} req - request
1144 * @param {Response} res - response
1145 * @param {Function} next - next
1146 * @returns {undefined} no return value
1147 */
1148Server.prototype._afterUse = function _afterUse(err, req, res, next) {
1149 var self = this;
1150
1151 // Handle error
1152 if (err) {
1153 self._onHandlerError(err, req, res);
1154 self._finishReqResCycle(req, res, err);
1155 return;
1156 }
1157
1158 // Stop
1159 if (err === false) {
1160 self._onHandlerStop(req, res);
1161 return;
1162 }
1163
1164 next();
1165};
1166
1167/**
1168 * Runs after next(false) is called
1169 *
1170 * @private
1171 * @memberof Server
1172 * @instance
1173 * @function _onHandlerStop
1174 * @param {Request} req - request
1175 * @param {Response} res - response
1176 * @returns {undefined} no return value
1177 */
1178Server.prototype._onHandlerStop = function _onHandlerStop(req, res) {
1179 res._handlersFinished = true;
1180 this._finishReqResCycle(req, res);
1181};
1182
1183/**
1184 * After route handlers finished
1185 * NOTE: only called when last handler calls next([err])
1186 *
1187 * @private
1188 * @memberof Server
1189 * @instance
1190 * @function _onHandlerError
1191 * @param {Error|String|undefined} err - router handler error or route name
1192 * @param {Request} req - request
1193 * @param {Response} res - response
1194 * @param {boolean} isUncaught - whether the error is uncaught
1195 * @returns {undefined} no return value
1196 */
1197Server.prototype._onHandlerError = function _onHandlerError(
1198 err,
1199 req,
1200 res,
1201 isUncaught
1202) {
1203 var self = this;
1204
1205 // Call route by name
1206 if (!isUncaught && _.isString(err)) {
1207 var routeName = err;
1208 var routeHandler = self.router.lookupByName(routeName, req, res);
1209
1210 // Cannot find route by name, called when next('route-name') doesn't
1211 // find any route, it's a 5xx error as it's a programatic error
1212 if (!routeHandler) {
1213 var routeByNameErr = new customErrorTypes.RouteMissingError(
1214 "Route by name doesn't exist"
1215 );
1216 routeByNameErr.code = 'ENOEXIST';
1217 self._afterRoute(routeByNameErr, req, res);
1218 return;
1219 }
1220 routeHandler(req, res, function afterRouter(routeErr) {
1221 self._afterRoute(routeErr, req, res);
1222 });
1223 return;
1224 }
1225
1226 // Handlers don't continue when error happen
1227 res._handlersFinished = true;
1228
1229 // Preserve handler err for finish event
1230 res.err = res.err || err;
1231
1232 // Error happened in router handlers
1233 self._routeErrorResponse(req, res, err, isUncaught);
1234};
1235
1236/**
1237 * Set up the request before routing and execution of handler chain functions.
1238 *
1239 * @private
1240 * @memberof Server
1241 * @instance
1242 * @function _setupRequest
1243 * @param {Object} req - the request object
1244 * @param {Object} res - the response object
1245 * @returns {undefined} no return value
1246 */
1247Server.prototype._setupRequest = function _setupRequest(req, res) {
1248 var self = this;
1249
1250 // Extend request
1251 req._dtraceId = dtrace.nextId();
1252 req.log = res.log = self.log;
1253 req._date = new Date();
1254 req._timeStart = process.hrtime();
1255 req.serverName = self.name;
1256 req.params = {};
1257 req.timers = [];
1258 req.dtrace = self.dtrace;
1259
1260 // Extend response
1261 res.acceptable = self.acceptable;
1262 res.formatters = self.formatters;
1263 res.req = req;
1264 res.serverName = self.name;
1265 res._handlersFinished = false;
1266 res._flushed = false;
1267 res._strictFormatters = this.strictFormatters;
1268
1269 // set header only if name isn't empty string
1270 if (self.name !== '') {
1271 res.setHeader('Server', self.name);
1272 }
1273
1274 // Request lifecycle events
1275
1276 // attach a listener for 'aborted' events, this will let us set
1277 // a flag so that we can stop processing the request if the client aborts
1278 // the connection (or we lose the connection).
1279 // we consider a closed request as flushed from metrics point of view
1280 function onReqAborted() {
1281 // Request was aborted, override the status code
1282 var err = new customErrorTypes.RequestCloseError();
1283 err.statusCode = 444;
1284
1285 // For backward compatibility we only set connection state to "close"
1286 // for RequestCloseError, also aborted is always immediatly followed
1287 // by a "close" event.
1288 // We don't set _connectionState to "close" in the happy path
1289 req._connectionState = 'close';
1290
1291 // Set status code and err for audit as req is already closed connection
1292 res.statusCode = err.statusCode;
1293 res.err = err;
1294 }
1295
1296 // Response lifecycle events
1297 function onResFinish() {
1298 var processHrTime = process.hrtime();
1299
1300 res._flushed = true;
1301 req._timeFlushed = processHrTime;
1302
1303 // Response may get flushed before handler callback is triggered
1304 req._timeFlushed = processHrTime;
1305 req._timePreEnd = req._timePreEnd || processHrTime;
1306 req._timeUseEnd = req._timeUseEnd || processHrTime;
1307 req._timeRouteEnd = req._timeRouteEnd || processHrTime;
1308
1309 // In Node < 10 "close" event dont fire always
1310 // https://github.com/nodejs/node/pull/20611
1311 self._finishReqResCycle(req, res);
1312 }
1313
1314 // We are handling when connection is being closed prematurely outside of
1315 // restify. It's not because the req is aborted.
1316 function onResClose() {
1317 res._flushed = true;
1318
1319 // Finish may already set the req._timeFlushed
1320 req._timeFlushed = req._timeFlushed || process.hrtime();
1321
1322 self._finishReqResCycle(req, res, res.err);
1323 }
1324
1325 // Request events
1326 req.once('aborted', onReqAborted);
1327
1328 // Response events
1329 res.once('finish', onResFinish);
1330 res.once('close', onResClose);
1331
1332 // attach a listener for the response's 'redirect' event
1333 res.on('redirect', function onRedirect(redirectLocation) {
1334 self.emit('redirect', redirectLocation);
1335 });
1336};
1337
1338/**
1339 * Maintaining the end of the request-response cycle:
1340 * - emitting after event
1341 * - updating inflight requests metrics
1342 * Check if the response is finished, and if not, wait for it before firing the
1343 * response object.
1344 *
1345 * @private
1346 * @memberof Server
1347 * @instance
1348 * @function _finishReqResCycle
1349 * @param {Object} req - the request object
1350 * @param {Object} res - the response object
1351 * @param {Object} [err] - a possible error as a result of failed route matching
1352 * or failed execution of the handler array.
1353 * @returns {undefined} no return value
1354 */
1355Server.prototype._finishReqResCycle = function _finishReqResCycle(
1356 req,
1357 res,
1358 err
1359) {
1360 var self = this;
1361 var route = req.route; // can be undefined when 404 or error
1362
1363 // if the returned err value was a string, then we're handling the
1364 // next('foo') case where we redirect to another middleware stack. don't
1365 // do anything here because we're not done yet.
1366 if (res._finished || _.isString(err)) {
1367 return;
1368 }
1369
1370 if (res._flushed && res._handlersFinished) {
1371 // decrement number of requests
1372 self._inflightRequests--;
1373 res._finished = true;
1374 req._timeFinished = process.hrtime();
1375
1376 // after event has signature of function(req, res, route, err) {...}
1377 var finalErr = err || res.err;
1378 req.emit('restifyDone', route, finalErr);
1379 self.emit('after', req, res, route, finalErr);
1380 } else if (
1381 res._handlersFinished === true &&
1382 res.headersSent === false &&
1383 !res.err
1384 ) {
1385 // if we reached the end of the handler chain and headers haven't been
1386 // sent AND there isn't an existing res.err (e.g., req abort/close),
1387 // it's possible it's a user error and a response was never written.
1388 // send a 500.
1389 res.send(
1390 new errors.InternalServerError(
1391 'reached the end of the handler chain without ' +
1392 'writing a response!'
1393 )
1394 );
1395 return;
1396 } else {
1397 // Store error for when the response is flushed and we actually emit the
1398 // 'after' event. The "err" object passed to this method takes
1399 // precedence, but in case it's not set, "res.err" may have been already
1400 // set by another code path and we want to preserve it. The caveat thus
1401 // is that the 'after' event will be emitted with the latest error that
1402 // was set before the response is fully flushed. While not ideal, this
1403 // is on purpose and accepted as a reasonable trade-off for now.
1404 res.err = err || res.err;
1405 }
1406};
1407
1408/**
1409 * Helper function to, when on router error, emit error events and then
1410 * flush the err.
1411 *
1412 * @private
1413 * @memberof Server
1414 * @instance
1415 * @function _routeErrorResponse
1416 * @param {Request} req - the request object
1417 * @param {Response} res - the response object
1418 * @param {Error} err - error
1419 * @param {boolean} isUncaught - whether the error is uncaught
1420 * @returns {undefined} no return value
1421 */
1422Server.prototype._routeErrorResponse = function _routeErrorResponse(
1423 req,
1424 res,
1425 err,
1426 isUncaught
1427) {
1428 var self = this;
1429
1430 if (
1431 isUncaught &&
1432 self.handleUncaughtExceptions &&
1433 self.listenerCount('uncaughtException') > 1
1434 ) {
1435 self.emit(
1436 'uncaughtException',
1437 req,
1438 res,
1439 req.route,
1440 err,
1441 function uncaughtExceptionCompleted() {
1442 // We provide a callback to listeners of the 'uncaughtException'
1443 // event and we call _finishReqResCycle when that callback is
1444 // called so that, in case the actual request/response lifecycle
1445 // was completed _before_ the error was thrown or emitted, and
1446 // thus _before_ route handlers were marked as "finished", we
1447 // can still mark the req/res lifecycle as complete.
1448 // This edge case can occur when e.g. a client aborts a request
1449 // and the route handler that handles that request throws an
1450 // uncaught exception _after_ the request was aborted and the
1451 // response was closed.
1452 self._finishReqResCycle(req, res, err);
1453 }
1454 );
1455 return;
1456 }
1457
1458 self._emitErrorEvents(req, res, null, err, function emitError() {
1459 // Prevent double handling
1460 if (res._sent) {
1461 return;
1462 }
1463
1464 // only automatically send errors that are known (e.g., restify-errors)
1465 if (err instanceof Error && _.isNumber(err.statusCode)) {
1466 res.send(err);
1467 return;
1468 }
1469
1470 // if the thrown exception is not really an Error object, e.g.,
1471 // "throw 'foo';"
1472 // try to do best effort here to pass on that value by casting it to a
1473 // string. This should work even for falsy values like 0, false, null,
1474 // or undefined.
1475 res.send(new errors.InternalError(String(err)));
1476 });
1477};
1478
1479/**
1480 * Emit error events when errors are encountered either while attempting to
1481 * route the request (via router) or while executing the handler chain.
1482 *
1483 * @private
1484 * @memberof Server
1485 * @instance
1486 * @function _emitErrorEvents
1487 * @param {Object} req - the request object
1488 * @param {Object} res - the response object
1489 * @param {Object} route - the current route, if applicable
1490 * @param {Object} err - an error object
1491 * @param {Object} cb - callback function
1492 * @returns {undefined} no return value
1493 * @fires Error#restifyError
1494 */
1495Server.prototype._emitErrorEvents = function _emitErrorEvents(
1496 req,
1497 res,
1498 route,
1499 err,
1500 cb
1501) {
1502 var self = this;
1503 // Error can be of any type: undefined, Error, Number, etc. so we need
1504 // to protect ourselves from trying to resolve names from non Error objects
1505 var errName = err && err.name;
1506 var normalizedErrName = errName && errEvtNameFromError(err);
1507
1508 req.log.trace(
1509 {
1510 err: err,
1511 errName: normalizedErrName
1512 },
1513 'entering emitErrorEvents',
1514 errName
1515 );
1516
1517 var errEvtNames = [];
1518
1519 // if we have listeners for the specific error, fire those first.
1520 // if there's no error name, we should not emit an event
1521 if (normalizedErrName && self.listeners(normalizedErrName).length > 0) {
1522 errEvtNames.push(normalizedErrName);
1523 }
1524
1525 // or if we have a generic error listener. always fire generic error event
1526 // listener afterwards.
1527 if (self.listeners('restifyError').length > 0) {
1528 errEvtNames.push('restifyError');
1529 }
1530
1531 // kick off the async listeners
1532 return vasync.forEachPipeline(
1533 {
1534 inputs: errEvtNames,
1535 func: function emitError(errEvtName, vasyncCb) {
1536 self.emit(errEvtName, req, res, err, function emitErrDone() {
1537 // the error listener may return arbitrary objects, throw
1538 // them away and continue on. don't want vasync to take
1539 // that error and stop, we want to emit every single event.
1540 return vasyncCb();
1541 });
1542 }
1543 },
1544 // eslint-disable-next-line handle-callback-err
1545 function onResult(__, results) {
1546 // vasync will never return error here since we throw them away.
1547 return cb();
1548 }
1549 );
1550};
1551
1552///--- Helpers
1553
1554/**
1555 * Verify and flatten a nested array of request handlers.
1556 *
1557 * @private
1558 * @function argumentsToChain
1559 * @throws {TypeError}
1560 * @param {Function[]} handlers - pass through of funcs from server.[method]
1561 * @returns {Array} request handlers
1562 */
1563function argumentsToChain(handlers) {
1564 assert.array(handlers, 'handlers');
1565
1566 var chain = [];
1567
1568 // A recursive function for unwinding a nested array of handlers into a
1569 // single chain.
1570 function process(array) {
1571 for (var i = 0; i < array.length; i++) {
1572 if (Array.isArray(array[i])) {
1573 // Recursively call on nested arrays
1574 process(array[i]);
1575 continue;
1576 }
1577 // If an element of the array isn't an array, ensure it is a
1578 // handler function and then push it onto the chain of handlers
1579 assert.func(array[i], 'handler');
1580 chain.push(array[i]);
1581 }
1582
1583 return chain;
1584 }
1585
1586 // Return the chain, note that if `handlers` is an empty array, this will
1587 // return an empty array.
1588 return process(handlers);
1589}
1590
1591/**
1592 * merge optional formatters with the default formatters to create a single
1593 * formatters object. the passed in optional formatters object looks like:
1594 * formatters: {
1595 * 'application/foo': function formatFoo(req, res, body) {...}
1596 * }
1597 * @private
1598 * @function mergeFormatters
1599 * @param {Object} fmt user specified formatters object
1600 * @returns {Object}
1601 */
1602
1603function mergeFormatters(fmt) {
1604 var arr = [];
1605 var obj = {};
1606
1607 function addFormatter(src, k) {
1608 assert.func(src[k], 'formatter');
1609
1610 var q = 1.0; // RFC 2616 sec14 - The default value is q=1
1611 var t = k;
1612
1613 if (k.indexOf(';') !== -1) {
1614 var tmp = k.split(/\s*;\s*/);
1615 t = tmp[0];
1616
1617 if (tmp[1].indexOf('q=') !== -1) {
1618 q = parseFloat(tmp[1].split('=')[1]);
1619 }
1620 }
1621
1622 if (k.indexOf('/') === -1) {
1623 k = mime.getType(k);
1624 }
1625
1626 obj[t] = src[k];
1627 arr.push({
1628 q: q,
1629 t: t
1630 });
1631 }
1632
1633 Object.keys(formatters).forEach(addFormatter.bind(this, formatters));
1634 Object.keys(fmt || {}).forEach(addFormatter.bind(this, fmt || {}));
1635
1636 arr = arr
1637 .sort(function sort(a, b) {
1638 return b.q - a.q;
1639 })
1640 .map(function map(a) {
1641 return a.t;
1642 });
1643
1644 return {
1645 formatters: obj,
1646 acceptable: arr
1647 };
1648}
1649
1650/**
1651 * Map an Error's .name property into the actual event name that is emitted
1652 * by the restify server object.
1653 *
1654 * @function
1655 * @private errEvtNameFromError
1656 * @param {Object} err - an error object
1657 * @returns {String} an event name to emit
1658 */
1659function errEvtNameFromError(err) {
1660 if (err.name === 'ResourceNotFoundError') {
1661 // remap the name for router errors
1662 return 'NotFound';
1663 } else if (err.name === 'InvalidVersionError') {
1664 // remap the name for router errors
1665 return 'VersionNotAllowed';
1666 } else if (err.name) {
1667 return err.name.replace(/Error$/, '');
1668 }
1669 // If the err is not an Error, then just return an empty string
1670 return '';
1671}
1672
1673/**
1674 * Mounts a chain on the given path against this HTTP verb
1675 *
1676 * @private
1677 * @function serverMethodFactory
1678 * @param {String} method - name of the HTTP method
1679 * @returns {Function} factory
1680 */
1681function serverMethodFactory(method) {
1682 return function serverMethod(opts) {
1683 if (opts instanceof RegExp || typeof opts === 'string') {
1684 opts = {
1685 path: opts
1686 };
1687 } else if (typeof opts === 'object') {
1688 opts = shallowCopy(opts);
1689 } else {
1690 throw new TypeError('path (string) required');
1691 }
1692
1693 if (arguments.length < 2) {
1694 throw new TypeError('handler (function) required');
1695 }
1696
1697 opts.method = method;
1698 opts.path = opts.path || opts.url;
1699
1700 // We accept both a variable number of handler functions, a
1701 // variable number of nested arrays of handler functions, or a mix
1702 // of both
1703 var handlers = Array.prototype.slice.call(arguments, 1);
1704 var chain = argumentsToChain(handlers);
1705 var route = this.router.mount(opts, chain);
1706
1707 return route.name;
1708 };
1709}