aboutsummaryrefslogtreecommitdiffstats
path: root/node_modules/body-parser/lib
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--node_modules/body-parser/lib/read.js247
-rw-r--r--node_modules/body-parser/lib/types/json.js186
-rw-r--r--node_modules/body-parser/lib/types/raw.js42
-rw-r--r--node_modules/body-parser/lib/types/text.js36
-rw-r--r--node_modules/body-parser/lib/types/urlencoded.js138
-rw-r--r--node_modules/body-parser/lib/utils.js100
6 files changed, 749 insertions, 0 deletions
diff --git a/node_modules/body-parser/lib/read.js b/node_modules/body-parser/lib/read.js
new file mode 100644
index 0000000..bf1a1df
--- /dev/null
+++ b/node_modules/body-parser/lib/read.js
@@ -0,0 +1,247 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ * @private
+ */
+
+const createError = require('http-errors')
+const getBody = require('raw-body')
+const iconv = require('iconv-lite')
+const onFinished = require('on-finished')
+const zlib = require('node:zlib')
+const hasBody = require('type-is').hasBody
+const { getCharset } = require('./utils')
+
+/**
+ * Module exports.
+ */
+
+module.exports = read
+
+/**
+ * Read a request into a buffer and parse.
+ *
+ * @param {Object} req
+ * @param {Object} res
+ * @param {Function} next
+ * @param {Function} parse
+ * @param {Function} debug
+ * @param {Object} options
+ * @private
+ */
+function read (req, res, next, parse, debug, options) {
+ if (onFinished.isFinished(req)) {
+ debug('body already parsed')
+ next()
+ return
+ }
+
+ if (!('body' in req)) {
+ req.body = undefined
+ }
+
+ // skip requests without bodies
+ if (!hasBody(req)) {
+ debug('skip empty body')
+ next()
+ return
+ }
+
+ debug('content-type %j', req.headers['content-type'])
+
+ // determine if request should be parsed
+ if (!options.shouldParse(req)) {
+ debug('skip parsing')
+ next()
+ return
+ }
+
+ let encoding = null
+ if (options?.skipCharset !== true) {
+ encoding = getCharset(req) || options.defaultCharset
+
+ // validate charset
+ if (!!options?.isValidCharset && !options.isValidCharset(encoding)) {
+ debug('invalid charset')
+ next(createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
+ charset: encoding,
+ type: 'charset.unsupported'
+ }))
+ return
+ }
+ }
+
+ let length
+ const opts = options
+ let stream
+
+ // read options
+ const verify = opts.verify
+
+ try {
+ // get the content stream
+ stream = contentstream(req, debug, opts.inflate)
+ length = stream.length
+ stream.length = undefined
+ } catch (err) {
+ return next(err)
+ }
+
+ // set raw-body options
+ opts.length = length
+ opts.encoding = verify
+ ? null
+ : encoding
+
+ // assert charset is supported
+ if (opts.encoding === null && encoding !== null && !iconv.encodingExists(encoding)) {
+ return next(createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
+ charset: encoding.toLowerCase(),
+ type: 'charset.unsupported'
+ }))
+ }
+
+ // read body
+ debug('read body')
+ getBody(stream, opts, function (error, body) {
+ if (error) {
+ let _error
+
+ if (error.type === 'encoding.unsupported') {
+ // echo back charset
+ _error = createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
+ charset: encoding.toLowerCase(),
+ type: 'charset.unsupported'
+ })
+ } else {
+ // set status code on error
+ _error = createError(400, error)
+ }
+
+ // unpipe from stream and destroy
+ if (stream !== req) {
+ req.unpipe()
+ stream.destroy()
+ }
+
+ // read off entire request
+ dump(req, function onfinished () {
+ next(createError(400, _error))
+ })
+ return
+ }
+
+ // verify
+ if (verify) {
+ try {
+ debug('verify body')
+ verify(req, res, body, encoding)
+ } catch (err) {
+ next(createError(403, err, {
+ body: body,
+ type: err.type || 'entity.verify.failed'
+ }))
+ return
+ }
+ }
+
+ // parse
+ let str = body
+ try {
+ debug('parse body')
+ str = typeof body !== 'string' && encoding !== null
+ ? iconv.decode(body, encoding)
+ : body
+ req.body = parse(str, encoding)
+ } catch (err) {
+ next(createError(400, err, {
+ body: str,
+ type: err.type || 'entity.parse.failed'
+ }))
+ return
+ }
+
+ next()
+ })
+}
+
+/**
+ * Get the content stream of the request.
+ *
+ * @param {Object} req
+ * @param {Function} debug
+ * @param {boolean} inflate
+ * @returns {Object}
+ * @private
+ */
+function contentstream (req, debug, inflate) {
+ const encoding = (req.headers['content-encoding'] || 'identity').toLowerCase()
+ const length = req.headers['content-length']
+
+ debug('content-encoding "%s"', encoding)
+
+ if (inflate === false && encoding !== 'identity') {
+ throw createError(415, 'content encoding unsupported', {
+ encoding: encoding,
+ type: 'encoding.unsupported'
+ })
+ }
+
+ if (encoding === 'identity') {
+ req.length = length
+ return req
+ }
+
+ const stream = createDecompressionStream(encoding, debug)
+ req.pipe(stream)
+ return stream
+}
+
+/**
+ * Create a decompression stream for the given encoding.
+ * @param {string} encoding
+ * @param {Function} debug
+ * @returns {Object}
+ * @private
+ */
+function createDecompressionStream (encoding, debug) {
+ switch (encoding) {
+ case 'deflate':
+ debug('inflate body')
+ return zlib.createInflate()
+ case 'gzip':
+ debug('gunzip body')
+ return zlib.createGunzip()
+ case 'br':
+ debug('brotli decompress body')
+ return zlib.createBrotliDecompress()
+ default:
+ throw createError(415, 'unsupported content encoding "' + encoding + '"', {
+ encoding: encoding,
+ type: 'encoding.unsupported'
+ })
+ }
+}
+
+/**
+ * Dump the contents of a request.
+ *
+ * @param {Object} req
+ * @param {Function} callback
+ * @private
+ */
+function dump (req, callback) {
+ if (onFinished.isFinished(req)) {
+ callback(null)
+ } else {
+ onFinished(req, callback)
+ req.resume()
+ }
+}
diff --git a/node_modules/body-parser/lib/types/json.js b/node_modules/body-parser/lib/types/json.js
new file mode 100644
index 0000000..14ca3e4
--- /dev/null
+++ b/node_modules/body-parser/lib/types/json.js
@@ -0,0 +1,186 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014 Jonathan Ong
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ * @private
+ */
+
+const debug = require('debug')('body-parser:json')
+const read = require('../read')
+const { normalizeOptions } = require('../utils')
+
+/**
+ * Module exports.
+ */
+
+module.exports = json
+
+/**
+ * RegExp to match the first non-space in a string.
+ *
+ * Allowed whitespace is defined in RFC 7159:
+ *
+ * ws = *(
+ * %x20 / ; Space
+ * %x09 / ; Horizontal tab
+ * %x0A / ; Line feed or New line
+ * %x0D ) ; Carriage return
+ */
+const FIRST_CHAR_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d])/ // eslint-disable-line no-control-regex
+
+const JSON_SYNTAX_CHAR = '#'
+const JSON_SYNTAX_REGEXP = /#+/g
+
+/**
+ * Create a middleware to parse JSON bodies.
+ *
+ * @param {Object} [options]
+ * @returns {Function}
+ * @public
+ */
+function json (options) {
+ const normalizedOptions = normalizeOptions(options, 'application/json')
+
+ const parse = createJsonParser(options)
+
+ const readOptions = {
+ ...normalizedOptions,
+ // assert charset per RFC 7159 sec 8.1
+ isValidCharset: (charset) => charset.slice(0, 4) === 'utf-'
+ }
+
+ return function jsonParser (req, res, next) {
+ read(req, res, next, parse, debug, readOptions)
+ }
+}
+
+/**
+ * Create a JSON parse function
+ *
+ * @param {object} [options]
+ * @return {function}
+ * @private
+ */
+function createJsonParser (options) {
+ const reviver = options?.reviver
+ const strict = options?.strict !== false
+
+ if (strict) {
+ return function parse (body) {
+ if (body.length === 0) {
+ // special-case empty json body, as it's a common client-side mistake
+ // TODO: maybe make this configurable or part of "strict" option
+ return {}
+ }
+
+ const first = firstchar(body)
+ if (first !== '{' && first !== '[') {
+ debug('strict violation')
+ throw createStrictSyntaxError(body, first)
+ }
+
+ try {
+ debug('parse json')
+ return JSON.parse(body, reviver)
+ } catch (e) {
+ throw normalizeJsonSyntaxError(e, {
+ message: e.message,
+ stack: e.stack
+ })
+ }
+ }
+ }
+
+ return function parse (body) {
+ if (body.length === 0) {
+ // special-case empty json body, as it's a common client-side mistake
+ // TODO: maybe make this configurable or part of "strict" option
+ return {}
+ }
+
+ try {
+ debug('parse json')
+ return JSON.parse(body, reviver)
+ } catch (e) {
+ throw normalizeJsonSyntaxError(e, {
+ message: e.message,
+ stack: e.stack
+ })
+ }
+ }
+}
+
+/**
+ * Create strict violation syntax error matching native error.
+ *
+ * @param {string} str
+ * @param {string} char
+ * @returns {Error}
+ * @private
+ */
+function createStrictSyntaxError (str, char) {
+ const index = str.indexOf(char)
+ let partial = ''
+
+ if (index !== -1) {
+ partial = str.substring(0, index) + JSON_SYNTAX_CHAR.repeat(str.length - index)
+ }
+
+ try {
+ JSON.parse(partial); /* istanbul ignore next */ throw new SyntaxError('strict violation')
+ } catch (e) {
+ return normalizeJsonSyntaxError(e, {
+ message: e.message.replace(JSON_SYNTAX_REGEXP, function (placeholder) {
+ return str.substring(index, index + placeholder.length)
+ }),
+ stack: e.stack
+ })
+ }
+}
+
+/**
+ * Get the first non-whitespace character in a string.
+ *
+ * @param {string} str
+ * @returns {string|undefined}
+ * @private
+ */
+function firstchar (str) {
+ const match = FIRST_CHAR_REGEXP.exec(str)
+
+ return match
+ ? match[1]
+ : undefined
+}
+
+/**
+ * Normalize a SyntaxError for JSON.parse.
+ *
+ * @param {SyntaxError} error
+ * @param {Object} obj
+ * @returns {SyntaxError}
+ * @private
+ */
+function normalizeJsonSyntaxError (error, obj) {
+ const keys = Object.getOwnPropertyNames(error)
+
+ for (let i = 0; i < keys.length; i++) {
+ const key = keys[i]
+ if (key !== 'stack' && key !== 'message') {
+ delete error[key]
+ }
+ }
+
+ // replace stack before message for Node.js 0.10 and below
+ error.stack = obj.stack.replace(error.message, obj.message)
+ error.message = obj.message
+
+ return error
+}
diff --git a/node_modules/body-parser/lib/types/raw.js b/node_modules/body-parser/lib/types/raw.js
new file mode 100644
index 0000000..c84083b
--- /dev/null
+++ b/node_modules/body-parser/lib/types/raw.js
@@ -0,0 +1,42 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const debug = require('debug')('body-parser:raw')
+const read = require('../read')
+const { normalizeOptions, passthrough } = require('../utils')
+
+/**
+ * Module exports.
+ */
+
+module.exports = raw
+
+/**
+ * Create a middleware to parse raw bodies.
+ *
+ * @param {Object} [options]
+ * @returns {Function}
+ * @public
+ */
+function raw (options) {
+ const normalizedOptions = normalizeOptions(options, 'application/octet-stream')
+
+ const readOptions = {
+ ...normalizedOptions,
+ // Skip charset validation and parse the body as is
+ skipCharset: true
+ }
+
+ return function rawParser (req, res, next) {
+ read(req, res, next, passthrough, debug, readOptions)
+ }
+}
diff --git a/node_modules/body-parser/lib/types/text.js b/node_modules/body-parser/lib/types/text.js
new file mode 100644
index 0000000..893c801
--- /dev/null
+++ b/node_modules/body-parser/lib/types/text.js
@@ -0,0 +1,36 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const debug = require('debug')('body-parser:text')
+const read = require('../read')
+const { normalizeOptions, passthrough } = require('../utils')
+
+/**
+ * Module exports.
+ */
+
+module.exports = text
+
+/**
+ * Create a middleware to parse text bodies.
+ *
+ * @param {Object} [options]
+ * @returns {Function}
+ * @public
+ */
+function text (options) {
+ const normalizedOptions = normalizeOptions(options, 'text/plain')
+
+ return function textParser (req, res, next) {
+ read(req, res, next, passthrough, debug, normalizedOptions)
+ }
+}
diff --git a/node_modules/body-parser/lib/types/urlencoded.js b/node_modules/body-parser/lib/types/urlencoded.js
new file mode 100644
index 0000000..1db964c
--- /dev/null
+++ b/node_modules/body-parser/lib/types/urlencoded.js
@@ -0,0 +1,138 @@
+/*!
+ * body-parser
+ * Copyright(c) 2014 Jonathan Ong
+ * Copyright(c) 2014-2015 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+'use strict'
+
+/**
+ * Module dependencies.
+ * @private
+ */
+
+const createError = require('http-errors')
+const debug = require('debug')('body-parser:urlencoded')
+const read = require('../read')
+const qs = require('qs')
+const { normalizeOptions } = require('../utils')
+
+/**
+ * Module exports.
+ */
+
+module.exports = urlencoded
+
+/**
+ * Create a middleware to parse urlencoded bodies.
+ *
+ * @param {Object} [options]
+ * @returns {Function}
+ * @public
+ */
+function urlencoded (options) {
+ const normalizedOptions = normalizeOptions(options, 'application/x-www-form-urlencoded')
+
+ if (normalizedOptions.defaultCharset !== 'utf-8' && normalizedOptions.defaultCharset !== 'iso-8859-1') {
+ throw new TypeError('option defaultCharset must be either utf-8 or iso-8859-1')
+ }
+
+ // create the appropriate query parser
+ const parse = createQueryParser(options)
+
+ const readOptions = {
+ ...normalizedOptions,
+ // assert charset
+ isValidCharset: (charset) => charset === 'utf-8' || charset === 'iso-8859-1'
+ }
+
+ return function urlencodedParser (req, res, next) {
+ read(req, res, next, parse, debug, readOptions)
+ }
+}
+
+/**
+ * Get the extended query parser.
+ *
+ * @param {Object} options
+ * @returns {Function}
+ * @private
+ */
+function createQueryParser (options) {
+ const extended = Boolean(options?.extended)
+ let parameterLimit = options?.parameterLimit !== undefined
+ ? options?.parameterLimit
+ : 1000
+ const charsetSentinel = options?.charsetSentinel
+ const interpretNumericEntities = options?.interpretNumericEntities
+ const depth = extended ? (options?.depth !== undefined ? options?.depth : 32) : 0
+
+ if (isNaN(parameterLimit) || parameterLimit < 1) {
+ throw new TypeError('option parameterLimit must be a positive number')
+ }
+
+ if (isNaN(depth) || depth < 0) {
+ throw new TypeError('option depth must be a zero or a positive number')
+ }
+
+ if (isFinite(parameterLimit)) {
+ parameterLimit = parameterLimit | 0
+ }
+
+ return function parse (body, encoding) {
+ if (!body.length) return {}
+
+ const paramCount = parameterCount(body, parameterLimit)
+
+ if (paramCount === undefined) {
+ debug('too many parameters')
+ throw createError(413, 'too many parameters', {
+ type: 'parameters.too.many'
+ })
+ }
+
+ const arrayLimit = extended ? Math.max(100, paramCount) : paramCount
+
+ debug('parse ' + (extended ? 'extended ' : '') + 'urlencoding')
+ try {
+ return qs.parse(body, {
+ allowPrototypes: true,
+ arrayLimit: arrayLimit,
+ depth: depth,
+ charsetSentinel: charsetSentinel,
+ interpretNumericEntities: interpretNumericEntities,
+ charset: encoding,
+ parameterLimit: parameterLimit,
+ strictDepth: true
+ })
+ } catch (err) {
+ if (err instanceof RangeError) {
+ throw createError(400, 'The input exceeded the depth', {
+ type: 'querystring.parse.rangeError'
+ })
+ } else {
+ throw err
+ }
+ }
+ }
+}
+
+/**
+ * Count the number of parameters, stopping once limit reached
+ *
+ * @param {string} body
+ * @param {number} limit
+ * @returns {number|undefined} Returns undefined if limit exceeded
+ * @private
+ */
+function parameterCount (body, limit) {
+ let count = 0
+ let index = -1
+ do {
+ count++
+ if (count > limit) return undefined // Early exit if limit exceeded
+ index = body.indexOf('&', index + 1)
+ } while (index !== -1)
+ return count
+}
diff --git a/node_modules/body-parser/lib/utils.js b/node_modules/body-parser/lib/utils.js
new file mode 100644
index 0000000..7a0dda4
--- /dev/null
+++ b/node_modules/body-parser/lib/utils.js
@@ -0,0 +1,100 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const bytes = require('bytes')
+const contentType = require('content-type')
+const typeis = require('type-is')
+
+/**
+ * Module exports.
+ */
+module.exports = {
+ getCharset,
+ normalizeOptions,
+ passthrough
+}
+
+/**
+ * Get the charset of a request.
+ *
+ * @param {Object} req
+ * @returns {string | undefined}
+ * @private
+ */
+function getCharset (req) {
+ const header = req.headers['content-type']
+ if (!header) return undefined
+ return contentType.parse(header).parameters.charset?.toLowerCase()
+}
+
+/**
+ * Get the simple type checker.
+ *
+ * @param {string | string[]} type
+ * @returns {Function}
+ * @private
+ */
+function typeChecker (type) {
+ return function checkType (req) {
+ return Boolean(typeis(req, type))
+ }
+}
+
+/**
+ * Normalizes the common options for all parsers.
+ *
+ * @param {Object} options options to normalize
+ * @param {string | string[] | Function} defaultType default content type(s) or a function to determine it
+ * @returns {Object}
+ * @private
+ */
+function normalizeOptions (options, defaultType) {
+ if (!defaultType) {
+ // Parsers must define a default content type
+ throw new TypeError('defaultType must be provided')
+ }
+
+ const inflate = options?.inflate !== false
+ const limit = typeof options?.limit === 'undefined' || options?.limit === null
+ ? 102400 // 100kb default
+ : bytes.parse(options.limit)
+ const type = options?.type || defaultType
+ const verify = options?.verify || false
+ const defaultCharset = options?.defaultCharset || 'utf-8'
+
+ if (limit === null) {
+ throw new TypeError(`option limit "${String(options.limit)}" is invalid`)
+ }
+
+ if (verify !== false && typeof verify !== 'function') {
+ throw new TypeError('option verify must be function')
+ }
+
+ // create the appropriate type checking function
+ const shouldParse = typeof type !== 'function'
+ ? typeChecker(type)
+ : type
+
+ return {
+ inflate,
+ limit,
+ verify,
+ defaultCharset,
+ shouldParse
+ }
+}
+
+/**
+ * Passthrough function that returns input unchanged.
+ * Used by parsers that don't need to transform the data.
+ *
+ * @param {*} value
+ * @returns {*}
+ * @private
+ */
+function passthrough (value) {
+ return value
+}