twitst4tz

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

index.js (5562B)


      1 /*!
      2  * type-is
      3  * Copyright(c) 2014 Jonathan Ong
      4  * Copyright(c) 2014-2015 Douglas Christopher Wilson
      5  * MIT Licensed
      6  */
      7 
      8 'use strict'
      9 
     10 /**
     11  * Module dependencies.
     12  * @private
     13  */
     14 
     15 var typer = require('media-typer')
     16 var mime = require('mime-types')
     17 
     18 /**
     19  * Module exports.
     20  * @public
     21  */
     22 
     23 module.exports = typeofrequest
     24 module.exports.is = typeis
     25 module.exports.hasBody = hasbody
     26 module.exports.normalize = normalize
     27 module.exports.match = mimeMatch
     28 
     29 /**
     30  * Compare a `value` content-type with `types`.
     31  * Each `type` can be an extension like `html`,
     32  * a special shortcut like `multipart` or `urlencoded`,
     33  * or a mime type.
     34  *
     35  * If no types match, `false` is returned.
     36  * Otherwise, the first `type` that matches is returned.
     37  *
     38  * @param {String} value
     39  * @param {Array} types
     40  * @public
     41  */
     42 
     43 function typeis (value, types_) {
     44   var i
     45   var types = types_
     46 
     47   // remove parameters and normalize
     48   var val = tryNormalizeType(value)
     49 
     50   // no type or invalid
     51   if (!val) {
     52     return false
     53   }
     54 
     55   // support flattened arguments
     56   if (types && !Array.isArray(types)) {
     57     types = new Array(arguments.length - 1)
     58     for (i = 0; i < types.length; i++) {
     59       types[i] = arguments[i + 1]
     60     }
     61   }
     62 
     63   // no types, return the content type
     64   if (!types || !types.length) {
     65     return val
     66   }
     67 
     68   var type
     69   for (i = 0; i < types.length; i++) {
     70     if (mimeMatch(normalize(type = types[i]), val)) {
     71       return type[0] === '+' || type.indexOf('*') !== -1
     72         ? val
     73         : type
     74     }
     75   }
     76 
     77   // no matches
     78   return false
     79 }
     80 
     81 /**
     82  * Check if a request has a request body.
     83  * A request with a body __must__ either have `transfer-encoding`
     84  * or `content-length` headers set.
     85  * http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3
     86  *
     87  * @param {Object} request
     88  * @return {Boolean}
     89  * @public
     90  */
     91 
     92 function hasbody (req) {
     93   return req.headers['transfer-encoding'] !== undefined ||
     94     !isNaN(req.headers['content-length'])
     95 }
     96 
     97 /**
     98  * Check if the incoming request contains the "Content-Type"
     99  * header field, and it contains any of the give mime `type`s.
    100  * If there is no request body, `null` is returned.
    101  * If there is no content type, `false` is returned.
    102  * Otherwise, it returns the first `type` that matches.
    103  *
    104  * Examples:
    105  *
    106  *     // With Content-Type: text/html; charset=utf-8
    107  *     this.is('html'); // => 'html'
    108  *     this.is('text/html'); // => 'text/html'
    109  *     this.is('text/*', 'application/json'); // => 'text/html'
    110  *
    111  *     // When Content-Type is application/json
    112  *     this.is('json', 'urlencoded'); // => 'json'
    113  *     this.is('application/json'); // => 'application/json'
    114  *     this.is('html', 'application/*'); // => 'application/json'
    115  *
    116  *     this.is('html'); // => false
    117  *
    118  * @param {String|Array} types...
    119  * @return {String|false|null}
    120  * @public
    121  */
    122 
    123 function typeofrequest (req, types_) {
    124   var types = types_
    125 
    126   // no body
    127   if (!hasbody(req)) {
    128     return null
    129   }
    130 
    131   // support flattened arguments
    132   if (arguments.length > 2) {
    133     types = new Array(arguments.length - 1)
    134     for (var i = 0; i < types.length; i++) {
    135       types[i] = arguments[i + 1]
    136     }
    137   }
    138 
    139   // request content type
    140   var value = req.headers['content-type']
    141 
    142   return typeis(value, types)
    143 }
    144 
    145 /**
    146  * Normalize a mime type.
    147  * If it's a shorthand, expand it to a valid mime type.
    148  *
    149  * In general, you probably want:
    150  *
    151  *   var type = is(req, ['urlencoded', 'json', 'multipart']);
    152  *
    153  * Then use the appropriate body parsers.
    154  * These three are the most common request body types
    155  * and are thus ensured to work.
    156  *
    157  * @param {String} type
    158  * @private
    159  */
    160 
    161 function normalize (type) {
    162   if (typeof type !== 'string') {
    163     // invalid type
    164     return false
    165   }
    166 
    167   switch (type) {
    168     case 'urlencoded':
    169       return 'application/x-www-form-urlencoded'
    170     case 'multipart':
    171       return 'multipart/*'
    172   }
    173 
    174   if (type[0] === '+') {
    175     // "+json" -> "*/*+json" expando
    176     return '*/*' + type
    177   }
    178 
    179   return type.indexOf('/') === -1
    180     ? mime.lookup(type)
    181     : type
    182 }
    183 
    184 /**
    185  * Check if `expected` mime type
    186  * matches `actual` mime type with
    187  * wildcard and +suffix support.
    188  *
    189  * @param {String} expected
    190  * @param {String} actual
    191  * @return {Boolean}
    192  * @private
    193  */
    194 
    195 function mimeMatch (expected, actual) {
    196   // invalid type
    197   if (expected === false) {
    198     return false
    199   }
    200 
    201   // split types
    202   var actualParts = actual.split('/')
    203   var expectedParts = expected.split('/')
    204 
    205   // invalid format
    206   if (actualParts.length !== 2 || expectedParts.length !== 2) {
    207     return false
    208   }
    209 
    210   // validate type
    211   if (expectedParts[0] !== '*' && expectedParts[0] !== actualParts[0]) {
    212     return false
    213   }
    214 
    215   // validate suffix wildcard
    216   if (expectedParts[1].substr(0, 2) === '*+') {
    217     return expectedParts[1].length <= actualParts[1].length + 1 &&
    218       expectedParts[1].substr(1) === actualParts[1].substr(1 - expectedParts[1].length)
    219   }
    220 
    221   // validate subtype
    222   if (expectedParts[1] !== '*' && expectedParts[1] !== actualParts[1]) {
    223     return false
    224   }
    225 
    226   return true
    227 }
    228 
    229 /**
    230  * Normalize a type and remove parameters.
    231  *
    232  * @param {string} value
    233  * @return {string}
    234  * @private
    235  */
    236 
    237 function normalizeType (value) {
    238   // parse the type
    239   var type = typer.parse(value)
    240 
    241   // remove the parameters
    242   type.parameters = undefined
    243 
    244   // reformat it
    245   return typer.format(type)
    246 }
    247 
    248 /**
    249  * Try to normalize a type and remove parameters.
    250  *
    251  * @param {string} value
    252  * @return {string}
    253  * @private
    254  */
    255 
    256 function tryNormalizeType (value) {
    257   if (!value) {
    258     return null
    259   }
    260 
    261   try {
    262     return normalizeType(value)
    263   } catch (err) {
    264     return null
    265   }
    266 }