Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ This module provides the following parsers:
* [Text body parser](#bodyparsertextoptions)
* [URL-encoded form body parser](#bodyparserurlencodedoptions)

All parsers support automatic inflation of `gzip`, `br` (brotli), `deflate`
and `zstd` encodings. `zstd` is available in Node.js `^22.15.0` and
`>=23.8.0`.

Other body parsers you might be interested in:

- [body](https://www.npmjs.com/package/body#readme)
Expand Down Expand Up @@ -72,8 +76,7 @@ The various errors returned by this module are described in the

Returns middleware that only parses `json` and only looks at requests where
the `Content-Type` header matches the `type` option. This parser accepts any
Unicode encoding of the body and supports automatic inflation of `gzip`,
`br` (brotli) and `deflate` encodings.
Unicode encoding of the body.

A new `body` object containing the parsed data is populated on the `request`
object after the middleware (i.e. `req.body`).
Expand Down Expand Up @@ -133,9 +136,7 @@ encoding of the request. The parsing can be aborted by throwing an error.
### bodyParser.raw([options])

Returns middleware that parses all bodies as a `Buffer` and only looks at
requests where the `Content-Type` header matches the `type` option. This
parser supports automatic inflation of `gzip`, `br` (brotli) and `deflate`
encodings.
requests where the `Content-Type` header matches the `type` option.

A new `body` object containing the parsed data is populated on the `request`
object after the middleware (i.e. `req.body`). This will be a `Buffer` object
Expand Down Expand Up @@ -181,9 +182,7 @@ encoding of the request. The parsing can be aborted by throwing an error.
### bodyParser.text([options])

Returns middleware that parses all bodies as a string and only looks at
requests where the `Content-Type` header matches the `type` option. This
parser supports automatic inflation of `gzip`, `br` (brotli) and `deflate`
encodings.
requests where the `Content-Type` header matches the `type` option.

A new `body` string containing the parsed data is populated on the `request`
object after the middleware (i.e. `req.body`). This will be a string of the
Expand Down Expand Up @@ -234,8 +233,7 @@ encoding of the request. The parsing can be aborted by throwing an error.

Returns middleware that only parses `urlencoded` bodies and only looks at
requests where the `Content-Type` header matches the `type` option. This
parser accepts only UTF-8 and ISO-8859-1 encodings of the body and supports
automatic inflation of `gzip`, `br` (brotli) and `deflate` encodings.
parser accepts only UTF-8 and ISO-8859-1 encodings of the body.

A new `body` object containing the parsed data is populated on the `request`
object after the middleware (i.e. `req.body`). This object will contain
Expand Down
20 changes: 15 additions & 5 deletions lib/read.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ const { getCharset } = require('./utils')

module.exports = read

/**
* @const
* whether current node version has zstandard support
*/
const hasZstandardSupport = 'createZstdDecompress' in zlib

/**
* Read a request into a buffer and parse.
*
Expand Down Expand Up @@ -222,12 +228,16 @@ function createDecompressionStream (encoding, debug) {
case 'br':
debug('brotli decompress body')
return zlib.createBrotliDecompress()
default:
throw createError(415, 'unsupported content encoding "' + encoding + '"', {
encoding: encoding,
type: 'encoding.unsupported'
})
case 'zstd':
if (hasZstandardSupport) {
debug('zstd decompress body')
return zlib.createZstdDecompress()
}
}
throw createError(415, 'unsupported content encoding "' + encoding + '"', {
encoding: encoding,
type: 'encoding.unsupported'
})
}

/**
Expand Down
23 changes: 23 additions & 0 deletions test/json.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
const assert = require('node:assert')
const AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage
const http = require('node:http')
const zlib = require('node:zlib')
const request = require('supertest')

const bodyParser = require('..')

const hasZstandardSupport = 'createZstdDecompress' in zlib
const zstandardit = hasZstandardSupport ? it : it.skip
const nozstandardit = !hasZstandardSupport ? it : it.skip

describe('bodyParser.json()', function () {
it('should parse JSON', function (done) {
request(createServer())
Expand Down Expand Up @@ -702,6 +707,24 @@ describe('bodyParser.json()', function () {
test.expect(200, '{"name":"论"}', done)
})

zstandardit('should support zstandard encoding', function (done) {
const server = createServer({ experimentalZstd: true, limit: '1kb' })
const test = request(server).post('/')
test.set('Content-Encoding', 'zstd')
test.set('Content-Type', 'application/json')
test.write(Buffer.from('28b52ffd200e7100007b226e616d65223a22e8aeba227d', 'hex'))
test.expect(200, '{"name":"论"}', done)
})

nozstandardit('should throw 415 if there\'s no zstandard support', function (done) {
const server = createServer({ experimentalZstd: true, limit: '1kb' })
const test = request(server).post('/')
test.set('Content-Encoding', 'zstd')
test.set('Content-Type', 'application/json')
test.write(Buffer.from('28b52ffd200e7100007b226e616d65223a22e8aeba227d', 'hex'))
test.expect(415, '[encoding.unsupported] unsupported content encoding "zstd"', done)
})

it('should be case-insensitive', function (done) {
const test = request(this.server).post('/')
test.set('Content-Encoding', 'GZIP')
Expand Down
23 changes: 23 additions & 0 deletions test/raw.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
const assert = require('node:assert')
const AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage
const http = require('node:http')
const zlib = require('node:zlib')
const request = require('supertest')

const bodyParser = require('..')

const hasZstandardSupport = 'createZstdDecompress' in zlib
const zstandardit = hasZstandardSupport ? it : it.skip
const nozstandardit = !hasZstandardSupport ? it : it.skip

describe('bodyParser.raw()', function () {
before(function () {
this.server = createServer()
Expand Down Expand Up @@ -458,6 +463,24 @@ describe('bodyParser.raw()', function () {
test.expect(200, 'buf:6e616d653de8aeba', done)
})

zstandardit('should support zstandard encoding', function (done) {
const server = createServer({ experimentalZstd: true, limit: '10kb' })
const test = request(server).post('/')
test.set('Content-Encoding', 'zstd')
test.set('Content-Type', 'application/octet-stream')
test.write(Buffer.from('28b52ffd20084100006e616d653de8aeba', 'hex'))
test.expect(200, 'buf:6e616d653de8aeba', done)
})

nozstandardit('should throw 415 if there\'s no zstandard support', function (done) {
const server = createServer({ experimentalZstd: true, limit: '10kb' })
const test = request(server).post('/')
test.set('Content-Encoding', 'zstd')
test.set('Content-Type', 'application/octet-stream')
test.write(Buffer.from('28b52ffd20084100006e616d653de8aeba', 'hex'))
test.expect(415, '[encoding.unsupported] unsupported content encoding "zstd"', done)
})

it('should be case-insensitive', function (done) {
const test = request(this.server).post('/')
test.set('Content-Encoding', 'GZIP')
Expand Down
23 changes: 23 additions & 0 deletions test/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
const assert = require('node:assert')
const AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage
const http = require('node:http')
const zlib = require('node:zlib')
const request = require('supertest')

const bodyParser = require('..')

const hasZstandardSupport = 'createZstdDecompress' in zlib
const zstandardit = hasZstandardSupport ? it : it.skip
const nozstandardit = !hasZstandardSupport ? it : it.skip

describe('bodyParser.text()', function () {
before(function () {
this.server = createServer()
Expand Down Expand Up @@ -528,6 +533,24 @@ describe('bodyParser.text()', function () {
test.expect(200, '"name is 论"', done)
})

zstandardit('should support zstandard encoding', function (done) {
const server = createServer({ experimentalZstd: true, limit: '10kb' })
const test = request(server).post('/')
test.set('Content-Encoding', 'zstd')
test.set('Content-Type', 'text/plain')
test.write(Buffer.from('28b52ffd200b5900006e616d6520697320e8aeba', 'hex'))
test.expect(200, '"name is 论"', done)
})

nozstandardit('should throw 415 if there\'s no zstandard support', function (done) {
const server = createServer({ experimentalZstd: true, limit: '10kb' })
const test = request(server).post('/')
test.set('Content-Encoding', 'zstd')
test.set('Content-Type', 'text/plain')
test.write(Buffer.from('28b52ffd200b5900006e616d6520697320e8aeba', 'hex'))
test.expect(415, '[encoding.unsupported] unsupported content encoding "zstd"', done)
})

it('should be case-insensitive', function (done) {
const test = request(this.server).post('/')
test.set('Content-Encoding', 'GZIP')
Expand Down
23 changes: 23 additions & 0 deletions test/urlencoded.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
const assert = require('node:assert')
const AsyncLocalStorage = require('node:async_hooks').AsyncLocalStorage
const http = require('node:http')
const zlib = require('node:zlib')
const request = require('supertest')

const bodyParser = require('..')

const hasZstandardSupport = 'createZstdDecompress' in zlib
const zstandardit = hasZstandardSupport ? it : it.skip
const nozstandardit = !hasZstandardSupport ? it : it.skip

describe('bodyParser.urlencoded()', function () {
before(function () {
this.server = createServer()
Expand Down Expand Up @@ -910,6 +915,24 @@ describe('bodyParser.urlencoded()', function () {
test.expect(200, '{"name":"论"}', done)
})

zstandardit('should support zstandard encoding', function (done) {
const server = createServer({ experimentalZstd: true })
const test = request(server).post('/')
test.set('Content-Encoding', 'zstd')
test.set('Content-Type', 'application/x-www-form-urlencoded')
test.write(Buffer.from('28b52ffd20084100006e616d653de8aeba', 'hex'))
test.expect(200, '{"name":"论"}', done)
})

nozstandardit('should throw 415 if there\'s no zstandard support', function (done) {
const server = createServer({ experimentalZstd: true })
const test = request(server).post('/')
test.set('Content-Encoding', 'zstd')
test.set('Content-Type', 'application/x-www-form-urlencoded')
test.write(Buffer.from('28b52ffd20084100006e616d653de8aeba', 'hex'))
test.expect(415, '[encoding.unsupported] unsupported content encoding "zstd"', done)
})

it('should be case-insensitive', function (done) {
const test = request(this.server).post('/')
test.set('Content-Encoding', 'GZIP')
Expand Down