fastify 3.29.2 → 3.29.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fastify.js +1 -1
- package/lib/contentTypeParser.js +100 -22
- package/package.json +2 -1
- package/test/content-parser.test.js +282 -2
- package/test/custom-parser.test.js +3 -3
package/fastify.js
CHANGED
package/lib/contentTypeParser.js
CHANGED
|
@@ -6,6 +6,7 @@ let lru = require('tiny-lru')
|
|
|
6
6
|
// See https://github.com/fastify/fastify/issues/2356
|
|
7
7
|
// and https://github.com/fastify/fastify/discussions/2907.
|
|
8
8
|
lru = typeof lru === 'function' ? lru : lru.default
|
|
9
|
+
const { parse: parseContentType } = require('content-type')
|
|
9
10
|
|
|
10
11
|
const secureJson = require('secure-json-parse')
|
|
11
12
|
const {
|
|
@@ -32,10 +33,11 @@ const warning = require('./warnings')
|
|
|
32
33
|
|
|
33
34
|
function ContentTypeParser (bodyLimit, onProtoPoisoning, onConstructorPoisoning) {
|
|
34
35
|
this[kDefaultJsonParse] = getDefaultJsonParser(onProtoPoisoning, onConstructorPoisoning)
|
|
35
|
-
|
|
36
|
-
this.customParsers
|
|
37
|
-
this.customParsers
|
|
38
|
-
this.
|
|
36
|
+
// using a map instead of a plain object to avoid prototype hijack attacks
|
|
37
|
+
this.customParsers = new Map()
|
|
38
|
+
this.customParsers.set('application/json', new Parser(true, false, bodyLimit, this[kDefaultJsonParse]))
|
|
39
|
+
this.customParsers.set('text/plain', new Parser(true, false, bodyLimit, defaultPlainTextParser))
|
|
40
|
+
this.parserList = [new ParserListItem('application/json'), new ParserListItem('text/plain')]
|
|
39
41
|
this.parserRegExpList = []
|
|
40
42
|
this.cache = lru(100)
|
|
41
43
|
}
|
|
@@ -65,38 +67,56 @@ ContentTypeParser.prototype.add = function (contentType, opts, parserFn) {
|
|
|
65
67
|
)
|
|
66
68
|
|
|
67
69
|
if (contentTypeIsString && contentType === '*') {
|
|
68
|
-
this.customParsers
|
|
70
|
+
this.customParsers.set('', parser)
|
|
69
71
|
} else {
|
|
70
72
|
if (contentTypeIsString) {
|
|
71
|
-
this.parserList.unshift(contentType)
|
|
73
|
+
this.parserList.unshift(new ParserListItem(contentType))
|
|
72
74
|
} else {
|
|
73
75
|
this.parserRegExpList.unshift(contentType)
|
|
74
76
|
}
|
|
75
|
-
this.customParsers
|
|
77
|
+
this.customParsers.set(contentType.toString(), parser)
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
ContentTypeParser.prototype.hasParser = function (contentType) {
|
|
80
|
-
return contentType
|
|
82
|
+
return this.customParsers.has(typeof contentType === 'string' ? contentType : contentType.toString())
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
ContentTypeParser.prototype.existingParser = function (contentType) {
|
|
84
|
-
if (contentType === 'application/json') {
|
|
85
|
-
return this.customParsers
|
|
86
|
+
if (contentType === 'application/json' && this.customParsers.has(contentType)) {
|
|
87
|
+
return this.customParsers.get(contentType).fn !== this[kDefaultJsonParse]
|
|
86
88
|
}
|
|
87
|
-
if (contentType === 'text/plain') {
|
|
88
|
-
return this.customParsers
|
|
89
|
+
if (contentType === 'text/plain' && this.customParsers.has(contentType)) {
|
|
90
|
+
return this.customParsers.get(contentType).fn !== defaultPlainTextParser
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
return
|
|
93
|
+
return this.hasParser(contentType)
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
ContentTypeParser.prototype.getParser = function (contentType) {
|
|
97
|
+
if (this.hasParser(contentType)) {
|
|
98
|
+
return this.customParsers.get(contentType)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const parser = this.cache.get(contentType)
|
|
102
|
+
// TODO not covered by tests, this is a security backport
|
|
103
|
+
/* istanbul ignore next */
|
|
104
|
+
if (parser !== undefined) return parser
|
|
105
|
+
|
|
106
|
+
const parsed = safeParseContentType(contentType)
|
|
107
|
+
|
|
108
|
+
// dummyContentType always the same object
|
|
109
|
+
// we can use === for the comparsion and return early
|
|
110
|
+
if (parsed === dummyContentType) {
|
|
111
|
+
return this.customParsers.get('')
|
|
112
|
+
}
|
|
113
|
+
|
|
95
114
|
// eslint-disable-next-line no-var
|
|
96
115
|
for (var i = 0; i !== this.parserList.length; ++i) {
|
|
97
|
-
const
|
|
98
|
-
if (
|
|
99
|
-
const parser = this.customParsers
|
|
116
|
+
const parserListItem = this.parserList[i]
|
|
117
|
+
if (compareContentType(parsed, parserListItem)) {
|
|
118
|
+
const parser = this.customParsers.get(parserListItem.name)
|
|
119
|
+
// we set request content-type in cache to reduce parsing of MIME type
|
|
100
120
|
this.cache.set(contentType, parser)
|
|
101
121
|
return parser
|
|
102
122
|
}
|
|
@@ -105,18 +125,19 @@ ContentTypeParser.prototype.getParser = function (contentType) {
|
|
|
105
125
|
// eslint-disable-next-line no-var
|
|
106
126
|
for (var j = 0; j !== this.parserRegExpList.length; ++j) {
|
|
107
127
|
const parserRegExp = this.parserRegExpList[j]
|
|
108
|
-
if (
|
|
109
|
-
const parser = this.customParsers
|
|
128
|
+
if (compareRegExpContentType(contentType, parsed.type, parserRegExp)) {
|
|
129
|
+
const parser = this.customParsers.get(parserRegExp.toString())
|
|
130
|
+
// we set request content-type in cache to reduce parsing of MIME type
|
|
110
131
|
this.cache.set(contentType, parser)
|
|
111
132
|
return parser
|
|
112
133
|
}
|
|
113
134
|
}
|
|
114
135
|
|
|
115
|
-
return this.customParsers
|
|
136
|
+
return this.customParsers.get('')
|
|
116
137
|
}
|
|
117
138
|
|
|
118
139
|
ContentTypeParser.prototype.removeAll = function () {
|
|
119
|
-
this.customParsers =
|
|
140
|
+
this.customParsers = new Map()
|
|
120
141
|
this.parserRegExpList = []
|
|
121
142
|
this.parserList = []
|
|
122
143
|
this.cache = lru(100)
|
|
@@ -125,7 +146,7 @@ ContentTypeParser.prototype.removeAll = function () {
|
|
|
125
146
|
ContentTypeParser.prototype.remove = function (contentType) {
|
|
126
147
|
if (!(typeof contentType === 'string' || contentType instanceof RegExp)) throw new FST_ERR_CTP_INVALID_TYPE()
|
|
127
148
|
|
|
128
|
-
|
|
149
|
+
this.customParsers.delete(contentType.toString())
|
|
129
150
|
|
|
130
151
|
const parsers = typeof contentType === 'string' ? this.parserList : this.parserRegExpList
|
|
131
152
|
|
|
@@ -290,7 +311,7 @@ function Parser (asString, asBuffer, bodyLimit, fn) {
|
|
|
290
311
|
function buildContentTypeParser (c) {
|
|
291
312
|
const contentTypeParser = new ContentTypeParser()
|
|
292
313
|
contentTypeParser[kDefaultJsonParse] = c[kDefaultJsonParse]
|
|
293
|
-
|
|
314
|
+
contentTypeParser.customParsers = new Map(c.customParsers.entries())
|
|
294
315
|
contentTypeParser.parserList = c.parserList.slice()
|
|
295
316
|
return contentTypeParser
|
|
296
317
|
}
|
|
@@ -343,6 +364,63 @@ function removeAllContentTypeParsers () {
|
|
|
343
364
|
this[kContentTypeParser].removeAll()
|
|
344
365
|
}
|
|
345
366
|
|
|
367
|
+
// dummy here to prevent repeated object creation
|
|
368
|
+
const dummyContentType = { type: '', parameters: Object.create(null) }
|
|
369
|
+
|
|
370
|
+
function safeParseContentType (contentType) {
|
|
371
|
+
try {
|
|
372
|
+
return parseContentType(contentType)
|
|
373
|
+
} catch (err) {
|
|
374
|
+
return dummyContentType
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function compareContentType (contentType, parserListItem) {
|
|
379
|
+
if (parserListItem.isEssence) {
|
|
380
|
+
// we do essence check
|
|
381
|
+
return contentType.type.indexOf(parserListItem) !== -1
|
|
382
|
+
} else {
|
|
383
|
+
// when the content-type includes parameters
|
|
384
|
+
// we do a full-text search
|
|
385
|
+
// reject essence content-type before checking parameters
|
|
386
|
+
if (contentType.type.indexOf(parserListItem.type) === -1) return false
|
|
387
|
+
for (const key of parserListItem.parameterKeys) {
|
|
388
|
+
// reject when missing parameters
|
|
389
|
+
if (!(key in contentType.parameters)) return false
|
|
390
|
+
// reject when parameters do not match
|
|
391
|
+
if (contentType.parameters[key] !== parserListItem.parameters[key]) return false
|
|
392
|
+
}
|
|
393
|
+
return true
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function compareRegExpContentType (contentType, essenceMIMEType, regexp) {
|
|
398
|
+
if (regexp.source.indexOf(';') === -1) {
|
|
399
|
+
// we do essence check
|
|
400
|
+
return regexp.test(essenceMIMEType)
|
|
401
|
+
} else {
|
|
402
|
+
// when the content-type includes parameters
|
|
403
|
+
// we do a full-text match
|
|
404
|
+
return regexp.test(contentType)
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function ParserListItem (contentType) {
|
|
409
|
+
this.name = contentType
|
|
410
|
+
// we pre-calculate all the needed information
|
|
411
|
+
// before content-type comparsion
|
|
412
|
+
const parsed = safeParseContentType(contentType)
|
|
413
|
+
this.type = parsed.type
|
|
414
|
+
this.parameters = parsed.parameters
|
|
415
|
+
this.parameterKeys = Object.keys(parsed.parameters)
|
|
416
|
+
this.isEssence = contentType.indexOf(';') === -1
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// used in ContentTypeParser.remove
|
|
420
|
+
ParserListItem.prototype.toString = function () {
|
|
421
|
+
return this.name
|
|
422
|
+
}
|
|
423
|
+
|
|
346
424
|
module.exports = ContentTypeParser
|
|
347
425
|
module.exports.helpers = {
|
|
348
426
|
buildContentTypeParser,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "3.29.
|
|
3
|
+
"version": "3.29.4",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -187,6 +187,7 @@
|
|
|
187
187
|
"light-my-request": "^4.2.0",
|
|
188
188
|
"pino": "^6.13.0",
|
|
189
189
|
"process-warning": "^1.0.0",
|
|
190
|
+
"content-type": "^1.0.4",
|
|
190
191
|
"proxy-addr": "^2.0.7",
|
|
191
192
|
"rfdc": "^1.1.4",
|
|
192
193
|
"secure-json-parse": "^2.0.0",
|
|
@@ -181,7 +181,7 @@ test('add', t => {
|
|
|
181
181
|
const contentTypeParser = fastify[keys.kContentTypeParser]
|
|
182
182
|
|
|
183
183
|
contentTypeParser.add('*', {}, first)
|
|
184
|
-
t.equal(contentTypeParser.customParsers
|
|
184
|
+
t.equal(contentTypeParser.customParsers.get('').fn, first)
|
|
185
185
|
})
|
|
186
186
|
|
|
187
187
|
t.end()
|
|
@@ -239,7 +239,7 @@ test('remove', t => {
|
|
|
239
239
|
|
|
240
240
|
contentTypeParser.remove('image/png')
|
|
241
241
|
|
|
242
|
-
t.same(
|
|
242
|
+
t.same(contentTypeParser.customParsers.size, 2)
|
|
243
243
|
})
|
|
244
244
|
|
|
245
245
|
t.end()
|
|
@@ -262,3 +262,283 @@ test('remove all should remove all existing parsers and reset cache', t => {
|
|
|
262
262
|
t.same(contentTypeParser.parserRegExpList.length, 0)
|
|
263
263
|
t.same(Object.keys(contentTypeParser.customParsers).length, 0)
|
|
264
264
|
})
|
|
265
|
+
|
|
266
|
+
test('Safeguard against malicious content-type / 1', async t => {
|
|
267
|
+
const badNames = Object.getOwnPropertyNames({}.__proto__) // eslint-disable-line
|
|
268
|
+
t.plan(badNames.length)
|
|
269
|
+
|
|
270
|
+
const fastify = Fastify()
|
|
271
|
+
|
|
272
|
+
fastify.post('/', async () => {
|
|
273
|
+
return 'ok'
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
for (const prop of badNames) {
|
|
277
|
+
const response = await fastify.inject({
|
|
278
|
+
method: 'POST',
|
|
279
|
+
path: '/',
|
|
280
|
+
headers: {
|
|
281
|
+
'content-type': prop
|
|
282
|
+
},
|
|
283
|
+
body: ''
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
t.same(response.statusCode, 415)
|
|
287
|
+
}
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
test('Safeguard against malicious content-type / 2', async t => {
|
|
291
|
+
t.plan(1)
|
|
292
|
+
|
|
293
|
+
const fastify = Fastify()
|
|
294
|
+
|
|
295
|
+
fastify.post('/', async () => {
|
|
296
|
+
return 'ok'
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
const response = await fastify.inject({
|
|
300
|
+
method: 'POST',
|
|
301
|
+
path: '/',
|
|
302
|
+
headers: {
|
|
303
|
+
'content-type': '\\u0063\\u006fnstructor'
|
|
304
|
+
},
|
|
305
|
+
body: ''
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
t.same(response.statusCode, 415)
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
test('Safeguard against malicious content-type / 3', async t => {
|
|
312
|
+
t.plan(1)
|
|
313
|
+
|
|
314
|
+
const fastify = Fastify()
|
|
315
|
+
|
|
316
|
+
fastify.post('/', async () => {
|
|
317
|
+
return 'ok'
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
const response = await fastify.inject({
|
|
321
|
+
method: 'POST',
|
|
322
|
+
path: '/',
|
|
323
|
+
headers: {
|
|
324
|
+
'content-type': 'constructor; charset=utf-8'
|
|
325
|
+
},
|
|
326
|
+
body: ''
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
t.same(response.statusCode, 415)
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
test('Safeguard against content-type spoofing - string', async t => {
|
|
333
|
+
t.plan(1)
|
|
334
|
+
|
|
335
|
+
const fastify = Fastify()
|
|
336
|
+
fastify.removeAllContentTypeParsers()
|
|
337
|
+
fastify.addContentTypeParser('text/plain', function (request, body, done) {
|
|
338
|
+
t.pass('should be called')
|
|
339
|
+
done(null, body)
|
|
340
|
+
})
|
|
341
|
+
fastify.addContentTypeParser('application/json', function (request, body, done) {
|
|
342
|
+
t.fail('shouldn\'t be called')
|
|
343
|
+
done(null, body)
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
fastify.post('/', async () => {
|
|
347
|
+
return 'ok'
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
await fastify.inject({
|
|
351
|
+
method: 'POST',
|
|
352
|
+
path: '/',
|
|
353
|
+
headers: {
|
|
354
|
+
'content-type': 'text/plain; content-type="application/json"'
|
|
355
|
+
},
|
|
356
|
+
body: ''
|
|
357
|
+
})
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
test('Safeguard against content-type spoofing - regexp', async t => {
|
|
361
|
+
t.plan(1)
|
|
362
|
+
|
|
363
|
+
const fastify = Fastify()
|
|
364
|
+
fastify.removeAllContentTypeParsers()
|
|
365
|
+
fastify.addContentTypeParser(/text\/plain/, function (request, body, done) {
|
|
366
|
+
t.pass('should be called')
|
|
367
|
+
done(null, body)
|
|
368
|
+
})
|
|
369
|
+
fastify.addContentTypeParser(/application\/json/, function (request, body, done) {
|
|
370
|
+
t.fail('shouldn\'t be called')
|
|
371
|
+
done(null, body)
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
fastify.post('/', async () => {
|
|
375
|
+
return 'ok'
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
await fastify.inject({
|
|
379
|
+
method: 'POST',
|
|
380
|
+
path: '/',
|
|
381
|
+
headers: {
|
|
382
|
+
'content-type': 'text/plain; content-type="application/json"'
|
|
383
|
+
},
|
|
384
|
+
body: ''
|
|
385
|
+
})
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
test('content-type match parameters - string 1', async t => {
|
|
389
|
+
t.plan(1)
|
|
390
|
+
|
|
391
|
+
const fastify = Fastify()
|
|
392
|
+
fastify.removeAllContentTypeParsers()
|
|
393
|
+
fastify.addContentTypeParser('text/plain; charset=utf8', function (request, body, done) {
|
|
394
|
+
t.fail('shouldn\'t be called')
|
|
395
|
+
done(null, body)
|
|
396
|
+
})
|
|
397
|
+
fastify.addContentTypeParser('application/json; charset=utf8', function (request, body, done) {
|
|
398
|
+
t.pass('should be called')
|
|
399
|
+
done(null, body)
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
fastify.post('/', async () => {
|
|
403
|
+
return 'ok'
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
await fastify.inject({
|
|
407
|
+
method: 'POST',
|
|
408
|
+
path: '/',
|
|
409
|
+
headers: {
|
|
410
|
+
'content-type': 'application/json; charset=utf8'
|
|
411
|
+
},
|
|
412
|
+
body: ''
|
|
413
|
+
})
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
test('content-type match parameters - string 2', async t => {
|
|
417
|
+
t.plan(1)
|
|
418
|
+
|
|
419
|
+
const fastify = Fastify()
|
|
420
|
+
fastify.removeAllContentTypeParsers()
|
|
421
|
+
fastify.addContentTypeParser('application/json; charset=utf8; foo=bar', function (request, body, done) {
|
|
422
|
+
t.pass('should be called')
|
|
423
|
+
done(null, body)
|
|
424
|
+
})
|
|
425
|
+
fastify.addContentTypeParser('text/plain; charset=utf8; foo=bar', function (request, body, done) {
|
|
426
|
+
t.fail('shouldn\'t be called')
|
|
427
|
+
done(null, body)
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
fastify.post('/', async () => {
|
|
431
|
+
return 'ok'
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
await fastify.inject({
|
|
435
|
+
method: 'POST',
|
|
436
|
+
path: '/',
|
|
437
|
+
headers: {
|
|
438
|
+
'content-type': 'application/json; foo=bar; charset=utf8'
|
|
439
|
+
},
|
|
440
|
+
body: ''
|
|
441
|
+
})
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
test('content-type match parameters - regexp', async t => {
|
|
445
|
+
t.plan(1)
|
|
446
|
+
|
|
447
|
+
const fastify = Fastify()
|
|
448
|
+
fastify.removeAllContentTypeParsers()
|
|
449
|
+
fastify.addContentTypeParser(/application\/json; charset=utf8/, function (request, body, done) {
|
|
450
|
+
t.pass('should be called')
|
|
451
|
+
done(null, body)
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
fastify.post('/', async () => {
|
|
455
|
+
return 'ok'
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
await fastify.inject({
|
|
459
|
+
method: 'POST',
|
|
460
|
+
path: '/',
|
|
461
|
+
headers: {
|
|
462
|
+
'content-type': 'application/json; charset=utf8'
|
|
463
|
+
},
|
|
464
|
+
body: ''
|
|
465
|
+
})
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
test('content-type fail when parameters not match - string 1', async t => {
|
|
469
|
+
t.plan(1)
|
|
470
|
+
|
|
471
|
+
const fastify = Fastify()
|
|
472
|
+
fastify.removeAllContentTypeParsers()
|
|
473
|
+
fastify.addContentTypeParser('application/json; charset=utf8; foo=bar', function (request, body, done) {
|
|
474
|
+
t.fail('shouldn\'t be called')
|
|
475
|
+
done(null, body)
|
|
476
|
+
})
|
|
477
|
+
|
|
478
|
+
fastify.post('/', async () => {
|
|
479
|
+
return 'ok'
|
|
480
|
+
})
|
|
481
|
+
|
|
482
|
+
const response = await fastify.inject({
|
|
483
|
+
method: 'POST',
|
|
484
|
+
path: '/',
|
|
485
|
+
headers: {
|
|
486
|
+
'content-type': 'application/json; charset=utf8'
|
|
487
|
+
},
|
|
488
|
+
body: ''
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
t.same(response.statusCode, 415)
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
test('content-type fail when parameters not match - string 2', async t => {
|
|
495
|
+
t.plan(1)
|
|
496
|
+
|
|
497
|
+
const fastify = Fastify()
|
|
498
|
+
fastify.removeAllContentTypeParsers()
|
|
499
|
+
fastify.addContentTypeParser('application/json; charset=utf8; foo=bar', function (request, body, done) {
|
|
500
|
+
t.fail('shouldn\'t be called')
|
|
501
|
+
done(null, body)
|
|
502
|
+
})
|
|
503
|
+
|
|
504
|
+
fastify.post('/', async () => {
|
|
505
|
+
return 'ok'
|
|
506
|
+
})
|
|
507
|
+
|
|
508
|
+
const response = await fastify.inject({
|
|
509
|
+
method: 'POST',
|
|
510
|
+
path: '/',
|
|
511
|
+
headers: {
|
|
512
|
+
'content-type': 'application/json; charset=utf8; foo=baz'
|
|
513
|
+
},
|
|
514
|
+
body: ''
|
|
515
|
+
})
|
|
516
|
+
|
|
517
|
+
t.same(response.statusCode, 415)
|
|
518
|
+
})
|
|
519
|
+
|
|
520
|
+
test('content-type fail when parameters not match - regexp', async t => {
|
|
521
|
+
t.plan(1)
|
|
522
|
+
|
|
523
|
+
const fastify = Fastify()
|
|
524
|
+
fastify.removeAllContentTypeParsers()
|
|
525
|
+
fastify.addContentTypeParser(/application\/json; charset=utf8; foo=bar/, function (request, body, done) {
|
|
526
|
+
t.fail('shouldn\'t be called')
|
|
527
|
+
done(null, body)
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
fastify.post('/', async () => {
|
|
531
|
+
return 'ok'
|
|
532
|
+
})
|
|
533
|
+
|
|
534
|
+
const response = await fastify.inject({
|
|
535
|
+
method: 'POST',
|
|
536
|
+
path: '/',
|
|
537
|
+
headers: {
|
|
538
|
+
'content-type': 'application/json; charset=utf8'
|
|
539
|
+
},
|
|
540
|
+
body: ''
|
|
541
|
+
})
|
|
542
|
+
|
|
543
|
+
t.same(response.statusCode, 415)
|
|
544
|
+
})
|
|
@@ -1120,7 +1120,7 @@ test('The charset should not interfere with the content type handling', t => {
|
|
|
1120
1120
|
url: 'http://localhost:' + fastify.server.address().port,
|
|
1121
1121
|
body: '{"hello":"world"}',
|
|
1122
1122
|
headers: {
|
|
1123
|
-
'Content-Type': 'application/json charset=utf-8'
|
|
1123
|
+
'Content-Type': 'application/json; charset=utf-8'
|
|
1124
1124
|
}
|
|
1125
1125
|
}, (err, response, body) => {
|
|
1126
1126
|
t.error(err)
|
|
@@ -1303,7 +1303,7 @@ test('contentTypeParser should add a custom parser with RegExp value', t => {
|
|
|
1303
1303
|
url: 'http://localhost:' + fastify.server.address().port,
|
|
1304
1304
|
body: '{"hello":"world"}',
|
|
1305
1305
|
headers: {
|
|
1306
|
-
'Content-Type': 'weird
|
|
1306
|
+
'Content-Type': 'weird/content-type+json'
|
|
1307
1307
|
}
|
|
1308
1308
|
}, (err, response, body) => {
|
|
1309
1309
|
t.error(err)
|
|
@@ -1333,7 +1333,7 @@ test('contentTypeParser should add multiple custom parsers with RegExp values',
|
|
|
1333
1333
|
done(null, 'xml')
|
|
1334
1334
|
})
|
|
1335
1335
|
|
|
1336
|
-
fastify.addContentTypeParser(/.*\+myExtension
|
|
1336
|
+
fastify.addContentTypeParser(/.*\+myExtension$/i, function (req, payload, done) {
|
|
1337
1337
|
let data = ''
|
|
1338
1338
|
payload.on('data', chunk => { data += chunk })
|
|
1339
1339
|
payload.on('end', () => {
|