Skip to content

Commit

Permalink
generic body parser
Browse files Browse the repository at this point in the history
Co-authored-by: S Dellysse <sdellysse@gmail.com>
Co-authored-by: ctcpip <ctcpip@users.noreply.github.com>

squashed the following commits:

Added support for external parsers to bodyParser.json()

removed test dependency on json-bigint

reworked doc to describe json parser() func better

added parser() option and doc for .text()

added parser() option and doc for .raw()

added parser() option and doc for .urlencoded()

cleanup to satisfy linter

added generic parser

converted json parser to use generic parser

converted raw parser to use generic parser

converted text parser to use generic parser

converted urlencoded parser to use generic parser

cleanup / fix linter warnings

removed items from README

added bodyParser.generic() getter

cleanup / fix linter warnings

fixed tests after rebase

satisfying linter

Ref'd genParser via the bodyparser getter to signal how third party parsers should import genParser'

removed dep on object-assign, which didnt support node < 0.10

minor text cleanup

🔧 add debug script

🐛 fix object merging

🔥 clean up

💚 remove node < 4 from CI
  • Loading branch information
ctcpip committed Jan 15, 2025
1 parent ee8bd68 commit 8715c66
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 391 deletions.
28 changes: 24 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,27 @@ specifies the number of bytes; if it is a string, the value is passed to the
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
to `'100kb'`.

##### parser

The `parser` option is the function called against the request body to convert
it to a Javascript object. If a `reviver` is supplied, it is supplied as the
second argument to this function.

```
parser(body, reviver) -> req.body
```

Defaults to `JSON.parse`.

##### reviver

The `reviver` option is passed directly to `JSON.parse` as the second
argument. You can find more information on this argument
You can find more information on this argument
[in the MDN documentation about JSON.parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Example.3A_Using_the_reviver_parameter).

##### strict

When set to `true`, will only accept arrays and objects; when `false` will
accept anything `JSON.parse` accepts. Defaults to `true`.
accept anything the `parser` accepts. Defaults to `true`.

##### type

Expand Down Expand Up @@ -290,11 +301,20 @@ of `✓`. Defaults to `false`.
Whether to decode numeric entities such as `&#9786;` when parsing an iso-8859-1
form. Defaults to `false`.


#### depth

The `depth` option is used to configure the maximum depth of the `qs` library when `extended` is `true`. This allows you to limit the amount of keys that are parsed and can be useful to prevent certain types of abuse. Defaults to `32`. It is recommended to keep this value as low as possible.

##### parser

The `parser` option, if supplied, is used to in place of the default parser to
convert the request body into a Javascript object. If this option is supplied,
both the `extended` and `parameterLimit` options are ignored.

```
parser(body) -> req.body
```

## Errors

The middlewares provided by this module create errors using the
Expand Down
160 changes: 160 additions & 0 deletions lib/generic-parser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*!
* body-parser
* Copyright(c) 2014 Jonathan Ong
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/

'use strict'

/**
* Module dependencies.
* @private
*/

var bytes = require('bytes')
var contentType = require('content-type')
var createError = require('http-errors')
var debug = require('debug')('body-parser:generic')
var isFinished = require('on-finished').isFinished
var read = require('./read')
var typeis = require('type-is')

/**
* Module exports.
*/

module.exports = generic

/**
* Use this to create a middleware that parses request bodies
*
* @param {object} [options]
* @return {function}
* @public
*/

function generic (parserOptions, parserOverrides) {
// Squash the options and the overrides down into one object
var opts = Object.create(parserOptions)
Object.assign(opts, parserOverrides)

var limit = typeof opts.limit !== 'number'
? bytes.parse(opts.limit || '100kb')
: opts.limit
var charset = opts.charset
var inflate = opts.inflate !== false
var verify = opts.verify || false
var parse = opts.parse || defaultParse
var defaultReqCharset = opts.defaultCharset || 'utf-8'
var type = opts.type

if (verify !== false && typeof verify !== 'function') {
throw new TypeError('option verify must be function')
}

// create the appropriate type checking function
var shouldParse = typeof type !== 'function'
? typeChecker(type)
: type

// create the appropriate charset validating function
var validCharset = typeof charset !== 'function'
? charsetValidator(charset)
: charset

return function genericParser (req, res, next) {
if (isFinished(req)) {
debug('body already parsed')
next()
return
}

if (!('body' in req)) {
req.body = undefined
}

// skip requests without bodies
if (!typeis.hasBody(req)) {
debug('skip empty body')
next()
return
}

debug('content-type %j', req.headers['content-type'])

// determine if request should be parsed
if (!shouldParse(req)) {
debug('skip parsing')
next()
return
}

// assert charset per RFC 7159 sec 8.1
var reqCharset = null
if (charset !== undefined) {
reqCharset = getCharset(req) || defaultReqCharset
if (!validCharset(reqCharset)) {
debug('invalid charset')
next(createError(415, 'unsupported charset "' + reqCharset.toUpperCase() + '"', {
charset: reqCharset,
type: 'charset.unsupported'
}))
return
}
}

// read
read(req, res, next, parse, debug, {
encoding: reqCharset,
inflate: inflate,
limit: limit,
verify: verify
})
}
}

function defaultParse (buf) {
return buf
}

/**
* Get the charset of a request.
*
* @param {object} req
* @api private
*/

function getCharset (req) {
try {
return (contentType.parse(req).parameters.charset || '').toLowerCase()
} catch (e) {
return undefined
}
}

/**
* Get the simple type checker.
*
* @param {string} type
* @return {function}
*/

function typeChecker (type) {
return function checkType (req) {
return Boolean(typeis(req, type))
}
}

/**
* Get the simple charset validator.
*
* @param {string} type
* @return {function}
*/

function charsetValidator (charset) {
return function validateCharset (reqCharset) {
return charset === reqCharset
}
}
Loading

0 comments on commit 8715c66

Please sign in to comment.