second
[josuexyz/.git] / 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       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 }