1 | // Copyright 2012 Mark Cavage, Inc. All rights reserved.
|
2 |
|
3 | ;
|
4 |
|
5 | const { emitWarning } = require('node:process');
|
6 | var url = require('url');
|
7 | var sprintf = require('util').format;
|
8 |
|
9 | var assert = require('assert-plus');
|
10 | var mime = require('mime');
|
11 | var Negotiator = require('negotiator');
|
12 | var uuid = require('uuid');
|
13 |
|
14 | var dtrace = require('./dtrace');
|
15 |
|
16 | ///-- Helpers
|
17 | /**
|
18 | * Creates and sets negotiator on request if one doesn't already exist,
|
19 | * then returns it.
|
20 | *
|
21 | * @private
|
22 | * @function negotiator
|
23 | * @param {Object} req - the request object
|
24 | * @returns {Object} a negotiator
|
25 | */
|
26 | function negotiator(req) {
|
27 | var h = req.headers;
|
28 |
|
29 | if (!req._negotiator) {
|
30 | req._negotiator = new Negotiator({
|
31 | headers: {
|
32 | accept: h.accept || '*/*',
|
33 | 'accept-encoding': h['accept-encoding'] || 'identity'
|
34 | }
|
35 | });
|
36 | }
|
37 |
|
38 | return req._negotiator;
|
39 | }
|
40 |
|
41 | ///--- API
|
42 |
|
43 | /**
|
44 | * Patch Request object and extends with extra functionalities
|
45 | *
|
46 | * @private
|
47 | * @function patch
|
48 | * @param {http.IncomingMessage|http2.Http2ServerRequest} Request -
|
49 | * Server Request
|
50 | * @returns {undefined} No return value
|
51 | */
|
52 | function patch(Request) {
|
53 | /**
|
54 | * Wraps all of the node
|
55 | * [http.IncomingMessage](https://nodejs.org/api/http.html)
|
56 | * APIs, events and properties, plus the following.
|
57 | * @class Request
|
58 | * @extends http.IncomingMessage
|
59 | */
|
60 |
|
61 | ///--- Patches
|
62 |
|
63 | /**
|
64 | * Builds an absolute URI for the request.
|
65 | *
|
66 | * @private
|
67 | * @memberof Request
|
68 | * @instance
|
69 | * @function absoluteUri
|
70 | * @param {String} path - a url path
|
71 | * @returns {String} uri
|
72 | */
|
73 | Request.prototype.absoluteUri = function absoluteUri(path) {
|
74 | assert.string(path, 'path');
|
75 |
|
76 | var protocol = this.isSecure() ? 'https://' : 'http://';
|
77 | var hostname = this.headers.host;
|
78 | return url.resolve(protocol + hostname + this.path() + '/', path);
|
79 | };
|
80 |
|
81 | /**
|
82 | * Check if the Accept header is present, and includes the given type.
|
83 | * When the Accept header is not present true is returned.
|
84 | * Otherwise the given type is matched by an exact match, and then subtypes.
|
85 | *
|
86 | * @public
|
87 | * @memberof Request
|
88 | * @instance
|
89 | * @function accepts
|
90 | * @param {String | String[]} types - an array of accept type headers
|
91 | * @returns {Boolean} is accepteed
|
92 | * @example
|
93 | * <caption>
|
94 | * You may pass the subtype such as html which is then converted internally
|
95 | * to text/html using the mime lookup table:
|
96 | * </caption>
|
97 | * // Accept: text/html
|
98 | * req.accepts('html');
|
99 | * // => true
|
100 | *
|
101 | * // Accept: text/*; application/json
|
102 | * req.accepts('html');
|
103 | * req.accepts('text/html');
|
104 | * req.accepts('text/plain');
|
105 | * req.accepts('application/json');
|
106 | * // => true
|
107 | *
|
108 | * req.accepts('image/png');
|
109 | * req.accepts('png');
|
110 | * // => false
|
111 | */
|
112 | Request.prototype.accepts = function accepts(types) {
|
113 | if (typeof types === 'string') {
|
114 | types = [types];
|
115 | }
|
116 |
|
117 | types = types.map(function map(t) {
|
118 | assert.string(t, 'type');
|
119 |
|
120 | if (t.indexOf('/') === -1) {
|
121 | t = mime.getType(t);
|
122 | }
|
123 | return t;
|
124 | });
|
125 |
|
126 | negotiator(this);
|
127 |
|
128 | return this._negotiator.preferredMediaType(types);
|
129 | };
|
130 |
|
131 | /**
|
132 | * Checks if the request accepts the encoding type(s) specified.
|
133 | *
|
134 | * @public
|
135 | * @memberof Request
|
136 | * @instance
|
137 | * @function acceptsEncoding
|
138 | * @param {String | String[]} types - an array of accept type headers
|
139 | * @returns {Boolean} is accepted encoding
|
140 | */
|
141 | Request.prototype.acceptsEncoding = function acceptsEncoding(types) {
|
142 | if (typeof types === 'string') {
|
143 | types = [types];
|
144 | }
|
145 |
|
146 | assert.arrayOfString(types, 'types');
|
147 |
|
148 | negotiator(this);
|
149 |
|
150 | return this._negotiator.preferredEncoding(types);
|
151 | };
|
152 |
|
153 | /**
|
154 | * Returns the value of the content-length header.
|
155 | *
|
156 | * @private
|
157 | * @memberof Request
|
158 | * @instance
|
159 | * @function getContentLength
|
160 | * @returns {Number} content length
|
161 | */
|
162 | Request.prototype.getContentLength = function getContentLength() {
|
163 | if (this._clen !== undefined) {
|
164 | return this._clen === false ? undefined : this._clen;
|
165 | }
|
166 |
|
167 | // We should not attempt to read and parse the body of an
|
168 | // Upgrade request, so force Content-Length to zero:
|
169 | if (this.isUpgradeRequest()) {
|
170 | return 0;
|
171 | }
|
172 |
|
173 | var len = this.header('content-length');
|
174 |
|
175 | if (!len) {
|
176 | this._clen = false;
|
177 | } else {
|
178 | this._clen = parseInt(len, 10);
|
179 | }
|
180 |
|
181 | return this._clen === false ? undefined : this._clen;
|
182 | };
|
183 | /**
|
184 | * Returns the value of the content-length header.
|
185 | * @public
|
186 | * @memberof Request
|
187 | * @instance
|
188 | * @function contentLength
|
189 | * @returns {Number}
|
190 | */
|
191 | Request.prototype.contentLength = Request.prototype.getContentLength;
|
192 |
|
193 | /**
|
194 | * Returns the value of the content-type header. If a content-type is not
|
195 | * set, this will return a default value of `application/octet-stream`.
|
196 | *
|
197 | * @private
|
198 | * @memberof Request
|
199 | * @instance
|
200 | * @function getContentType
|
201 | * @returns {String} content type
|
202 | */
|
203 | Request.prototype.getContentType = function getContentType() {
|
204 | if (this._contentType !== undefined) {
|
205 | return this._contentType;
|
206 | }
|
207 |
|
208 | var index;
|
209 | var type = this.headers['content-type'];
|
210 |
|
211 | if (!type) {
|
212 | // RFC2616 section 7.2.1
|
213 | this._contentType = 'application/octet-stream';
|
214 | } else if ((index = type.indexOf(';')) === -1) {
|
215 | this._contentType = type;
|
216 | } else {
|
217 | this._contentType = type.substring(0, index);
|
218 | }
|
219 |
|
220 | // #877 content-types need to be case insensitive.
|
221 | this._contentType = this._contentType.toLowerCase();
|
222 |
|
223 | return this._contentType;
|
224 | };
|
225 |
|
226 | /**
|
227 | * Returns the value of the content-type header. If a content-type is not
|
228 | * set, this will return a default value of `application/octet-stream`
|
229 | * @public
|
230 | * @memberof Request
|
231 | * @instance
|
232 | * @function getContentType
|
233 | * @returns {String} content type
|
234 | */
|
235 | Request.prototype.contentType = Request.prototype.getContentType;
|
236 |
|
237 | /**
|
238 | * Returns a Date object representing when the request was setup.
|
239 | * Like `time()`, but returns a Date object.
|
240 | *
|
241 | * @public
|
242 | * @memberof Request
|
243 | * @instance
|
244 | * @function date
|
245 | * @returns {Date} date when request began being processed
|
246 | */
|
247 | Request.prototype.date = function date() {
|
248 | return this._date;
|
249 | };
|
250 |
|
251 | /**
|
252 | * Retrieves the complete URI requested by the client.
|
253 | *
|
254 | * @private
|
255 | * @memberof Request
|
256 | * @instance
|
257 | * @function getHref
|
258 | * @returns {String} URI
|
259 | */
|
260 | Request.prototype.getHref = function getHref() {
|
261 | return this.getUrl().href;
|
262 | };
|
263 |
|
264 | /**
|
265 | * Returns the full requested URL.
|
266 | * @public
|
267 | * @memberof Request
|
268 | * @instance
|
269 | * @function href
|
270 | * @returns {String}
|
271 | * @example
|
272 | * // incoming request is http://localhost:3000/foo/bar?a=1
|
273 | * server.get('/:x/bar', function(req, res, next) {
|
274 | * console.warn(req.href());
|
275 | * // => /foo/bar/?a=1
|
276 | * });
|
277 | */
|
278 | Request.prototype.href = Request.prototype.getHref;
|
279 |
|
280 | /**
|
281 | * Retrieves the request uuid. was created when the request was setup.
|
282 | *
|
283 | * @private
|
284 | * @memberof Request
|
285 | * @instance
|
286 | * @function getId
|
287 | * @returns {String} id
|
288 | */
|
289 | Request.prototype.getId = function getId() {
|
290 | if (this._id !== undefined) {
|
291 | return this._id;
|
292 | }
|
293 |
|
294 | this._id = uuid.v4();
|
295 |
|
296 | return this._id;
|
297 | };
|
298 |
|
299 | /**
|
300 | * Returns the request id. If a `reqId` value is passed in,
|
301 | * this will become the request’s new id. The request id is immutable,
|
302 | * and can only be set once. Attempting to set the request id more than
|
303 | * once will cause restify to throw.
|
304 | *
|
305 | * @public
|
306 | * @memberof Request
|
307 | * @instance
|
308 | * @function id
|
309 | * @param {String} reqId - request id
|
310 | * @returns {String} id
|
311 | */
|
312 | Request.prototype.id = function id(reqId) {
|
313 | var self = this;
|
314 |
|
315 | if (reqId) {
|
316 | if (self._id) {
|
317 | throw new Error(
|
318 | 'request id is immutable, cannot be set again!'
|
319 | );
|
320 | } else {
|
321 | assert.string(reqId, 'reqId');
|
322 | self._id = reqId;
|
323 | return self._id;
|
324 | }
|
325 | }
|
326 |
|
327 | return self.getId();
|
328 | };
|
329 |
|
330 | /**
|
331 | * Retrieves the cleaned up url path.
|
332 | * e.g., /foo?a=1 => /foo
|
333 | *
|
334 | * @private
|
335 | * @memberof Request
|
336 | * @instance
|
337 | * @function getPath
|
338 | * @returns {String} path
|
339 | */
|
340 | Request.prototype.getPath = function getPath() {
|
341 | return this.getUrl().pathname;
|
342 | };
|
343 |
|
344 | /**
|
345 | * Returns the cleaned up requested URL.
|
346 | * @public
|
347 | * @memberof Request
|
348 | * @instance
|
349 | * @function getPath
|
350 | * @returns {String}
|
351 | * @example
|
352 | * // incoming request is http://localhost:3000/foo/bar?a=1
|
353 | * server.get('/:x/bar', function(req, res, next) {
|
354 | * console.warn(req.path());
|
355 | * // => /foo/bar
|
356 | * });
|
357 | */
|
358 | Request.prototype.path = Request.prototype.getPath;
|
359 |
|
360 | /**
|
361 | * Returns the raw query string. Returns empty string
|
362 | * if no query string is found.
|
363 | *
|
364 | * @public
|
365 | * @memberof Request
|
366 | * @instance
|
367 | * @function getQuery
|
368 | * @returns {String} query
|
369 | * @example
|
370 | * // incoming request is /foo?a=1
|
371 | * req.getQuery();
|
372 | * // => 'a=1'
|
373 | * @example
|
374 | * <caption>
|
375 | * If the queryParser plugin is used, the parsed query string is
|
376 | * available under the req.query:
|
377 | * </caption>
|
378 | * // incoming request is /foo?a=1
|
379 | * server.use(restify.plugins.queryParser());
|
380 | * req.query;
|
381 | * // => { a: 1 }
|
382 | */
|
383 | Request.prototype.getQuery = function getQuery() {
|
384 | // always return a string, because this is the raw query string.
|
385 | // if the queryParser plugin is used, req.query will provide an empty
|
386 | // object fallback.
|
387 | return this.getUrl().query || '';
|
388 | };
|
389 |
|
390 | /**
|
391 | * Returns the raw query string. Returns empty string
|
392 | * if no query string is found
|
393 | * @private
|
394 | * @memberof Request
|
395 | * @instance
|
396 | * @function query
|
397 | * @returns {String}
|
398 | */
|
399 | Request.prototype.query = Request.prototype.getQuery;
|
400 |
|
401 | /**
|
402 | * The number of ms since epoch of when this request began being processed.
|
403 | * Like date(), but returns a number.
|
404 | *
|
405 | * @public
|
406 | * @memberof Request
|
407 | * @instance
|
408 | * @function time
|
409 | * @returns {Number} time when request began being processed in epoch:
|
410 | * ellapsed milliseconds since
|
411 | * January 1, 1970, 00:00:00 UTC
|
412 | */
|
413 | Request.prototype.time = function time() {
|
414 | return this._date.getTime();
|
415 | };
|
416 |
|
417 | /**
|
418 | * returns a parsed URL object.
|
419 | *
|
420 | * @private
|
421 | * @memberof Request
|
422 | * @instance
|
423 | * @function getUrl
|
424 | * @returns {Object} url
|
425 | */
|
426 | Request.prototype.getUrl = function getUrl() {
|
427 | if (this._cacheURL !== this.url) {
|
428 | this._url = url.parse(this.url);
|
429 | this._cacheURL = this.url;
|
430 | }
|
431 | return this._url;
|
432 | };
|
433 |
|
434 | /**
|
435 | * Returns the accept-version header.
|
436 | *
|
437 | * @private
|
438 | * @memberof Request
|
439 | * @instance
|
440 | * @function getVersion
|
441 | * @returns {String} version
|
442 | */
|
443 | Request.prototype.getVersion = function getVersion() {
|
444 | if (this._version !== undefined) {
|
445 | return this._version;
|
446 | }
|
447 |
|
448 | this._version =
|
449 | this.headers['accept-version'] ||
|
450 | this.headers['x-api-version'] ||
|
451 | '*';
|
452 |
|
453 | return this._version;
|
454 | };
|
455 |
|
456 | /**
|
457 | * Returns the accept-version header.
|
458 | * @public
|
459 | * @memberof Request
|
460 | * @instance
|
461 | * @function version
|
462 | * @returns {String}
|
463 | */
|
464 | Request.prototype.version = Request.prototype.getVersion;
|
465 |
|
466 | /**
|
467 | * Returns the version of the route that matched.
|
468 | *
|
469 | * @private
|
470 | * @memberof Request
|
471 | * @instance
|
472 | * @function matchedVersion
|
473 | * @returns {String} version
|
474 | */
|
475 | Request.prototype.matchedVersion = function matchedVersion() {
|
476 | if (this._matchedVersion !== undefined) {
|
477 | return this._matchedVersion;
|
478 | } else {
|
479 | return this.version();
|
480 | }
|
481 | };
|
482 |
|
483 | /**
|
484 | * Get the case-insensitive request header key,
|
485 | * and optionally provide a default value (express-compliant).
|
486 | * Returns any header off the request. also, 'correct' any
|
487 | * correctly spelled 'referrer' header to the actual spelling used.
|
488 | *
|
489 | * @public
|
490 | * @memberof Request
|
491 | * @instance
|
492 | * @function header
|
493 | * @param {String} key - the key of the header
|
494 | * @param {String} [defaultValue] - default value if header isn't
|
495 | * found on the req
|
496 | * @returns {String} header value
|
497 | * @example
|
498 | * req.header('Host');
|
499 | * req.header('HOST');
|
500 | * req.header('Accept', '*\/*');
|
501 | */
|
502 | Request.prototype.header = function header(key, defaultValue) {
|
503 | assert.string(key, 'key');
|
504 |
|
505 | key = key.toLowerCase();
|
506 |
|
507 | if (key === 'referer' || key === 'referrer') {
|
508 | key = 'referer';
|
509 | }
|
510 |
|
511 | return this.headers[key] || defaultValue;
|
512 | };
|
513 |
|
514 | /**
|
515 | * Returns any trailer header off the request. Also, 'correct' any
|
516 | * correctly spelled 'referrer' header to the actual spelling used.
|
517 | *
|
518 | * @public
|
519 | * @memberof Request
|
520 | * @instance
|
521 | * @function trailer
|
522 | * @param {String} name - the name of the header
|
523 | * @param {String} value - default value if header isn't found on the req
|
524 | * @returns {String} trailer value
|
525 | */
|
526 | Request.prototype.trailer = function trailer(name, value) {
|
527 | assert.string(name, 'name');
|
528 | name = name.toLowerCase();
|
529 |
|
530 | if (name === 'referer' || name === 'referrer') {
|
531 | name = 'referer';
|
532 | }
|
533 |
|
534 | return (this.trailers || {})[name] || value;
|
535 | };
|
536 |
|
537 | /**
|
538 | * Check if the incoming request contains the `Content-Type` header field,
|
539 | * and if it contains the given mime type.
|
540 | *
|
541 | * @public
|
542 | * @memberof Request
|
543 | * @instance
|
544 | * @function is
|
545 | * @param {String} type - a content-type header value
|
546 | * @returns {Boolean} is content-type header
|
547 | * @example
|
548 | * // With Content-Type: text/html; charset=utf-8
|
549 | * req.is('html');
|
550 | * req.is('text/html');
|
551 | * // => true
|
552 | *
|
553 | * // When Content-Type is application/json
|
554 | * req.is('json');
|
555 | * req.is('application/json');
|
556 | * // => true
|
557 | *
|
558 | * req.is('html');
|
559 | * // => false
|
560 | */
|
561 | Request.prototype.is = function is(type) {
|
562 | assert.string(type, 'type');
|
563 |
|
564 | var contentType = this.getContentType();
|
565 | var matches = true;
|
566 |
|
567 | if (!contentType) {
|
568 | return false;
|
569 | }
|
570 |
|
571 | if (type.indexOf('/') === -1) {
|
572 | type = mime.getType(type);
|
573 | }
|
574 |
|
575 | if (type.indexOf('*') !== -1) {
|
576 | type = type.split('/');
|
577 | contentType = contentType.split('/');
|
578 | matches &= type[0] === '*' || type[0] === contentType[0];
|
579 | matches &= type[1] === '*' || type[1] === contentType[1];
|
580 | } else {
|
581 | matches = contentType === type;
|
582 | }
|
583 |
|
584 | return matches;
|
585 | };
|
586 |
|
587 | /**
|
588 | * Check if the incoming request is chunked.
|
589 | *
|
590 | * @public
|
591 | * @memberof Request
|
592 | * @instance
|
593 | * @function isChunked
|
594 | * @returns {Boolean} is chunked
|
595 | */
|
596 | Request.prototype.isChunked = function isChunked() {
|
597 | return this.headers['transfer-encoding'] === 'chunked';
|
598 | };
|
599 |
|
600 | /**
|
601 | * Check if the incoming request is kept alive.
|
602 | *
|
603 | * @public
|
604 | * @memberof Request
|
605 | * @instance
|
606 | * @function isKeepAlive
|
607 | * @returns {Boolean} is keep alive
|
608 | */
|
609 | Request.prototype.isKeepAlive = function isKeepAlive() {
|
610 | if (this._keepAlive !== undefined) {
|
611 | return this._keepAlive;
|
612 | }
|
613 |
|
614 | if (this.headers.connection) {
|
615 | this._keepAlive = /keep-alive/i.test(this.headers.connection);
|
616 | } else {
|
617 | this._keepAlive = this.httpVersion === '1.0' ? false : true;
|
618 | }
|
619 |
|
620 | return this._keepAlive;
|
621 | };
|
622 |
|
623 | /**
|
624 | * Check if the incoming request is encrypted.
|
625 | *
|
626 | * @public
|
627 | * @memberof Request
|
628 | * @instance
|
629 | * @function isSecure
|
630 | * @returns {Boolean} is secure
|
631 | */
|
632 | Request.prototype.isSecure = function isSecure() {
|
633 | if (this._secure !== undefined) {
|
634 | return this._secure;
|
635 | }
|
636 |
|
637 | this._secure = this.connection.encrypted ? true : false;
|
638 | return this._secure;
|
639 | };
|
640 |
|
641 | /**
|
642 | * Check if the incoming request has been upgraded.
|
643 | *
|
644 | * @public
|
645 | * @memberof Request
|
646 | * @instance
|
647 | * @function isUpgradeRequest
|
648 | * @returns {Boolean} is upgraded
|
649 | */
|
650 | Request.prototype.isUpgradeRequest = function isUpgradeRequest() {
|
651 | if (this._upgradeRequest !== undefined) {
|
652 | return this._upgradeRequest;
|
653 | } else {
|
654 | return false;
|
655 | }
|
656 | };
|
657 |
|
658 | /**
|
659 | * Check if the incoming request is an upload verb.
|
660 | *
|
661 | * @public
|
662 | * @memberof Request
|
663 | * @instance
|
664 | * @function isUpload
|
665 | * @returns {Boolean} is upload
|
666 | */
|
667 | Request.prototype.isUpload = function isUpload() {
|
668 | var m = this.method;
|
669 | return m === 'PATCH' || m === 'POST' || m === 'PUT';
|
670 | };
|
671 |
|
672 | /**
|
673 | * toString serialization
|
674 | *
|
675 | * @public
|
676 | * @memberof Request
|
677 | * @instance
|
678 | * @function toString
|
679 | * @returns {String} serialized request
|
680 | */
|
681 | Request.prototype.toString = function toString() {
|
682 | var headers = '';
|
683 | var self = this;
|
684 | var str;
|
685 |
|
686 | Object.keys(this.headers).forEach(function forEach(k) {
|
687 | headers += sprintf('%s: %s\n', k, self.headers[k]);
|
688 | });
|
689 |
|
690 | str = sprintf(
|
691 | '%s %s HTTP/%s\n%s',
|
692 | this.method,
|
693 | this.url,
|
694 | this.httpVersion,
|
695 | headers
|
696 | );
|
697 |
|
698 | return str;
|
699 | };
|
700 |
|
701 | /**
|
702 | * Returns the user-agent header.
|
703 | *
|
704 | * @public
|
705 | * @memberof Request
|
706 | * @instance
|
707 | * @function userAgent
|
708 | * @returns {String} user agent
|
709 | */
|
710 | Request.prototype.userAgent = function userAgent() {
|
711 | return this.headers['user-agent'];
|
712 | };
|
713 |
|
714 | /**
|
715 | * Start the timer for a request handler.
|
716 | * By default, restify uses calls this automatically for all handlers
|
717 | * registered in your handler chain.
|
718 | * However, this can be called manually for nested functions inside the
|
719 | * handler chain to record timing information.
|
720 | *
|
721 | * @public
|
722 | * @memberof Request
|
723 | * @instance
|
724 | * @function startHandlerTimer
|
725 | * @param {String} handlerName - The name of the handler.
|
726 | * @returns {undefined} no return value
|
727 | * @example
|
728 | * <caption>
|
729 | * You must explicitly invoke
|
730 | * endHandlerTimer() after invoking this function. Otherwise timing
|
731 | * information will be inaccurate.
|
732 | * </caption>
|
733 | * server.get('/', function fooHandler(req, res, next) {
|
734 | * vasync.pipeline({
|
735 | * funcs: [
|
736 | * function nestedHandler1(req, res, next) {
|
737 | * req.startHandlerTimer('nestedHandler1');
|
738 | * // do something
|
739 | * req.endHandlerTimer('nestedHandler1');
|
740 | * return next();
|
741 | * },
|
742 | * function nestedHandler1(req, res, next) {
|
743 | * req.startHandlerTimer('nestedHandler2');
|
744 | * // do something
|
745 | * req.endHandlerTimer('nestedHandler2');
|
746 | * return next();
|
747 | *
|
748 | * }...
|
749 | * ]...
|
750 | * }, next);
|
751 | * });
|
752 | */
|
753 | Request.prototype.startHandlerTimer = function startHandlerTimer(
|
754 | handlerName
|
755 | ) {
|
756 | var self = this;
|
757 |
|
758 | // For nested handlers, we prepend the top level handler func name
|
759 | var name =
|
760 | self._currentHandler === handlerName
|
761 | ? handlerName
|
762 | : self._currentHandler + '-' + handlerName;
|
763 |
|
764 | if (!self._timerMap) {
|
765 | self._timerMap = {};
|
766 | }
|
767 |
|
768 | self._timerMap[name] = process.hrtime();
|
769 |
|
770 | if (self.dtrace) {
|
771 | dtrace._rstfy_probes['handler-start'].fire(function fire() {
|
772 | return [
|
773 | self.serverName,
|
774 | self._currentRoute, // set in server._run
|
775 | name,
|
776 | self._dtraceId
|
777 | ];
|
778 | });
|
779 | }
|
780 | };
|
781 |
|
782 | /**
|
783 | * End the timer for a request handler.
|
784 | * You must invoke this function if you called `startRequestHandler` on a
|
785 | * handler. Otherwise the time recorded will be incorrect.
|
786 | *
|
787 | * @public
|
788 | * @memberof Request
|
789 | * @instance
|
790 | * @function endHandlerTimer
|
791 | * @param {String} handlerName - The name of the handler.
|
792 | * @returns {undefined} no return value
|
793 | */
|
794 | Request.prototype.endHandlerTimer = function endHandlerTimer(handlerName) {
|
795 | var self = this;
|
796 |
|
797 | // For nested handlers, we prepend the top level handler func name
|
798 | var name =
|
799 | self._currentHandler === handlerName
|
800 | ? handlerName
|
801 | : self._currentHandler + '-' + handlerName;
|
802 |
|
803 | if (!self.timers) {
|
804 | self.timers = [];
|
805 | }
|
806 |
|
807 | self._timerMap[name] = process.hrtime(self._timerMap[name]);
|
808 | self.timers.push({
|
809 | name: name,
|
810 | time: self._timerMap[name]
|
811 | });
|
812 |
|
813 | if (self.dtrace) {
|
814 | dtrace._rstfy_probes['handler-done'].fire(function fire() {
|
815 | return [
|
816 | self.serverName,
|
817 | self._currentRoute, // set in server._run
|
818 | name,
|
819 | self._dtraceId
|
820 | ];
|
821 | });
|
822 | }
|
823 | };
|
824 |
|
825 | /**
|
826 | * Returns the connection state of the request. Current possible values are:
|
827 | * - `close` - when the request has been closed by the clien
|
828 | *
|
829 | * @public
|
830 | * @memberof Request
|
831 | * @instance
|
832 | * @function connectionState
|
833 | * @returns {String} connection state (`"close"`)
|
834 | */
|
835 | Request.prototype.connectionState = function connectionState() {
|
836 | var self = this;
|
837 | return self._connectionState;
|
838 | };
|
839 |
|
840 | /**
|
841 | * Returns true when connection state is "close"
|
842 | *
|
843 | * @private
|
844 | * @memberof Request
|
845 | * @instance
|
846 | * @function closed
|
847 | * @returns {Boolean} is closed
|
848 | */
|
849 | Request.prototype.closed = function closed() {
|
850 | emitWarning(
|
851 | 'restify req.closed is deprecated, will be removed on Restify 10',
|
852 | 'RestifyDeprecationWarning',
|
853 | 'RestifyDEPReqClosed'
|
854 | );
|
855 | var self = this;
|
856 | return self.connectionState() === 'close';
|
857 | };
|
858 |
|
859 | /**
|
860 | * Returns the route object to which the current request was matched to.
|
861 | *
|
862 | * @public
|
863 | * @memberof Request
|
864 | * @instance
|
865 | * @function getRoute
|
866 | * @returns {Object} route
|
867 | * @example
|
868 | * <caption>Route info object structure:</caption>
|
869 | * {
|
870 | * path: '/ping/:name',
|
871 | * method: 'GET',
|
872 | * versions: [],
|
873 | * name: 'getpingname'
|
874 | * }
|
875 | */
|
876 | Request.prototype.getRoute = function getRoute() {
|
877 | var self = this;
|
878 | return self.route;
|
879 | };
|
880 | }
|
881 |
|
882 | module.exports = patch;
|