Initial commit
[yaffs-website] / node_modules / body-parser / lib / types / urlencoded.js
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       return debug('body already parsed'), next()
82     }
83
84     req.body = req.body || {}
85
86     // skip requests without bodies
87     if (!typeis.hasBody(req)) {
88       return debug('skip empty body'), next()
89     }
90
91     debug('content-type %j', req.headers['content-type'])
92
93     // determine if request should be parsed
94     if (!shouldParse(req)) {
95       return debug('skip parsing'), next()
96     }
97
98     // assert charset
99     var charset = getCharset(req) || 'utf-8'
100     if (charset !== 'utf-8') {
101       debug('invalid charset')
102       next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
103         charset: charset
104       }))
105       return
106     }
107
108     // read
109     read(req, res, next, parse, debug, {
110       debug: debug,
111       encoding: charset,
112       inflate: inflate,
113       limit: limit,
114       verify: verify
115     })
116   }
117 }
118
119 /**
120  * Get the extended query parser.
121  *
122  * @param {object} options
123  */
124
125 function extendedparser(options) {
126   var parameterLimit = options.parameterLimit !== undefined
127     ? options.parameterLimit
128     : 1000
129   var parse = parser('qs')
130
131   if (isNaN(parameterLimit) || parameterLimit < 1) {
132     throw new TypeError('option parameterLimit must be a positive number')
133   }
134
135   if (isFinite(parameterLimit)) {
136     parameterLimit = parameterLimit | 0
137   }
138
139   return function queryparse(body) {
140     var paramCount = parameterCount(body, parameterLimit)
141
142     if (paramCount === undefined) {
143       debug('too many parameters')
144       throw createError(413, 'too many parameters')
145     }
146
147     var arrayLimit = Math.max(100, paramCount)
148
149     debug('parse extended urlencoding')
150     return parse(body, {
151       allowPrototypes: true,
152       arrayLimit: arrayLimit,
153       depth: Infinity,
154       parameterLimit: parameterLimit
155     })
156   }
157 }
158
159 /**
160  * Get the charset of a request.
161  *
162  * @param {object} req
163  * @api private
164  */
165
166 function getCharset(req) {
167   try {
168     return contentType.parse(req).parameters.charset.toLowerCase()
169   } catch (e) {
170     return undefined
171   }
172 }
173
174 /**
175  * Count the number of parameters, stopping once limit reached
176  *
177  * @param {string} body
178  * @param {number} limit
179  * @api private
180  */
181
182 function parameterCount(body, limit) {
183   var count = 0
184   var index = 0
185
186   while ((index = body.indexOf('&', index)) !== -1) {
187     count++
188     index++
189
190     if (count === limit) {
191       return undefined
192     }
193   }
194
195   return count
196 }
197
198 /**
199  * Get parser for module name dynamically.
200  *
201  * @param {string} name
202  * @return {function}
203  * @api private
204  */
205
206 function parser(name) {
207   var mod = parsers[name]
208
209   if (mod !== undefined) {
210     return mod.parse
211   }
212
213   // this uses a switch for static require analysis
214   switch (name) {
215     case 'qs':
216       mod = require('qs')
217       break
218     case 'querystring':
219       mod = require('querystring')
220       break
221   }
222
223   // store to prevent invoking require()
224   parsers[name] = mod
225
226   return mod.parse
227 }
228
229 /**
230  * Get the simple query parser.
231  *
232  * @param {object} options
233  */
234
235 function simpleparser(options) {
236   var parameterLimit = options.parameterLimit !== undefined
237     ? options.parameterLimit
238     : 1000
239   var parse = parser('querystring')
240
241   if (isNaN(parameterLimit) || parameterLimit < 1) {
242     throw new TypeError('option parameterLimit must be a positive number')
243   }
244
245   if (isFinite(parameterLimit)) {
246     parameterLimit = parameterLimit | 0
247   }
248
249   return function queryparse(body) {
250     var paramCount = parameterCount(body, parameterLimit)
251
252     if (paramCount === undefined) {
253       debug('too many parameters')
254       throw createError(413, 'too many parameters')
255     }
256
257     debug('parse urlencoding')
258     return parse(body, undefined, undefined, {maxKeys: parameterLimit})
259   }
260 }
261
262 /**
263  * Get the simple type checker.
264  *
265  * @param {string} type
266  * @return {function}
267  */
268
269 function typeChecker(type) {
270   return function checkType(req) {
271     return Boolean(typeis(req, type))
272   }
273 }