3 * Copyright(c) 2009-2013 TJ Holowaychuk
4 * Copyright(c) 2013 Roman Shtylman
5 * Copyright(c) 2014-2015 Douglas Christopher Wilson
12 * Module dependencies.
16 var accepts
= require('accepts');
17 var isIP
= require('net').isIP
;
18 var typeis
= require('type-is');
19 var http
= require('http');
20 var fresh
= require('fresh');
21 var parseRange
= require('range-parser');
22 var parse
= require('parseurl');
23 var proxyaddr
= require('proxy-addr');
30 var req
= Object
.create(http
.IncomingMessage
.prototype)
40 * Return request header.
42 * The `Referrer` header field is special-cased,
43 * both `Referrer` and `Referer` are interchangeable.
47 * req.get('Content-Type');
50 * req.get('content-type');
53 * req.get('Something');
56 * Aliased as `req.header()`.
58 * @param {String} name
64 req
.header
= function header(name
) {
66 throw new TypeError('name argument is required to req.get');
69 if (typeof name
!== 'string') {
70 throw new TypeError('name must be a string to req.get');
73 var lc
= name
.toLowerCase();
78 return this.headers
.referrer
79 || this.headers
.referer
;
81 return this.headers
[lc
];
88 * Check if the given `type(s)` is acceptable, returning
89 * the best match when true, otherwise `undefined`, in which
90 * case you should respond with 406 "Not Acceptable".
92 * The `type` value may be a single MIME type string
93 * such as "application/json", an extension name
94 * such as "json", a comma-delimited list such as "json, html, text/plain",
95 * an argument list such as `"json", "html", "text/plain"`,
96 * or an array `["json", "html", "text/plain"]`. When a list
97 * or array is given, the _best_ match, if any is returned.
101 * // Accept: text/html
102 * req.accepts('html');
105 * // Accept: text/*, application/json
106 * req.accepts('html');
108 * req.accepts('text/html');
110 * req.accepts('json, text');
112 * req.accepts('application/json');
113 * // => "application/json"
115 * // Accept: text/*, application/json
116 * req.accepts('image/png');
117 * req.accepts('png');
120 * // Accept: text/*;q=.5, application/json
121 * req.accepts(['html', 'json']);
122 * req.accepts('html', 'json');
123 * req.accepts('html, json');
126 * @param {String|Array} type(s)
127 * @return {String|Array|Boolean}
131 req
.accepts = function(){
132 var accept
= accepts(this);
133 return accept
.types
.apply(accept
, arguments
);
137 * Check if the given `encoding`s are accepted.
139 * @param {String} ...encoding
140 * @return {String|Array}
144 req
.acceptsEncodings = function(){
145 var accept
= accepts(this);
146 return accept
.encodings
.apply(accept
, arguments
);
150 * Check if the given `charset`s are acceptable,
151 * otherwise you should respond with 406 "Not Acceptable".
153 * @param {String} ...charset
154 * @return {String|Array}
158 req
.acceptsCharsets = function(){
159 var accept
= accepts(this);
160 return accept
.charsets
.apply(accept
, arguments
);
164 * Check if the given `lang`s are acceptable,
165 * otherwise you should respond with 406 "Not Acceptable".
167 * @param {String} ...lang
168 * @return {String|Array}
172 req
.acceptsLanguages = function(){
173 var accept
= accepts(this);
174 return accept
.languages
.apply(accept
, arguments
);
178 * Parse Range header field, capping to the given `size`.
180 * Unspecified ranges such as "0-" require knowledge of your resource length. In
181 * the case of a byte range this is of course the total number of bytes. If the
182 * Range header field is not given `undefined` is returned, `-1` when unsatisfiable,
183 * and `-2` when syntactically invalid.
185 * When ranges are returned, the array has a "type" property which is the type of
186 * range that is required (most commonly, "bytes"). Each array element is an object
187 * with a "start" and "end" property for the portion of the range.
189 * The "combine" option can be set to `true` and overlapping & adjacent ranges
190 * will be combined into a single range.
192 * NOTE: remember that ranges are inclusive, so for example "Range: users=0-3"
193 * should respond with 4 users when available, not 3.
195 * @param {number} size
196 * @param {object} [options]
197 * @param {boolean} [options.combine=false]
198 * @return {number|array}
202 req
.range
= function range(size
, options
) {
203 var range
= this.get('Range');
205 return parseRange(size
, range
, options
);
209 * Parse the query string of `req.url`.
211 * This uses the "query parser" setting to parse the raw
212 * string into an object.
218 defineGetter(req
, 'query', function query(){
219 var queryparse
= this.app
.get('query parser fn');
222 // parsing is disabled
223 return Object
.create(null);
226 var querystring
= parse(this).query
;
228 return queryparse(querystring
);
232 * Check if the incoming request contains the "Content-Type"
233 * header field, and it contains the given mime `type`.
237 * // With Content-Type: text/html; charset=utf-8
239 * req.is('text/html');
243 * // When Content-Type is application/json
245 * req.is('application/json');
246 * req.is('application/*');
252 * @param {String|Array} types...
253 * @return {String|false|null}
257 req
.is
= function is(types
) {
260 // support flattened arguments
261 if (!Array
.isArray(types
)) {
262 arr
= new Array(arguments
.length
);
263 for (var i
= 0; i
< arr
.length
; i
++) {
264 arr
[i
] = arguments
[i
];
268 return typeis(this, arr
);
272 * Return the protocol string "http" or "https"
273 * when requested with TLS. When the "trust proxy"
274 * setting trusts the socket address, the
275 * "X-Forwarded-Proto" header field will be trusted
276 * and used if present.
278 * If you're running behind a reverse proxy that
279 * supplies https for you this may be enabled.
285 defineGetter(req
, 'protocol', function protocol(){
286 var proto
= this.connection
.encrypted
289 var trust
= this.app
.get('trust proxy fn');
291 if (!trust(this.connection
.remoteAddress
, 0)) {
295 // Note: X-Forwarded-Proto is normally only ever a
296 // single value, but this is to be safe.
297 var header
= this.get('X-Forwarded-Proto') || proto
298 var index
= header
.indexOf(',')
301 ? header
.substring(0, index
).trim()
308 * req.protocol === 'https'
314 defineGetter(req
, 'secure', function secure(){
315 return this.protocol
=== 'https';
319 * Return the remote address from the trusted proxy.
321 * The is the remote address on the socket unless
322 * "trust proxy" is set.
328 defineGetter(req
, 'ip', function ip(){
329 var trust
= this.app
.get('trust proxy fn');
330 return proxyaddr(this, trust
);
334 * When "trust proxy" is set, trusted proxy addresses + client.
336 * For example if the value were "client, proxy1, proxy2"
337 * you would receive the array `["client", "proxy1", "proxy2"]`
338 * where "proxy2" is the furthest down-stream and "proxy1" and
339 * "proxy2" were trusted.
345 defineGetter(req
, 'ips', function ips() {
346 var trust
= this.app
.get('trust proxy fn');
347 var addrs
= proxyaddr
.all(this, trust
);
349 // reverse the order (to farthest -> closest)
350 // and remove socket address
351 addrs
.reverse().pop()
357 * Return subdomains as an array.
359 * Subdomains are the dot-separated parts of the host before the main domain of
360 * the app. By default, the domain of the app is assumed to be the last two
361 * parts of the host. This can be changed by setting "subdomain offset".
363 * For example, if the domain is "tobi.ferrets.example.com":
364 * If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`.
365 * If "subdomain offset" is 3, req.subdomains is `["tobi"]`.
371 defineGetter(req
, 'subdomains', function subdomains() {
372 var hostname
= this.hostname
;
374 if (!hostname
) return [];
376 var offset
= this.app
.get('subdomain offset');
377 var subdomains
= !isIP(hostname
)
378 ? hostname
.split('.').reverse()
381 return subdomains
.slice(offset
);
385 * Short-hand for `url.parse(req.url).pathname`.
391 defineGetter(req
, 'path', function path() {
392 return parse(this).pathname
;
396 * Parse the "Host" header field to a host.
398 * When the "trust proxy" setting trusts the socket
399 * address, the "X-Forwarded-Host" header field will
406 defineGetter(req
, 'host', function host(){
407 var trust
= this.app
.get('trust proxy fn');
408 var val
= this.get('X-Forwarded-Host');
410 if (!val
|| !trust(this.connection
.remoteAddress
, 0)) {
411 val
= this.get('Host');
412 } else if (val
.indexOf(',') !== -1) {
413 // Note: X-Forwarded-Host is normally only ever a
414 // single value, but this is to be safe.
415 val
= val
.substring(0, val
.indexOf(',')).trimRight()
418 return val
|| undefined;
422 * Parse the "Host" header field to a hostname.
424 * When the "trust proxy" setting trusts the socket
425 * address, the "X-Forwarded-Host" header field will
432 defineGetter(req
, 'hostname', function hostname(){
433 var host
= this.host
;
437 // IPv6 literal support
438 var offset
= host
[0] === '['
439 ? host
.indexOf(']') + 1
441 var index
= host
.indexOf(':', offset
);
444 ? host
.substring(0, index
)
449 * Check if the request is fresh, aka
450 * Last-Modified or the ETag
457 defineGetter(req
, 'fresh', function(){
458 var method
= this.method
;
460 var status
= res
.statusCode
462 // GET or HEAD for weak freshness validation only
463 if ('GET' !== method
&& 'HEAD' !== method
) return false;
465 // 2xx or 304 as per rfc2616 14.26
466 if ((status
>= 200 && status
< 300) || 304 === status
) {
467 return fresh(this.headers
, {
468 'etag': res
.get('ETag'),
469 'last-modified': res
.get('Last-Modified')
477 * Check if the request is stale, aka
478 * "Last-Modified" and / or the "ETag" for the
479 * resource has changed.
485 defineGetter(req
, 'stale', function stale(){
490 * Check if the request was an _XMLHttpRequest_.
496 defineGetter(req
, 'xhr', function xhr(){
497 var val
= this.get('X-Requested-With') || '';
498 return val
.toLowerCase() === 'xmlhttprequest';
502 * Helper function for creating a getter on an object.
504 * @param {Object} obj
505 * @param {String} name
506 * @param {Function} getter
509 function defineGetter(obj
, name
, getter
) {
510 Object
.defineProperty(obj
, name
, {