strong-tie/inbound-calls
0
1'use strict'2 3const fs = require('node:fs')4const t = require('tap')5const test = t.test6const sget = require('simple-get').concat7const Fastify = require('../fastify')8const jsonParser = require('fast-json-body')9const { getServerUrl, plainTextParser } = require('./helper')10 11process.removeAllListeners('warning')12 13test('contentTypeParser method should exist', t => {14 t.plan(1)15 const fastify = Fastify()16 t.ok(fastify.addContentTypeParser)17})18 19test('contentTypeParser should add a custom parser', t => {20 t.plan(3)21 const fastify = Fastify()22 23 fastify.post('/', (req, reply) => {24 reply.send(req.body)25 })26 27 fastify.options('/', (req, reply) => {28 reply.send(req.body)29 })30 31 fastify.addContentTypeParser('application/jsoff', function (req, payload, done) {32 jsonParser(payload, function (err, body) {33 done(err, body)34 })35 })36 37 fastify.listen({ port: 0 }, err => {38 t.error(err)39 40 t.teardown(() => fastify.close())41 42 t.test('in POST', t => {43 t.plan(3)44 45 sget({46 method: 'POST',47 url: getServerUrl(fastify),48 body: '{"hello":"world"}',49 headers: {50 'Content-Type': 'application/jsoff'51 }52 }, (err, response, body) => {53 t.error(err)54 t.equal(response.statusCode, 200)55 t.same(body.toString(), JSON.stringify({ hello: 'world' }))56 })57 })58 59 t.test('in OPTIONS', t => {60 t.plan(3)61 62 sget({63 method: 'OPTIONS',64 url: getServerUrl(fastify),65 body: '{"hello":"world"}',66 headers: {67 'Content-Type': 'application/jsoff'68 }69 }, (err, response, body) => {70 t.error(err)71 t.equal(response.statusCode, 200)72 t.same(body.toString(), JSON.stringify({ hello: 'world' }))73 })74 })75 })76})77 78test('contentTypeParser should handle multiple custom parsers', t => {79 t.plan(7)80 const fastify = Fastify()81 82 fastify.post('/', (req, reply) => {83 reply.send(req.body)84 })85 86 fastify.post('/hello', (req, reply) => {87 reply.send(req.body)88 })89 90 function customParser (req, payload, done) {91 jsonParser(payload, function (err, body) {92 done(err, body)93 })94 }95 96 fastify.addContentTypeParser('application/jsoff', customParser)97 fastify.addContentTypeParser('application/ffosj', customParser)98 99 fastify.listen({ port: 0 }, err => {100 t.error(err)101 t.teardown(() => { fastify.close() })102 103 sget({104 method: 'POST',105 url: getServerUrl(fastify),106 body: '{"hello":"world"}',107 headers: {108 'Content-Type': 'application/jsoff'109 }110 }, (err, response, body) => {111 t.error(err)112 t.equal(response.statusCode, 200)113 t.same(body.toString(), JSON.stringify({ hello: 'world' }))114 })115 116 sget({117 method: 'POST',118 url: getServerUrl(fastify) + '/hello',119 body: '{"hello":"world"}',120 headers: {121 'Content-Type': 'application/ffosj'122 }123 }, (err, response, body) => {124 t.error(err)125 t.equal(response.statusCode, 200)126 t.same(body.toString(), JSON.stringify({ hello: 'world' }))127 })128 })129})130 131test('contentTypeParser should handle an array of custom contentTypes', t => {132 t.plan(7)133 const fastify = Fastify()134 135 fastify.post('/', (req, reply) => {136 reply.send(req.body)137 })138 139 fastify.post('/hello', (req, reply) => {140 reply.send(req.body)141 })142 143 function customParser (req, payload, done) {144 jsonParser(payload, function (err, body) {145 done(err, body)146 })147 }148 149 fastify.addContentTypeParser(['application/jsoff', 'application/ffosj'], customParser)150 151 fastify.listen({ port: 0 }, err => {152 t.error(err)153 t.teardown(() => { fastify.close() })154 155 sget({156 method: 'POST',157 url: getServerUrl(fastify),158 body: '{"hello":"world"}',159 headers: {160 'Content-Type': 'application/jsoff'161 }162 }, (err, response, body) => {163 t.error(err)164 t.equal(response.statusCode, 200)165 t.same(body.toString(), JSON.stringify({ hello: 'world' }))166 })167 168 sget({169 method: 'POST',170 url: getServerUrl(fastify) + '/hello',171 body: '{"hello":"world"}',172 headers: {173 'Content-Type': 'application/ffosj'174 }175 }, (err, response, body) => {176 t.error(err)177 t.equal(response.statusCode, 200)178 t.same(body.toString(), JSON.stringify({ hello: 'world' }))179 })180 })181})182 183test('contentTypeParser should handle errors', t => {184 t.plan(3)185 const fastify = Fastify()186 187 fastify.post('/', (req, reply) => {188 reply.send(req.body)189 })190 191 fastify.addContentTypeParser('application/jsoff', function (req, payload, done) {192 done(new Error('kaboom!'), {})193 })194 195 fastify.listen({ port: 0 }, err => {196 t.error(err)197 198 sget({199 method: 'POST',200 url: getServerUrl(fastify),201 body: '{"hello":"world"}',202 headers: {203 'Content-Type': 'application/jsoff'204 }205 }, (err, response, body) => {206 t.error(err)207 t.equal(response.statusCode, 500)208 fastify.close()209 })210 })211})212 213test('contentTypeParser should support encapsulation', t => {214 t.plan(6)215 const fastify = Fastify()216 217 fastify.register((instance, opts, done) => {218 instance.addContentTypeParser('application/jsoff', () => {})219 t.ok(instance.hasContentTypeParser('application/jsoff'))220 221 instance.register((instance, opts, done) => {222 instance.addContentTypeParser('application/ffosj', () => {})223 t.ok(instance.hasContentTypeParser('application/jsoff'))224 t.ok(instance.hasContentTypeParser('application/ffosj'))225 done()226 })227 228 done()229 })230 231 fastify.ready(err => {232 t.error(err)233 t.notOk(fastify.hasContentTypeParser('application/jsoff'))234 t.notOk(fastify.hasContentTypeParser('application/ffosj'))235 })236})237 238test('contentTypeParser should support encapsulation, second try', t => {239 t.plan(4)240 const fastify = Fastify()241 242 fastify.register((instance, opts, done) => {243 instance.post('/', (req, reply) => {244 reply.send(req.body)245 })246 247 instance.addContentTypeParser('application/jsoff', function (req, payload, done) {248 jsonParser(payload, function (err, body) {249 done(err, body)250 })251 })252 253 done()254 })255 256 fastify.listen({ port: 0 }, err => {257 t.error(err)258 259 sget({260 method: 'POST',261 url: getServerUrl(fastify),262 body: '{"hello":"world"}',263 headers: {264 'Content-Type': 'application/jsoff'265 }266 }, (err, response, body) => {267 t.error(err)268 t.equal(response.statusCode, 200)269 t.same(body.toString(), JSON.stringify({ hello: 'world' }))270 fastify.close()271 })272 })273})274 275test('contentTypeParser shouldn\'t support request with undefined "Content-Type"', t => {276 t.plan(3)277 const fastify = Fastify()278 279 fastify.post('/', (req, reply) => {280 reply.send(req.body)281 })282 283 fastify.addContentTypeParser('application/jsoff', function (req, payload, done) {284 jsonParser(payload, function (err, body) {285 done(err, body)286 })287 })288 289 fastify.listen({ port: 0 }, err => {290 t.error(err)291 292 sget({293 method: 'POST',294 url: getServerUrl(fastify),295 body: 'unknown content type!',296 headers: {297 // 'Content-Type': undefined298 }299 }, (err, response, body) => {300 t.error(err)301 t.equal(response.statusCode, 415)302 fastify.close()303 })304 })305})306 307test('the content type should be a string or RegExp', t => {308 t.plan(2)309 const fastify = Fastify()310 311 try {312 fastify.addContentTypeParser(null, () => {})313 t.fail()314 } catch (err) {315 t.equal(err.code, 'FST_ERR_CTP_INVALID_TYPE')316 t.equal(err.message, 'The content type should be a string or a RegExp')317 }318})319 320test('the content type cannot be an empty string', t => {321 t.plan(2)322 const fastify = Fastify()323 324 try {325 fastify.addContentTypeParser('', () => {})326 t.fail()327 } catch (err) {328 t.equal(err.code, 'FST_ERR_CTP_EMPTY_TYPE')329 t.equal(err.message, 'The content type cannot be an empty string')330 }331})332 333test('the content type handler should be a function', t => {334 t.plan(2)335 const fastify = Fastify()336 337 try {338 fastify.addContentTypeParser('aaa', null)339 t.fail()340 } catch (err) {341 t.equal(err.code, 'FST_ERR_CTP_INVALID_HANDLER')342 t.equal(err.message, 'The content type handler should be a function')343 }344})345 346test('catch all content type parser', t => {347 t.plan(7)348 const fastify = Fastify()349 350 fastify.post('/', (req, reply) => {351 reply.send(req.body)352 })353 354 fastify.addContentTypeParser('*', function (req, payload, done) {355 let data = ''356 payload.on('data', chunk => { data += chunk })357 payload.on('end', () => {358 done(null, data)359 })360 })361 362 fastify.listen({ port: 0 }, err => {363 t.error(err)364 365 sget({366 method: 'POST',367 url: getServerUrl(fastify),368 body: 'hello',369 headers: {370 'Content-Type': 'application/jsoff'371 }372 }, (err, response, body) => {373 t.error(err)374 t.equal(response.statusCode, 200)375 t.same(body.toString(), 'hello')376 377 sget({378 method: 'POST',379 url: getServerUrl(fastify),380 body: 'hello',381 headers: {382 'Content-Type': 'very-weird-content-type'383 }384 }, (err, response, body) => {385 t.error(err)386 t.equal(response.statusCode, 200)387 t.same(body.toString(), 'hello')388 fastify.close()389 })390 })391 })392})393 394test('catch all content type parser should not interfere with other conte type parsers', t => {395 t.plan(7)396 const fastify = Fastify()397 398 fastify.post('/', (req, reply) => {399 reply.send(req.body)400 })401 402 fastify.addContentTypeParser('*', function (req, payload, done) {403 let data = ''404 payload.on('data', chunk => { data += chunk })405 payload.on('end', () => {406 done(null, data)407 })408 })409 410 fastify.addContentTypeParser('application/jsoff', function (req, payload, done) {411 jsonParser(payload, function (err, body) {412 done(err, body)413 })414 })415 416 fastify.listen({ port: 0 }, err => {417 t.error(err)418 419 sget({420 method: 'POST',421 url: getServerUrl(fastify),422 body: '{"hello":"world"}',423 headers: {424 'Content-Type': 'application/jsoff'425 }426 }, (err, response, body) => {427 t.error(err)428 t.equal(response.statusCode, 200)429 t.same(body.toString(), JSON.stringify({ hello: 'world' }))430 431 sget({432 method: 'POST',433 url: getServerUrl(fastify),434 body: 'hello',435 headers: {436 'Content-Type': 'very-weird-content-type'437 }438 }, (err, response, body) => {439 t.error(err)440 t.equal(response.statusCode, 200)441 t.same(body.toString(), 'hello')442 fastify.close()443 })444 })445 })446})447 448// Issue 492 https://github.com/fastify/fastify/issues/492449test('\'*\' catch undefined Content-Type requests', t => {450 t.plan(4)451 452 const fastify = Fastify()453 454 t.teardown(fastify.close.bind(fastify))455 456 fastify.addContentTypeParser('*', function (req, payload, done) {457 let data = ''458 payload.on('data', chunk => { data += chunk })459 payload.on('end', () => {460 done(null, data)461 })462 })463 464 fastify.post('/', (req, res) => {465 // Needed to avoid json stringify466 res.type('text/plain').send(req.body)467 })468 469 fastify.listen({ port: 0 }, function (err) {470 t.error(err)471 472 const fileStream = fs.createReadStream(__filename)473 474 sget({475 method: 'POST',476 url: getServerUrl(fastify) + '/',477 body: fileStream478 }, (err, response, body) => {479 t.error(err)480 t.equal(response.statusCode, 200)481 t.equal(body + '', fs.readFileSync(__filename).toString())482 })483 })484})485 486test('cannot add custom parser after binding', t => {487 t.plan(2)488 489 const fastify = Fastify()490 491 t.teardown(fastify.close.bind(fastify))492 493 fastify.post('/', (req, res) => {494 res.type('text/plain').send(req.body)495 })496 497 fastify.listen({ port: 0 }, function (err) {498 t.error(err)499 500 try {501 fastify.addContentTypeParser('*', () => {})502 t.fail()503 } catch (e) {504 t.pass()505 }506 })507})508 509test('Can override the default json parser', t => {510 t.plan(5)511 const fastify = Fastify()512 513 fastify.post('/', (req, reply) => {514 reply.send(req.body)515 })516 517 fastify.addContentTypeParser('application/json', function (req, payload, done) {518 t.ok('called')519 jsonParser(payload, function (err, body) {520 done(err, body)521 })522 })523 524 fastify.listen({ port: 0 }, err => {525 t.error(err)526 527 sget({528 method: 'POST',529 url: getServerUrl(fastify),530 body: '{"hello":"world"}',531 headers: {532 'Content-Type': 'application/json'533 }534 }, (err, response, body) => {535 t.error(err)536 t.equal(response.statusCode, 200)537 t.equal(body.toString(), '{"hello":"world"}')538 fastify.close()539 })540 })541})542 543test('Can override the default plain text parser', t => {544 t.plan(5)545 const fastify = Fastify()546 547 fastify.post('/', (req, reply) => {548 reply.send(req.body)549 })550 551 fastify.addContentTypeParser('text/plain', function (req, payload, done) {552 t.ok('called')553 plainTextParser(payload, function (err, body) {554 done(err, body)555 })556 })557 558 fastify.listen({ port: 0 }, err => {559 t.error(err)560 561 sget({562 method: 'POST',563 url: getServerUrl(fastify),564 body: 'hello world',565 headers: {566 'Content-Type': 'text/plain'567 }568 }, (err, response, body) => {569 t.error(err)570 t.equal(response.statusCode, 200)571 t.equal(body.toString(), 'hello world')572 fastify.close()573 })574 })575})576 577test('Can override the default json parser in a plugin', t => {578 t.plan(5)579 const fastify = Fastify()580 581 fastify.register((instance, opts, done) => {582 instance.addContentTypeParser('application/json', function (req, payload, done) {583 t.ok('called')584 jsonParser(payload, function (err, body) {585 done(err, body)586 })587 })588 589 instance.post('/', (req, reply) => {590 reply.send(req.body)591 })592 593 done()594 })595 596 fastify.listen({ port: 0 }, err => {597 t.error(err)598 599 sget({600 method: 'POST',601 url: getServerUrl(fastify),602 body: '{"hello":"world"}',603 headers: {604 'Content-Type': 'application/json'605 }606 }, (err, response, body) => {607 t.error(err)608 t.equal(response.statusCode, 200)609 t.equal(body.toString(), '{"hello":"world"}')610 fastify.close()611 })612 })613})614 615test('Can\'t override the json parser multiple times', t => {616 t.plan(2)617 const fastify = Fastify()618 619 fastify.addContentTypeParser('application/json', function (req, payload, done) {620 jsonParser(payload, function (err, body) {621 done(err, body)622 })623 })624 625 try {626 fastify.addContentTypeParser('application/json', function (req, payload, done) {627 t.ok('called')628 jsonParser(payload, function (err, body) {629 done(err, body)630 })631 })632 } catch (err) {633 t.equal(err.code, 'FST_ERR_CTP_ALREADY_PRESENT')634 t.equal(err.message, 'Content type parser \'application/json\' already present.')635 }636})637 638test('Can\'t override the plain text parser multiple times', t => {639 t.plan(2)640 const fastify = Fastify()641 642 fastify.addContentTypeParser('text/plain', function (req, payload, done) {643 plainTextParser(payload, function (err, body) {644 done(err, body)645 })646 })647 648 try {649 fastify.addContentTypeParser('text/plain', function (req, payload, done) {650 t.ok('called')651 plainTextParser(payload, function (err, body) {652 done(err, body)653 })654 })655 } catch (err) {656 t.equal(err.code, 'FST_ERR_CTP_ALREADY_PRESENT')657 t.equal(err.message, 'Content type parser \'text/plain\' already present.')658 }659})660 661test('Should get the body as string', t => {662 t.plan(6)663 const fastify = Fastify()664 665 fastify.post('/', (req, reply) => {666 reply.send(req.body)667 })668 669 fastify.addContentTypeParser('application/json', { parseAs: 'string' }, function (req, body, done) {670 t.ok('called')671 t.ok(typeof body === 'string')672 try {673 const json = JSON.parse(body)674 done(null, json)675 } catch (err) {676 err.statusCode = 400677 done(err, undefined)678 }679 })680 681 fastify.listen({ port: 0 }, err => {682 t.error(err)683 684 sget({685 method: 'POST',686 url: getServerUrl(fastify),687 body: '{"hello":"world"}',688 headers: {689 'Content-Type': 'application/json'690 }691 }, (err, response, body) => {692 t.error(err)693 t.equal(response.statusCode, 200)694 t.equal(body.toString(), '{"hello":"world"}')695 fastify.close()696 })697 })698})699 700test('Should return defined body with no custom parser defined and content type = \'text/plain\'', t => {701 t.plan(4)702 const fastify = Fastify()703 704 fastify.post('/', (req, reply) => {705 reply.send(req.body)706 })707 708 fastify.listen({ port: 0 }, err => {709 t.error(err)710 711 sget({712 method: 'POST',713 url: getServerUrl(fastify),714 body: 'hello world',715 headers: {716 'Content-Type': 'text/plain'717 }718 }, (err, response, body) => {719 t.error(err)720 t.equal(response.statusCode, 200)721 t.equal(body.toString(), 'hello world')722 fastify.close()723 })724 })725})726 727test('Should have typeof body object with no custom parser defined, no body defined and content type = \'text/plain\'', t => {728 t.plan(4)729 const fastify = Fastify()730 731 fastify.post('/', (req, reply) => {732 reply.send(req.body)733 })734 735 fastify.listen({ port: 0 }, err => {736 t.error(err)737 738 sget({739 method: 'POST',740 url: getServerUrl(fastify),741 headers: {742 'Content-Type': 'text/plain'743 }744 }, (err, response, body) => {745 t.error(err)746 t.equal(response.statusCode, 200)747 t.equal(typeof body, 'object')748 fastify.close()749 })750 })751})752 