twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

request.js (12504B)


      1 /*!
      2  * express
      3  * Copyright(c) 2009-2013 TJ Holowaychuk
      4  * Copyright(c) 2013 Roman Shtylman
      5  * Copyright(c) 2014-2015 Douglas Christopher Wilson
      6  * MIT Licensed
      7  */
      8 
      9 'use strict';
     10 
     11 /**
     12  * Module dependencies.
     13  * @private
     14  */
     15 
     16 var accepts = require('accepts');
     17 var deprecate = require('depd')('express');
     18 var isIP = require('net').isIP;
     19 var typeis = require('type-is');
     20 var http = require('http');
     21 var fresh = require('fresh');
     22 var parseRange = require('range-parser');
     23 var parse = require('parseurl');
     24 var proxyaddr = require('proxy-addr');
     25 
     26 /**
     27  * Request prototype.
     28  * @public
     29  */
     30 
     31 var req = Object.create(http.IncomingMessage.prototype)
     32 
     33 /**
     34  * Module exports.
     35  * @public
     36  */
     37 
     38 module.exports = req
     39 
     40 /**
     41  * Return request header.
     42  *
     43  * The `Referrer` header field is special-cased,
     44  * both `Referrer` and `Referer` are interchangeable.
     45  *
     46  * Examples:
     47  *
     48  *     req.get('Content-Type');
     49  *     // => "text/plain"
     50  *
     51  *     req.get('content-type');
     52  *     // => "text/plain"
     53  *
     54  *     req.get('Something');
     55  *     // => undefined
     56  *
     57  * Aliased as `req.header()`.
     58  *
     59  * @param {String} name
     60  * @return {String}
     61  * @public
     62  */
     63 
     64 req.get =
     65 req.header = function header(name) {
     66   if (!name) {
     67     throw new TypeError('name argument is required to req.get');
     68   }
     69 
     70   if (typeof name !== 'string') {
     71     throw new TypeError('name must be a string to req.get');
     72   }
     73 
     74   var lc = name.toLowerCase();
     75 
     76   switch (lc) {
     77     case 'referer':
     78     case 'referrer':
     79       return this.headers.referrer
     80         || this.headers.referer;
     81     default:
     82       return this.headers[lc];
     83   }
     84 };
     85 
     86 /**
     87  * To do: update docs.
     88  *
     89  * Check if the given `type(s)` is acceptable, returning
     90  * the best match when true, otherwise `undefined`, in which
     91  * case you should respond with 406 "Not Acceptable".
     92  *
     93  * The `type` value may be a single MIME type string
     94  * such as "application/json", an extension name
     95  * such as "json", a comma-delimited list such as "json, html, text/plain",
     96  * an argument list such as `"json", "html", "text/plain"`,
     97  * or an array `["json", "html", "text/plain"]`. When a list
     98  * or array is given, the _best_ match, if any is returned.
     99  *
    100  * Examples:
    101  *
    102  *     // Accept: text/html
    103  *     req.accepts('html');
    104  *     // => "html"
    105  *
    106  *     // Accept: text/*, application/json
    107  *     req.accepts('html');
    108  *     // => "html"
    109  *     req.accepts('text/html');
    110  *     // => "text/html"
    111  *     req.accepts('json, text');
    112  *     // => "json"
    113  *     req.accepts('application/json');
    114  *     // => "application/json"
    115  *
    116  *     // Accept: text/*, application/json
    117  *     req.accepts('image/png');
    118  *     req.accepts('png');
    119  *     // => undefined
    120  *
    121  *     // Accept: text/*;q=.5, application/json
    122  *     req.accepts(['html', 'json']);
    123  *     req.accepts('html', 'json');
    124  *     req.accepts('html, json');
    125  *     // => "json"
    126  *
    127  * @param {String|Array} type(s)
    128  * @return {String|Array|Boolean}
    129  * @public
    130  */
    131 
    132 req.accepts = function(){
    133   var accept = accepts(this);
    134   return accept.types.apply(accept, arguments);
    135 };
    136 
    137 /**
    138  * Check if the given `encoding`s are accepted.
    139  *
    140  * @param {String} ...encoding
    141  * @return {String|Array}
    142  * @public
    143  */
    144 
    145 req.acceptsEncodings = function(){
    146   var accept = accepts(this);
    147   return accept.encodings.apply(accept, arguments);
    148 };
    149 
    150 req.acceptsEncoding = deprecate.function(req.acceptsEncodings,
    151   'req.acceptsEncoding: Use acceptsEncodings instead');
    152 
    153 /**
    154  * Check if the given `charset`s are acceptable,
    155  * otherwise you should respond with 406 "Not Acceptable".
    156  *
    157  * @param {String} ...charset
    158  * @return {String|Array}
    159  * @public
    160  */
    161 
    162 req.acceptsCharsets = function(){
    163   var accept = accepts(this);
    164   return accept.charsets.apply(accept, arguments);
    165 };
    166 
    167 req.acceptsCharset = deprecate.function(req.acceptsCharsets,
    168   'req.acceptsCharset: Use acceptsCharsets instead');
    169 
    170 /**
    171  * Check if the given `lang`s are acceptable,
    172  * otherwise you should respond with 406 "Not Acceptable".
    173  *
    174  * @param {String} ...lang
    175  * @return {String|Array}
    176  * @public
    177  */
    178 
    179 req.acceptsLanguages = function(){
    180   var accept = accepts(this);
    181   return accept.languages.apply(accept, arguments);
    182 };
    183 
    184 req.acceptsLanguage = deprecate.function(req.acceptsLanguages,
    185   'req.acceptsLanguage: Use acceptsLanguages instead');
    186 
    187 /**
    188  * Parse Range header field, capping to the given `size`.
    189  *
    190  * Unspecified ranges such as "0-" require knowledge of your resource length. In
    191  * the case of a byte range this is of course the total number of bytes. If the
    192  * Range header field is not given `undefined` is returned, `-1` when unsatisfiable,
    193  * and `-2` when syntactically invalid.
    194  *
    195  * When ranges are returned, the array has a "type" property which is the type of
    196  * range that is required (most commonly, "bytes"). Each array element is an object
    197  * with a "start" and "end" property for the portion of the range.
    198  *
    199  * The "combine" option can be set to `true` and overlapping & adjacent ranges
    200  * will be combined into a single range.
    201  *
    202  * NOTE: remember that ranges are inclusive, so for example "Range: users=0-3"
    203  * should respond with 4 users when available, not 3.
    204  *
    205  * @param {number} size
    206  * @param {object} [options]
    207  * @param {boolean} [options.combine=false]
    208  * @return {number|array}
    209  * @public
    210  */
    211 
    212 req.range = function range(size, options) {
    213   var range = this.get('Range');
    214   if (!range) return;
    215   return parseRange(size, range, options);
    216 };
    217 
    218 /**
    219  * Return the value of param `name` when present or `defaultValue`.
    220  *
    221  *  - Checks route placeholders, ex: _/user/:id_
    222  *  - Checks body params, ex: id=12, {"id":12}
    223  *  - Checks query string params, ex: ?id=12
    224  *
    225  * To utilize request bodies, `req.body`
    226  * should be an object. This can be done by using
    227  * the `bodyParser()` middleware.
    228  *
    229  * @param {String} name
    230  * @param {Mixed} [defaultValue]
    231  * @return {String}
    232  * @public
    233  */
    234 
    235 req.param = function param(name, defaultValue) {
    236   var params = this.params || {};
    237   var body = this.body || {};
    238   var query = this.query || {};
    239 
    240   var args = arguments.length === 1
    241     ? 'name'
    242     : 'name, default';
    243   deprecate('req.param(' + args + '): Use req.params, req.body, or req.query instead');
    244 
    245   if (null != params[name] && params.hasOwnProperty(name)) return params[name];
    246   if (null != body[name]) return body[name];
    247   if (null != query[name]) return query[name];
    248 
    249   return defaultValue;
    250 };
    251 
    252 /**
    253  * Check if the incoming request contains the "Content-Type"
    254  * header field, and it contains the give mime `type`.
    255  *
    256  * Examples:
    257  *
    258  *      // With Content-Type: text/html; charset=utf-8
    259  *      req.is('html');
    260  *      req.is('text/html');
    261  *      req.is('text/*');
    262  *      // => true
    263  *
    264  *      // When Content-Type is application/json
    265  *      req.is('json');
    266  *      req.is('application/json');
    267  *      req.is('application/*');
    268  *      // => true
    269  *
    270  *      req.is('html');
    271  *      // => false
    272  *
    273  * @param {String|Array} types...
    274  * @return {String|false|null}
    275  * @public
    276  */
    277 
    278 req.is = function is(types) {
    279   var arr = types;
    280 
    281   // support flattened arguments
    282   if (!Array.isArray(types)) {
    283     arr = new Array(arguments.length);
    284     for (var i = 0; i < arr.length; i++) {
    285       arr[i] = arguments[i];
    286     }
    287   }
    288 
    289   return typeis(this, arr);
    290 };
    291 
    292 /**
    293  * Return the protocol string "http" or "https"
    294  * when requested with TLS. When the "trust proxy"
    295  * setting trusts the socket address, the
    296  * "X-Forwarded-Proto" header field will be trusted
    297  * and used if present.
    298  *
    299  * If you're running behind a reverse proxy that
    300  * supplies https for you this may be enabled.
    301  *
    302  * @return {String}
    303  * @public
    304  */
    305 
    306 defineGetter(req, 'protocol', function protocol(){
    307   var proto = this.connection.encrypted
    308     ? 'https'
    309     : 'http';
    310   var trust = this.app.get('trust proxy fn');
    311 
    312   if (!trust(this.connection.remoteAddress, 0)) {
    313     return proto;
    314   }
    315 
    316   // Note: X-Forwarded-Proto is normally only ever a
    317   //       single value, but this is to be safe.
    318   var header = this.get('X-Forwarded-Proto') || proto
    319   var index = header.indexOf(',')
    320 
    321   return index !== -1
    322     ? header.substring(0, index).trim()
    323     : header.trim()
    324 });
    325 
    326 /**
    327  * Short-hand for:
    328  *
    329  *    req.protocol === 'https'
    330  *
    331  * @return {Boolean}
    332  * @public
    333  */
    334 
    335 defineGetter(req, 'secure', function secure(){
    336   return this.protocol === 'https';
    337 });
    338 
    339 /**
    340  * Return the remote address from the trusted proxy.
    341  *
    342  * The is the remote address on the socket unless
    343  * "trust proxy" is set.
    344  *
    345  * @return {String}
    346  * @public
    347  */
    348 
    349 defineGetter(req, 'ip', function ip(){
    350   var trust = this.app.get('trust proxy fn');
    351   return proxyaddr(this, trust);
    352 });
    353 
    354 /**
    355  * When "trust proxy" is set, trusted proxy addresses + client.
    356  *
    357  * For example if the value were "client, proxy1, proxy2"
    358  * you would receive the array `["client", "proxy1", "proxy2"]`
    359  * where "proxy2" is the furthest down-stream and "proxy1" and
    360  * "proxy2" were trusted.
    361  *
    362  * @return {Array}
    363  * @public
    364  */
    365 
    366 defineGetter(req, 'ips', function ips() {
    367   var trust = this.app.get('trust proxy fn');
    368   var addrs = proxyaddr.all(this, trust);
    369 
    370   // reverse the order (to farthest -> closest)
    371   // and remove socket address
    372   addrs.reverse().pop()
    373 
    374   return addrs
    375 });
    376 
    377 /**
    378  * Return subdomains as an array.
    379  *
    380  * Subdomains are the dot-separated parts of the host before the main domain of
    381  * the app. By default, the domain of the app is assumed to be the last two
    382  * parts of the host. This can be changed by setting "subdomain offset".
    383  *
    384  * For example, if the domain is "tobi.ferrets.example.com":
    385  * If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`.
    386  * If "subdomain offset" is 3, req.subdomains is `["tobi"]`.
    387  *
    388  * @return {Array}
    389  * @public
    390  */
    391 
    392 defineGetter(req, 'subdomains', function subdomains() {
    393   var hostname = this.hostname;
    394 
    395   if (!hostname) return [];
    396 
    397   var offset = this.app.get('subdomain offset');
    398   var subdomains = !isIP(hostname)
    399     ? hostname.split('.').reverse()
    400     : [hostname];
    401 
    402   return subdomains.slice(offset);
    403 });
    404 
    405 /**
    406  * Short-hand for `url.parse(req.url).pathname`.
    407  *
    408  * @return {String}
    409  * @public
    410  */
    411 
    412 defineGetter(req, 'path', function path() {
    413   return parse(this).pathname;
    414 });
    415 
    416 /**
    417  * Parse the "Host" header field to a hostname.
    418  *
    419  * When the "trust proxy" setting trusts the socket
    420  * address, the "X-Forwarded-Host" header field will
    421  * be trusted.
    422  *
    423  * @return {String}
    424  * @public
    425  */
    426 
    427 defineGetter(req, 'hostname', function hostname(){
    428   var trust = this.app.get('trust proxy fn');
    429   var host = this.get('X-Forwarded-Host');
    430 
    431   if (!host || !trust(this.connection.remoteAddress, 0)) {
    432     host = this.get('Host');
    433   } else if (host.indexOf(',') !== -1) {
    434     // Note: X-Forwarded-Host is normally only ever a
    435     //       single value, but this is to be safe.
    436     host = host.substring(0, host.indexOf(',')).trimRight()
    437   }
    438 
    439   if (!host) return;
    440 
    441   // IPv6 literal support
    442   var offset = host[0] === '['
    443     ? host.indexOf(']') + 1
    444     : 0;
    445   var index = host.indexOf(':', offset);
    446 
    447   return index !== -1
    448     ? host.substring(0, index)
    449     : host;
    450 });
    451 
    452 // TODO: change req.host to return host in next major
    453 
    454 defineGetter(req, 'host', deprecate.function(function host(){
    455   return this.hostname;
    456 }, 'req.host: Use req.hostname instead'));
    457 
    458 /**
    459  * Check if the request is fresh, aka
    460  * Last-Modified and/or the ETag
    461  * still match.
    462  *
    463  * @return {Boolean}
    464  * @public
    465  */
    466 
    467 defineGetter(req, 'fresh', function(){
    468   var method = this.method;
    469   var res = this.res
    470   var status = res.statusCode
    471 
    472   // GET or HEAD for weak freshness validation only
    473   if ('GET' !== method && 'HEAD' !== method) return false;
    474 
    475   // 2xx or 304 as per rfc2616 14.26
    476   if ((status >= 200 && status < 300) || 304 === status) {
    477     return fresh(this.headers, {
    478       'etag': res.get('ETag'),
    479       'last-modified': res.get('Last-Modified')
    480     })
    481   }
    482 
    483   return false;
    484 });
    485 
    486 /**
    487  * Check if the request is stale, aka
    488  * "Last-Modified" and / or the "ETag" for the
    489  * resource has changed.
    490  *
    491  * @return {Boolean}
    492  * @public
    493  */
    494 
    495 defineGetter(req, 'stale', function stale(){
    496   return !this.fresh;
    497 });
    498 
    499 /**
    500  * Check if the request was an _XMLHttpRequest_.
    501  *
    502  * @return {Boolean}
    503  * @public
    504  */
    505 
    506 defineGetter(req, 'xhr', function xhr(){
    507   var val = this.get('X-Requested-With') || '';
    508   return val.toLowerCase() === 'xmlhttprequest';
    509 });
    510 
    511 /**
    512  * Helper function for creating a getter on an object.
    513  *
    514  * @param {Object} obj
    515  * @param {String} name
    516  * @param {Function} getter
    517  * @private
    518  */
    519 function defineGetter(obj, name, getter) {
    520   Object.defineProperty(obj, name, {
    521     configurable: true,
    522     enumerable: true,
    523     get: getter
    524   });
    525 }