Skip to content

Commit 239f149

Browse files
authored
feat: add per route busboy configuration (#580)
1 parent b4c6b3d commit 239f149

5 files changed

Lines changed: 157 additions & 1 deletion

File tree

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,20 @@ fastify.post('/', async function (req, reply) {
125125
})
126126
```
127127

128+
Or to a route options when `attachFieldsToBody` is used.
129+
```js
130+
fastify.post('/', {
131+
config: {
132+
multipartOptions: {
133+
limits: { fileSize: 1000 }
134+
}
135+
}
136+
}, async function (req, reply) {
137+
const buffer = req.body.file.toBuffer();
138+
reply.send()
139+
})
140+
```
141+
128142
## Handle multiple file streams
129143

130144
```js

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ function fastifyMultipart (fastify, options, done) {
7272
return
7373
}
7474

75-
for await (const part of req.parts()) {
75+
for await (const part of req.parts(req.routeOptions.config.multipartOptions)) {
7676
req.body = part.fields
7777

7878
if (part.file) {

test/multipart-fileLimit.test.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,3 +316,127 @@ test('should NOT throw fileSize limitation error when throwFileSizeLimit is glob
316316
t.assert.ifError(error)
317317
}
318318
})
319+
320+
test('should throw fileSize limitation error when used alongside attachFieldsToBody and set request config', async function (t) {
321+
t.plan(1)
322+
323+
const fastify = Fastify()
324+
t.after(() => fastify.close())
325+
326+
fastify.register(multipart, {
327+
attachFieldsToBody: true
328+
})
329+
330+
const randomFileBuffer = Buffer.alloc(900_000)
331+
crypto.randomFillSync(randomFileBuffer)
332+
333+
fastify.post('/', {
334+
config: {
335+
multipartOptions: {
336+
limits: {
337+
fileSize: 800_000
338+
}
339+
}
340+
}
341+
}, async function (req, reply) {
342+
t.assert.fail('it should throw')
343+
344+
reply.status(200).send()
345+
})
346+
347+
await fastify.listen({ port: 0 })
348+
349+
// request
350+
const form = new FormData()
351+
const opts = {
352+
hostname: '127.0.0.1',
353+
port: fastify.server.address().port,
354+
path: '/',
355+
headers: form.getHeaders(),
356+
method: 'POST'
357+
}
358+
359+
const tmpFile = 'test/random-file'
360+
fs.writeFileSync(tmpFile, randomFileBuffer)
361+
362+
const req = http.request(opts)
363+
form.append('upload', fs.createReadStream(tmpFile))
364+
365+
form.pipe(req)
366+
367+
try {
368+
const [res] = await once(req, 'response')
369+
t.assert.equal(res.statusCode, 413)
370+
res.resume()
371+
await once(res, 'end')
372+
373+
fs.unlinkSync(tmpFile)
374+
} catch (error) {
375+
t.assert.ifError(error)
376+
}
377+
})
378+
379+
test('should not throw fileSize limitation error when used alongside attachFieldsToBody and set request config', async function (t) {
380+
t.plan(4)
381+
382+
const fastify = Fastify()
383+
t.after(() => fastify.close())
384+
385+
fastify.register(multipart, {
386+
attachFieldsToBody: true
387+
})
388+
389+
const randomFileBuffer = Buffer.alloc(900_000)
390+
crypto.randomFillSync(randomFileBuffer)
391+
392+
fastify.post('/', {
393+
config: {
394+
multipartOptions: {
395+
limits: {
396+
fileSize: 1_000_000
397+
}
398+
}
399+
}
400+
}, async function (req, reply) {
401+
t.assert.ok(req.isMultipart())
402+
403+
t.assert.deepStrictEqual(Object.keys(req.body), ['upload'])
404+
405+
const content = await req.body.upload.toBuffer()
406+
407+
t.assert.strictEqual(content.toString(), randomFileBuffer.toString())
408+
409+
reply.status(200).send()
410+
})
411+
412+
await fastify.listen({ port: 0 })
413+
414+
// request
415+
const form = new FormData()
416+
const opts = {
417+
hostname: '127.0.0.1',
418+
port: fastify.server.address().port,
419+
path: '/',
420+
headers: form.getHeaders(),
421+
method: 'POST'
422+
}
423+
424+
const tmpFile = 'test/random-file'
425+
fs.writeFileSync(tmpFile, randomFileBuffer)
426+
427+
const req = http.request(opts)
428+
form.append('upload', fs.createReadStream(tmpFile))
429+
430+
form.pipe(req)
431+
432+
try {
433+
const [res] = await once(req, 'response')
434+
t.assert.equal(res.statusCode, 200)
435+
res.resume()
436+
await once(res, 'end')
437+
438+
fs.unlinkSync(tmpFile)
439+
} catch (error) {
440+
t.assert.ifError(error)
441+
}
442+
})

types/index.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ declare module 'fastify' {
3535
interface FastifyInstance {
3636
multipartErrors: MultipartErrors;
3737
}
38+
39+
interface FastifyContextConfig {
40+
multipartOptions?: Omit<BusboyConfig, 'headers'>
41+
}
3842
}
3943

4044
type FastifyMultipartPlugin = FastifyPluginCallback<

types/index.test-d.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,20 @@ const runServer = async () => {
179179
reply.send()
180180
})
181181

182+
app.post('/upload/files', {
183+
config: {
184+
multipartOptions: {}
185+
}
186+
}, async function (req, reply) {
187+
expectType<Omit<BusboyConfig, 'headers'>>(req.routeOptions.config.multipartOptions)
188+
reply.send()
189+
})
190+
191+
app.post('/upload/files', async function (req, reply) {
192+
expectError<Omit<BusboyConfig, 'headers'>>(req.routeOptions.config?.multipartOptions)
193+
reply.send()
194+
})
195+
182196
await app.ready()
183197
}
184198

0 commit comments

Comments
 (0)