123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- /**
- * Module dependencies.
- */
- var accepts = require('accepts');
- var typeis = require('type-is');
- var http = require('http');
- var fresh = require('fresh');
- var parseRange = require('range-parser');
- var parse = require('parseurl');
- var proxyaddr = require('proxy-addr');
- /**
- * Request prototype.
- */
- var req = exports = module.exports = {
- __proto__: http.IncomingMessage.prototype
- };
- /**
- * Return request header.
- *
- * The `Referrer` header field is special-cased,
- * both `Referrer` and `Referer` are interchangeable.
- *
- * Examples:
- *
- * req.get('Content-Type');
- * // => "text/plain"
- *
- * req.get('content-type');
- * // => "text/plain"
- *
- * req.get('Something');
- * // => undefined
- *
- * Aliased as `req.header()`.
- *
- * @param {String} name
- * @return {String}
- * @api public
- */
- req.get =
- req.header = function(name){
- switch (name = name.toLowerCase()) {
- case 'referer':
- case 'referrer':
- return this.headers.referrer
- || this.headers.referer;
- default:
- return this.headers[name];
- }
- };
- /**
- * To do: update docs.
- *
- * Check if the given `type(s)` is acceptable, returning
- * the best match when true, otherwise `undefined`, in which
- * case you should respond with 406 "Not Acceptable".
- *
- * The `type` value may be a single mime type string
- * such as "application/json", the extension name
- * such as "json", a comma-delimted list such as "json, html, text/plain",
- * an argument list such as `"json", "html", "text/plain"`,
- * or an array `["json", "html", "text/plain"]`. When a list
- * or array is given the _best_ match, if any is returned.
- *
- * Examples:
- *
- * // Accept: text/html
- * req.accepts('html');
- * // => "html"
- *
- * // Accept: text/*, application/json
- * req.accepts('html');
- * // => "html"
- * req.accepts('text/html');
- * // => "text/html"
- * req.accepts('json, text');
- * // => "json"
- * req.accepts('application/json');
- * // => "application/json"
- *
- * // Accept: text/*, application/json
- * req.accepts('image/png');
- * req.accepts('png');
- * // => undefined
- *
- * // Accept: text/*;q=.5, application/json
- * req.accepts(['html', 'json']);
- * req.accepts('html', 'json');
- * req.accepts('html, json');
- * // => "json"
- *
- * @param {String|Array} type(s)
- * @return {String}
- * @api public
- */
- req.accepts = function(){
- var accept = accepts(this);
- return accept.types.apply(accept, arguments);
- };
- /**
- * Check if the given `encoding` is accepted.
- *
- * @param {String} encoding
- * @return {Boolean}
- * @api public
- */
- req.acceptsEncoding = // backwards compatibility
- req.acceptsEncodings = function(){
- var accept = accepts(this);
- return accept.encodings.apply(accept, arguments);
- };
- /**
- * To do: update docs.
- *
- * Check if the given `charset` is acceptable,
- * otherwise you should respond with 406 "Not Acceptable".
- *
- * @param {String} charset
- * @return {Boolean}
- * @api public
- */
- req.acceptsCharset = // backwards compatibility
- req.acceptsCharsets = function(){
- var accept = accepts(this);
- return accept.charsets.apply(accept, arguments);
- };
- /**
- * To do: update docs.
- *
- * Check if the given `lang` is acceptable,
- * otherwise you should respond with 406 "Not Acceptable".
- *
- * @param {String} lang
- * @return {Boolean}
- * @api public
- */
- req.acceptsLanguage = // backwards compatibility
- req.acceptsLanguages = function(){
- var accept = accepts(this);
- return accept.languages.apply(accept, arguments);
- };
- /**
- * Parse Range header field,
- * capping to the given `size`.
- *
- * Unspecified ranges such as "0-" require
- * knowledge of your resource length. In
- * the case of a byte range this is of course
- * the total number of bytes. If the Range
- * header field is not given `null` is returned,
- * `-1` when unsatisfiable, `-2` when syntactically invalid.
- *
- * NOTE: remember that ranges are inclusive, so
- * for example "Range: users=0-3" should respond
- * with 4 users when available, not 3.
- *
- * @param {Number} size
- * @return {Array}
- * @api public
- */
- req.range = function(size){
- var range = this.get('Range');
- if (!range) return;
- return parseRange(size, range);
- };
- /**
- * Return the value of param `name` when present or `defaultValue`.
- *
- * - Checks route placeholders, ex: _/user/:id_
- * - Checks body params, ex: id=12, {"id":12}
- * - Checks query string params, ex: ?id=12
- *
- * To utilize request bodies, `req.body`
- * should be an object. This can be done by using
- * the `bodyParser()` middleware.
- *
- * @param {String} name
- * @param {Mixed} [defaultValue]
- * @return {String}
- * @api public
- */
- req.param = function(name, defaultValue){
- var params = this.params || {};
- var body = this.body || {};
- var query = this.query || {};
- if (null != params[name] && params.hasOwnProperty(name)) return params[name];
- if (null != body[name]) return body[name];
- if (null != query[name]) return query[name];
- return defaultValue;
- };
- /**
- * Check if the incoming request contains the "Content-Type"
- * header field, and it contains the give mime `type`.
- *
- * Examples:
- *
- * // With Content-Type: text/html; charset=utf-8
- * req.is('html');
- * req.is('text/html');
- * req.is('text/*');
- * // => true
- *
- * // When Content-Type is application/json
- * req.is('json');
- * req.is('application/json');
- * req.is('application/*');
- * // => true
- *
- * req.is('html');
- * // => false
- *
- * @param {String} type
- * @return {Boolean}
- * @api public
- */
- req.is = function(types){
- if (!Array.isArray(types)) types = [].slice.call(arguments);
- return typeis(this, types);
- };
- /**
- * Return the protocol string "http" or "https"
- * when requested with TLS. When the "trust proxy"
- * setting trusts the socket address, the
- * "X-Forwarded-Proto" header field will be trusted.
- * If you're running behind a reverse proxy that
- * supplies https for you this may be enabled.
- *
- * @return {String}
- * @api public
- */
- req.__defineGetter__('protocol', function(){
- var trust = this.app.get('trust proxy fn');
- if (!trust(this.connection.remoteAddress)) {
- return this.connection.encrypted
- ? 'https'
- : 'http';
- }
- // Note: X-Forwarded-Proto is normally only ever a
- // single value, but this is to be safe.
- var proto = this.get('X-Forwarded-Proto') || 'http';
- return proto.split(/\s*,\s*/)[0];
- });
- /**
- * Short-hand for:
- *
- * req.protocol == 'https'
- *
- * @return {Boolean}
- * @api public
- */
- req.__defineGetter__('secure', function(){
- return 'https' == this.protocol;
- });
- /**
- * Return the remote address from the trusted proxy.
- *
- * The is the remote address on the socket unless
- * "trust proxy" is set.
- *
- * @return {String}
- * @api public
- */
- req.__defineGetter__('ip', function(){
- var trust = this.app.get('trust proxy fn');
- return proxyaddr(this, trust);
- });
- /**
- * When "trust proxy" is set, trusted proxy addresses + client.
- *
- * For example if the value were "client, proxy1, proxy2"
- * you would receive the array `["client", "proxy1", "proxy2"]`
- * where "proxy2" is the furthest down-stream and "proxy1" and
- * "proxy2" were trusted.
- *
- * @return {Array}
- * @api public
- */
- req.__defineGetter__('ips', function(){
- var trust = this.app.get('trust proxy fn');
- var addrs = proxyaddr.all(this, trust);
- return addrs.slice(1).reverse();
- });
- /**
- * Return subdomains as an array.
- *
- * Subdomains are the dot-separated parts of the host before the main domain of
- * the app. By default, the domain of the app is assumed to be the last two
- * parts of the host. This can be changed by setting "subdomain offset".
- *
- * For example, if the domain is "tobi.ferrets.example.com":
- * If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`.
- * If "subdomain offset" is 3, req.subdomains is `["tobi"]`.
- *
- * @return {Array}
- * @api public
- */
- req.__defineGetter__('subdomains', function(){
- var offset = this.app.get('subdomain offset');
- return (this.host || '')
- .split('.')
- .reverse()
- .slice(offset);
- });
- /**
- * Short-hand for `url.parse(req.url).pathname`.
- *
- * @return {String}
- * @api public
- */
- req.__defineGetter__('path', function(){
- return parse(this).pathname;
- });
- /**
- * Parse the "Host" header field hostname.
- *
- * When the "trust proxy" setting trusts the socket
- * address, the "X-Forwarded-Host" header field will
- * be trusted.
- *
- * @return {String}
- * @api public
- */
- req.__defineGetter__('host', function(){
- var trust = this.app.get('trust proxy fn');
- var host = this.get('X-Forwarded-Host');
- if (!host || !trust(this.connection.remoteAddress)) {
- host = this.get('Host');
- }
- if (!host) return;
- // IPv6 literal support
- var offset = host[0] === '['
- ? host.indexOf(']') + 1
- : 0;
- var index = host.indexOf(':', offset);
- return ~index
- ? host.substring(0, index)
- : host;
- });
- /**
- * Check if the request is fresh, aka
- * Last-Modified and/or the ETag
- * still match.
- *
- * @return {Boolean}
- * @api public
- */
- req.__defineGetter__('fresh', function(){
- var method = this.method;
- var s = this.res.statusCode;
- // GET or HEAD for weak freshness validation only
- if ('GET' != method && 'HEAD' != method) return false;
- // 2xx or 304 as per rfc2616 14.26
- if ((s >= 200 && s < 300) || 304 == s) {
- return fresh(this.headers, this.res._headers);
- }
- return false;
- });
- /**
- * Check if the request is stale, aka
- * "Last-Modified" and / or the "ETag" for the
- * resource has changed.
- *
- * @return {Boolean}
- * @api public
- */
- req.__defineGetter__('stale', function(){
- return !this.fresh;
- });
- /**
- * Check if the request was an _XMLHttpRequest_.
- *
- * @return {Boolean}
- * @api public
- */
- req.__defineGetter__('xhr', function(){
- var val = this.get('X-Requested-With') || '';
- return 'xmlhttprequest' == val.toLowerCase();
- });
|