Skip to content

Commit 07ce14d

Browse files
authored
Added support for brotli ('br') content-encoding (#406)
1 parent 6cea6bd commit 07ce14d

File tree

7 files changed

+112
-10
lines changed

7 files changed

+112
-10
lines changed

HISTORY.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
unreleased
2+
=========================
3+
4+
* add brotli support #406
5+
16
2.0.0-beta.2 / 2023-02-23
27
=========================
38

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ The various errors returned by this module are described in the
6464

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

7070
A new `body` object containing the parsed data is populated on the `request`
7171
object after the middleware (i.e. `req.body`).
@@ -119,7 +119,8 @@ encoding of the request. The parsing can be aborted by throwing an error.
119119

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

124125
A new `body` object containing the parsed data is populated on the `request`
125126
object after the middleware (i.e. `req.body`). This will be a `Buffer` object
@@ -164,7 +165,8 @@ encoding of the request. The parsing can be aborted by throwing an error.
164165

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

169171
A new `body` string containing the parsed data is populated on the `request`
170172
object after the middleware (i.e. `req.body`). This will be a string of the
@@ -214,7 +216,7 @@ encoding of the request. The parsing can be aborted by throwing an error.
214216
Returns middleware that only parses `urlencoded` bodies and only looks at
215217
requests where the `Content-Type` header matches the `type` option. This
216218
parser accepts only UTF-8 encoding of the body and supports automatic
217-
inflation of `gzip` and `deflate` encodings.
219+
inflation of `gzip`, `br` (brotli) and `deflate` encodings.
218220

219221
A new `body` object containing the parsed data is populated on the `request`
220222
object after the middleware (i.e. `req.body`). This object will contain

lib/read.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ var zlib = require('zlib')
2525

2626
module.exports = read
2727

28+
/**
29+
* @const
30+
* whether current node version has brotli support
31+
*/
32+
var hasBrotliSupport = 'createBrotliDecompress' in zlib
33+
2834
/**
2935
* Read a request into a buffer and parse.
3036
*
@@ -174,11 +180,20 @@ function contentstream (req, debug, inflate) {
174180
stream = req
175181
stream.length = length
176182
break
177-
default:
178-
throw createError(415, 'unsupported content encoding "' + encoding + '"', {
179-
encoding: encoding,
180-
type: 'encoding.unsupported'
181-
})
183+
case 'br':
184+
if (hasBrotliSupport) {
185+
stream = zlib.createBrotliDecompress()
186+
debug('brotli decompress body')
187+
req.pipe(stream)
188+
}
189+
break
190+
}
191+
192+
if (stream === undefined) {
193+
throw createError(415, 'unsupported content encoding "' + encoding + '"', {
194+
encoding: encoding,
195+
type: 'encoding.unsupported'
196+
})
182197
}
183198

184199
return stream

test/json.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
1212
? describe
1313
: describe.skip
1414

15+
var hasBrotliSupport = 'createBrotliDecompress' in require('zlib')
16+
var brotlit = hasBrotliSupport ? it : it.skip
17+
var nobrotlit = !hasBrotliSupport ? it : it.skip
18+
1519
describe('bodyParser.json()', function () {
1620
it('should parse JSON', function (done) {
1721
request(createServer())
@@ -683,6 +687,22 @@ describe('bodyParser.json()', function () {
683687
test.expect(200, '{"name":"论"}', done)
684688
})
685689

690+
brotlit('should support brotli encoding', function (done) {
691+
var test = request(this.server).post('/')
692+
test.set('Content-Encoding', 'br')
693+
test.set('Content-Type', 'application/json')
694+
test.write(Buffer.from('8b06807b226e616d65223a22e8aeba227d03', 'hex'))
695+
test.expect(200, '{"name":"论"}', done)
696+
})
697+
698+
nobrotlit('should throw 415 if there\'s no brotli support', function (done) {
699+
var test = request(this.server).post('/')
700+
test.set('Content-Encoding', 'br')
701+
test.set('Content-Type', 'application/json')
702+
test.write(Buffer.from('8b06807b226e616d65223a22e8aeba227d03', 'hex'))
703+
test.expect(415, 'unsupported content encoding "br"', done)
704+
})
705+
686706
it('should be case-insensitive', function (done) {
687707
var test = request(this.server).post('/')
688708
test.set('Content-Encoding', 'GZIP')

test/raw.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
1212
? describe
1313
: describe.skip
1414

15+
var hasBrotliSupport = 'createBrotliDecompress' in require('zlib')
16+
var brotlit = hasBrotliSupport ? it : it.skip
17+
var nobrotlit = !hasBrotliSupport ? it : it.skip
18+
1519
describe('bodyParser.raw()', function () {
1620
before(function () {
1721
this.server = createServer()
@@ -455,6 +459,22 @@ describe('bodyParser.raw()', function () {
455459
test.expect(200, 'buf:6e616d653de8aeba', done)
456460
})
457461

462+
brotlit('should support brotli encoding', function (done) {
463+
var test = request(this.server).post('/')
464+
test.set('Content-Encoding', 'br')
465+
test.set('Content-Type', 'application/octet-stream')
466+
test.write(Buffer.from('8b03806e616d653de8aeba03', 'hex'))
467+
test.expect(200, 'buf:6e616d653de8aeba', done)
468+
})
469+
470+
nobrotlit('should throw 415 if there\'s no brotli support', function (done) {
471+
var test = request(this.server).post('/')
472+
test.set('Content-Encoding', 'br')
473+
test.set('Content-Type', 'application/octet-stream')
474+
test.write(Buffer.from('8b03806e616d653de8aeba03', 'hex'))
475+
test.expect(415, 'unsupported content encoding "br"', done)
476+
})
477+
458478
it('should be case-insensitive', function (done) {
459479
var test = request(this.server).post('/')
460480
test.set('Content-Encoding', 'GZIP')

test/text.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
1212
? describe
1313
: describe.skip
1414

15+
var hasBrotliSupport = 'createBrotliDecompress' in require('zlib')
16+
var brotlit = hasBrotliSupport ? it : it.skip
17+
var nobrotlit = !hasBrotliSupport ? it : it.skip
18+
1519
describe('bodyParser.text()', function () {
1620
before(function () {
1721
this.server = createServer()
@@ -525,6 +529,22 @@ describe('bodyParser.text()', function () {
525529
test.expect(200, '"name is 论"', done)
526530
})
527531

532+
brotlit('should support brotli encoding', function (done) {
533+
var test = request(this.server).post('/')
534+
test.set('Content-Encoding', 'br')
535+
test.set('Content-Type', 'text/plain')
536+
test.write(Buffer.from('0b05806e616d6520697320e8aeba03', 'hex'))
537+
test.expect(200, '"name is 论"', done)
538+
})
539+
540+
nobrotlit('should throw 415 if there\'s no brotli support', function (done) {
541+
var test = request(this.server).post('/')
542+
test.set('Content-Encoding', 'br')
543+
test.set('Content-Type', 'text/plain')
544+
test.write(Buffer.from('0b05806e616d6520697320e8aeba03', 'hex'))
545+
test.expect(415, 'unsupported content encoding "br"', done)
546+
})
547+
528548
it('should be case-insensitive', function (done) {
529549
var test = request(this.server).post('/')
530550
test.set('Content-Encoding', 'GZIP')

test/urlencoded.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ var describeAsyncHooks = typeof asyncHooks.AsyncLocalStorage === 'function'
1212
? describe
1313
: describe.skip
1414

15+
var hasBrotliSupport = 'createBrotliDecompress' in require('zlib')
16+
var brotlit = hasBrotliSupport ? it : it.skip
17+
var nobrotlit = !hasBrotliSupport ? it : it.skip
18+
1519
describe('bodyParser.urlencoded()', function () {
1620
before(function () {
1721
this.server = createServer()
@@ -831,6 +835,22 @@ describe('bodyParser.urlencoded()', function () {
831835
test.expect(200, '{"name":"论"}', done)
832836
})
833837

838+
brotlit('should support brotli encoding', function (done) {
839+
var test = request(this.server).post('/')
840+
test.set('Content-Encoding', 'br')
841+
test.set('Content-Type', 'application/x-www-form-urlencoded')
842+
test.write(Buffer.from('8b03806e616d653de8aeba03', 'hex'))
843+
test.expect(200, '{"name":"论"}', done)
844+
})
845+
846+
nobrotlit('should throw 415 if there\'s no brotli support', function (done) {
847+
var test = request(this.server).post('/')
848+
test.set('Content-Encoding', 'br')
849+
test.set('Content-Type', 'application/x-www-form-urlencoded')
850+
test.write(Buffer.from('789ccb4bcc4db57db16e17001068042f', 'hex'))
851+
test.expect(415, 'unsupported content encoding "br"', done)
852+
})
853+
834854
it('should be case-insensitive', function (done) {
835855
var test = request(this.server).post('/')
836856
test.set('Content-Encoding', 'GZIP')

0 commit comments

Comments
 (0)