twitst4tz

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

urlencoded.js (5797B)


      1 /*!
      2  * body-parser
      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 bytes = require('bytes')
     16 var contentType = require('content-type')
     17 var createError = require('http-errors')
     18 var debug = require('debug')('body-parser:urlencoded')
     19 var deprecate = require('depd')('body-parser')
     20 var read = require('../read')
     21 var typeis = require('type-is')
     22 
     23 /**
     24  * Module exports.
     25  */
     26 
     27 module.exports = urlencoded
     28 
     29 /**
     30  * Cache of parser modules.
     31  */
     32 
     33 var parsers = Object.create(null)
     34 
     35 /**
     36  * Create a middleware to parse urlencoded bodies.
     37  *
     38  * @param {object} [options]
     39  * @return {function}
     40  * @public
     41  */
     42 
     43 function urlencoded (options) {
     44   var opts = options || {}
     45 
     46   // notice because option default will flip in next major
     47   if (opts.extended === undefined) {
     48     deprecate('undefined extended: provide extended option')
     49   }
     50 
     51   var extended = opts.extended !== false
     52   var inflate = opts.inflate !== false
     53   var limit = typeof opts.limit !== 'number'
     54     ? bytes.parse(opts.limit || '100kb')
     55     : opts.limit
     56   var type = opts.type || 'application/x-www-form-urlencoded'
     57   var verify = opts.verify || false
     58 
     59   if (verify !== false && typeof verify !== 'function') {
     60     throw new TypeError('option verify must be function')
     61   }
     62 
     63   // create the appropriate query parser
     64   var queryparse = extended
     65     ? extendedparser(opts)
     66     : simpleparser(opts)
     67 
     68   // create the appropriate type checking function
     69   var shouldParse = typeof type !== 'function'
     70     ? typeChecker(type)
     71     : type
     72 
     73   function parse (body) {
     74     return body.length
     75       ? queryparse(body)
     76       : {}
     77   }
     78 
     79   return function urlencodedParser (req, res, next) {
     80     if (req._body) {
     81       debug('body already parsed')
     82       next()
     83       return
     84     }
     85 
     86     req.body = req.body || {}
     87 
     88     // skip requests without bodies
     89     if (!typeis.hasBody(req)) {
     90       debug('skip empty body')
     91       next()
     92       return
     93     }
     94 
     95     debug('content-type %j', req.headers['content-type'])
     96 
     97     // determine if request should be parsed
     98     if (!shouldParse(req)) {
     99       debug('skip parsing')
    100       next()
    101       return
    102     }
    103 
    104     // assert charset
    105     var charset = getCharset(req) || 'utf-8'
    106     if (charset !== 'utf-8') {
    107       debug('invalid charset')
    108       next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
    109         charset: charset,
    110         type: 'charset.unsupported'
    111       }))
    112       return
    113     }
    114 
    115     // read
    116     read(req, res, next, parse, debug, {
    117       debug: debug,
    118       encoding: charset,
    119       inflate: inflate,
    120       limit: limit,
    121       verify: verify
    122     })
    123   }
    124 }
    125 
    126 /**
    127  * Get the extended query parser.
    128  *
    129  * @param {object} options
    130  */
    131 
    132 function extendedparser (options) {
    133   var parameterLimit = options.parameterLimit !== undefined
    134     ? options.parameterLimit
    135     : 1000
    136   var parse = parser('qs')
    137 
    138   if (isNaN(parameterLimit) || parameterLimit < 1) {
    139     throw new TypeError('option parameterLimit must be a positive number')
    140   }
    141 
    142   if (isFinite(parameterLimit)) {
    143     parameterLimit = parameterLimit | 0
    144   }
    145 
    146   return function queryparse (body) {
    147     var paramCount = parameterCount(body, parameterLimit)
    148 
    149     if (paramCount === undefined) {
    150       debug('too many parameters')
    151       throw createError(413, 'too many parameters', {
    152         type: 'parameters.too.many'
    153       })
    154     }
    155 
    156     var arrayLimit = Math.max(100, paramCount)
    157 
    158     debug('parse extended urlencoding')
    159     return parse(body, {
    160       allowPrototypes: true,
    161       arrayLimit: arrayLimit,
    162       depth: Infinity,
    163       parameterLimit: parameterLimit
    164     })
    165   }
    166 }
    167 
    168 /**
    169  * Get the charset of a request.
    170  *
    171  * @param {object} req
    172  * @api private
    173  */
    174 
    175 function getCharset (req) {
    176   try {
    177     return (contentType.parse(req).parameters.charset || '').toLowerCase()
    178   } catch (e) {
    179     return undefined
    180   }
    181 }
    182 
    183 /**
    184  * Count the number of parameters, stopping once limit reached
    185  *
    186  * @param {string} body
    187  * @param {number} limit
    188  * @api private
    189  */
    190 
    191 function parameterCount (body, limit) {
    192   var count = 0
    193   var index = 0
    194 
    195   while ((index = body.indexOf('&', index)) !== -1) {
    196     count++
    197     index++
    198 
    199     if (count === limit) {
    200       return undefined
    201     }
    202   }
    203 
    204   return count
    205 }
    206 
    207 /**
    208  * Get parser for module name dynamically.
    209  *
    210  * @param {string} name
    211  * @return {function}
    212  * @api private
    213  */
    214 
    215 function parser (name) {
    216   var mod = parsers[name]
    217 
    218   if (mod !== undefined) {
    219     return mod.parse
    220   }
    221 
    222   // this uses a switch for static require analysis
    223   switch (name) {
    224     case 'qs':
    225       mod = require('qs')
    226       break
    227     case 'querystring':
    228       mod = require('querystring')
    229       break
    230   }
    231 
    232   // store to prevent invoking require()
    233   parsers[name] = mod
    234 
    235   return mod.parse
    236 }
    237 
    238 /**
    239  * Get the simple query parser.
    240  *
    241  * @param {object} options
    242  */
    243 
    244 function simpleparser (options) {
    245   var parameterLimit = options.parameterLimit !== undefined
    246     ? options.parameterLimit
    247     : 1000
    248   var parse = parser('querystring')
    249 
    250   if (isNaN(parameterLimit) || parameterLimit < 1) {
    251     throw new TypeError('option parameterLimit must be a positive number')
    252   }
    253 
    254   if (isFinite(parameterLimit)) {
    255     parameterLimit = parameterLimit | 0
    256   }
    257 
    258   return function queryparse (body) {
    259     var paramCount = parameterCount(body, parameterLimit)
    260 
    261     if (paramCount === undefined) {
    262       debug('too many parameters')
    263       throw createError(413, 'too many parameters', {
    264         type: 'parameters.too.many'
    265       })
    266     }
    267 
    268     debug('parse urlencoding')
    269     return parse(body, undefined, undefined, { maxKeys: parameterLimit })
    270   }
    271 }
    272 
    273 /**
    274  * Get the simple type checker.
    275  *
    276  * @param {string} type
    277  * @return {function}
    278  */
    279 
    280 function typeChecker (type) {
    281   return function checkType (req) {
    282     return Boolean(typeis(req, type))
    283   }
    284 }