Skip to content

Commit

Permalink
Added support for brotli ('br') content-encoding (#406)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielgindi authored Aug 17, 2024
1 parent 6cea6bd commit 07ce14d
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 10 deletions.
5 changes: 5 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
unreleased
=========================

* add brotli support #406

2.0.0-beta.2 / 2023-02-23
=========================

Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ 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` and
`deflate` encodings.
Unicode encoding of the body and supports automatic inflation of `gzip`,
`br` (brotli) and `deflate` encodings.

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 @@ -119,7 +119,8 @@ encoding of the request. The parsing can be aborted by throwing an error.

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` and `deflate` encodings.
parser supports automatic inflation of `gzip`, `br` (brotli) and `deflate`
encodings.

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 @@ -164,7 +165,8 @@ encoding of the request. The parsing can be aborted by throwing an error.

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` and `deflate` encodings.
parser supports automatic inflation of `gzip`, `br` (brotli) and `deflate`
encodings.

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 @@ -214,7 +216,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 encoding of the body and supports automatic
inflation of `gzip` and `deflate` encodings.
inflation of `gzip`, `br` (brotli) and `deflate` encodings.

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
25 changes: 20 additions & 5 deletions lib/read.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ var zlib = require('zlib')

module.exports = read

/**
* @const
* whether current node version has brotli support
*/
var hasBrotliSupport = 'createBrotliDecompress' in zlib

/**
* Read a request into a buffer and parse.
*
Expand Down Expand Up @@ -174,11 +180,20 @@ function contentstream (req, debug, inflate) {
stream = req
stream.length = length
break
default:
throw createError(415, 'unsupported content encoding "' + encoding + '"', {
encoding: encoding,
type: 'encoding.unsupported'
})
case 'br':
if (hasBrotliSupport) {
stream = zlib.createBrotliDecompress()
debug('brotli decompress body')
req.pipe(stream)
}
break
}

if (stream === undefined) {
throw createError(415, 'unsupported content encoding "' + encoding + '"', {
encoding: encoding,
type: 'encoding.unsupported'
})
}

return stream
Expand Down
20 changes: 20 additions & 0 deletions test/json.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
? describe
: describe.skip

var hasBrotliSupport = 'createBrotliDecompress' in require('zlib')
var brotlit = hasBrotliSupport ? it : it.skip
var nobrotlit = !hasBrotliSupport ? it : it.skip

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

brotlit('should support brotli encoding', function (done) {
var test = request(this.server).post('/')
test.set('Content-Encoding', 'br')
test.set('Content-Type', 'application/json')
test.write(Buffer.from('8b06807b226e616d65223a22e8aeba227d03', 'hex'))
test.expect(200, '{"name":"论"}', done)
})

nobrotlit('should throw 415 if there\'s no brotli support', function (done) {
var test = request(this.server).post('/')
test.set('Content-Encoding', 'br')
test.set('Content-Type', 'application/json')
test.write(Buffer.from('8b06807b226e616d65223a22e8aeba227d03', 'hex'))
test.expect(415, 'unsupported content encoding "br"', done)
})

it('should be case-insensitive', function (done) {
var test = request(this.server).post('/')
test.set('Content-Encoding', 'GZIP')
Expand Down
20 changes: 20 additions & 0 deletions test/raw.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
? describe
: describe.skip

var hasBrotliSupport = 'createBrotliDecompress' in require('zlib')
var brotlit = hasBrotliSupport ? it : it.skip
var nobrotlit = !hasBrotliSupport ? it : it.skip

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

brotlit('should support brotli encoding', function (done) {
var test = request(this.server).post('/')
test.set('Content-Encoding', 'br')
test.set('Content-Type', 'application/octet-stream')
test.write(Buffer.from('8b03806e616d653de8aeba03', 'hex'))
test.expect(200, 'buf:6e616d653de8aeba', done)
})

nobrotlit('should throw 415 if there\'s no brotli support', function (done) {
var test = request(this.server).post('/')
test.set('Content-Encoding', 'br')
test.set('Content-Type', 'application/octet-stream')
test.write(Buffer.from('8b03806e616d653de8aeba03', 'hex'))
test.expect(415, 'unsupported content encoding "br"', done)
})

it('should be case-insensitive', function (done) {
var test = request(this.server).post('/')
test.set('Content-Encoding', 'GZIP')
Expand Down
20 changes: 20 additions & 0 deletions test/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
? describe
: describe.skip

var hasBrotliSupport = 'createBrotliDecompress' in require('zlib')
var brotlit = hasBrotliSupport ? it : it.skip
var nobrotlit = !hasBrotliSupport ? it : it.skip

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

brotlit('should support brotli encoding', function (done) {
var test = request(this.server).post('/')
test.set('Content-Encoding', 'br')
test.set('Content-Type', 'text/plain')
test.write(Buffer.from('0b05806e616d6520697320e8aeba03', 'hex'))
test.expect(200, '"name is 论"', done)
})

nobrotlit('should throw 415 if there\'s no brotli support', function (done) {
var test = request(this.server).post('/')
test.set('Content-Encoding', 'br')
test.set('Content-Type', 'text/plain')
test.write(Buffer.from('0b05806e616d6520697320e8aeba03', 'hex'))
test.expect(415, 'unsupported content encoding "br"', done)
})

it('should be case-insensitive', function (done) {
var test = request(this.server).post('/')
test.set('Content-Encoding', 'GZIP')
Expand Down
20 changes: 20 additions & 0 deletions test/urlencoded.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
? describe
: describe.skip

var hasBrotliSupport = 'createBrotliDecompress' in require('zlib')
var brotlit = hasBrotliSupport ? it : it.skip
var nobrotlit = !hasBrotliSupport ? it : it.skip

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

brotlit('should support brotli encoding', function (done) {
var test = request(this.server).post('/')
test.set('Content-Encoding', 'br')
test.set('Content-Type', 'application/x-www-form-urlencoded')
test.write(Buffer.from('8b03806e616d653de8aeba03', 'hex'))
test.expect(200, '{"name":"论"}', done)
})

nobrotlit('should throw 415 if there\'s no brotli support', function (done) {
var test = request(this.server).post('/')
test.set('Content-Encoding', 'br')
test.set('Content-Type', 'application/x-www-form-urlencoded')
test.write(Buffer.from('789ccb4bcc4db57db16e17001068042f', 'hex'))
test.expect(415, 'unsupported content encoding "br"', done)
})

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

0 comments on commit 07ce14d

Please sign in to comment.