Skip to content

Commit

Permalink
feat: add support for maxFields on multipart body
Browse files Browse the repository at this point in the history
Closes #19
Fixes #18
  • Loading branch information
thetutlage committed May 24, 2019
1 parent abcd2bb commit 39e544d
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 19 deletions.
11 changes: 11 additions & 0 deletions config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,17 @@ export const config: BodyParserConfig = {
*/
encoding: 'utf-8',

/*
|--------------------------------------------------------------------------
| Max Fields
|--------------------------------------------------------------------------
|
| The maximum number of fields allowed in the request body. The field includes
| text inputs and files both.
|
*/
maxFields: 1000,

/*
|--------------------------------------------------------------------------
| Request body limit
Expand Down
2 changes: 1 addition & 1 deletion japaFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ require('ts-node/register')

const { configure } = require('japa')
configure({
files: ['test/**/body-parser.spec.ts']
files: ['test/**/*.spec.ts']
})
4 changes: 3 additions & 1 deletion src/BodyParser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ export class BodyParserMiddleware {
const multipartConfig = this._getConfigFor('multipart')

if (this._isType(request, multipartConfig.types)) {
request['multipart'] = new Multipart(request.request)
request['multipart'] = new Multipart(request.request, {
maxFields: multipartConfig.maxFields,
})

/**
* Skip parsing when `autoProcess` is disabled or route matches one
Expand Down
1 change: 1 addition & 0 deletions src/Contracts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ declare module '@ioc:Adonis/Src/BodyParser' {
*/
type BodyParserMultipartConfig = BodyParserBaseConfig & {
autoProcess: boolean,
maxFields: number,
processManually: string[],
tmpFileName (): string,
}
Expand Down
12 changes: 9 additions & 3 deletions src/Multipart/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export class Multipart implements MultipartContract {
*/
public consumed = false

constructor (private _request: IncomingMessage) {
constructor (private _request: IncomingMessage, private _config: { maxFields: number }) {
}

/**
Expand Down Expand Up @@ -203,13 +203,19 @@ export class Multipart implements MultipartContract {
return
}

const form = new multiparty.Form()
const form = new multiparty.Form(this._config)

/**
* Raise error when form encounters an
* error
*/
form.on('error', reject)
form.on('error', (error: Error) => {
if (error.message === 'maxFields 1 exceeded.') {
reject(new Exception('Max fields limit exceeded', 413, 'E_REQUEST_ENTITY_TOO_LARGE'))
} else {
reject(error)
}
})

/**
* Process each part at a time and also resolve the
Expand Down
8 changes: 4 additions & 4 deletions test/file.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ test.group('File', () => {
const server = createServer(async (req, res) => {
let file: File | null = null

const multipart = new Multipart(req)
const multipart = new Multipart(req, { maxFields: 1000 })
multipart.onFile('package', async (p) => {
file = new File({
fileName: p.filename,
Expand Down Expand Up @@ -67,7 +67,7 @@ test.group('File', () => {
const server = createServer(async (req, res) => {
let file: File | null = null

const multipart = new Multipart(req)
const multipart = new Multipart(req, { maxFields: 1000 })
multipart.onFile('package', async (p) => {
file = new File({
fileName: p.filename,
Expand All @@ -94,7 +94,7 @@ test.group('File', () => {
const server = createServer(async (req, res) => {
let file: File | null = null

const multipart = new Multipart(req)
const multipart = new Multipart(req, { maxFields: 1000 })
multipart.onFile('package', async (p) => {
file = new File({
fileName: p.filename,
Expand Down Expand Up @@ -138,7 +138,7 @@ test.group('File', () => {
const server = createServer(async (req, res) => {
let file: File | null = null

const multipart = new Multipart(req)
const multipart = new Multipart(req, { maxFields: 1000 })
multipart.onFile('package', async (p) => {
file = new File({
fileName: p.filename,
Expand Down
43 changes: 33 additions & 10 deletions test/multipart.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ test.group('Multipart', () => {
let part: null | MultipartStream = null

const server = createServer(async (req, res) => {
const multipart = new Multipart(req)
const multipart = new Multipart(req, { maxFields: 1000 })
multipart.onFile('package', async (p) => {
part = p
part.resume()
Expand All @@ -49,7 +49,7 @@ test.group('Multipart', () => {
let part: null | MultipartStream = null

const server = createServer(async (req, res) => {
const multipart = new Multipart(req)
const multipart = new Multipart(req, { maxFields: 1000 })
multipart.onFile('package', async () => {
throw new Error('Cannot process')
})
Expand All @@ -73,7 +73,7 @@ test.group('Multipart', () => {
const stack: string[] = []

const server = createServer(async (req, res) => {
const multipart = new Multipart(req)
const multipart = new Multipart(req, { maxFields: 1000 })
multipart.onFile('package', async (part) => {
stack.push('before')
part.resume()
Expand All @@ -94,7 +94,7 @@ test.group('Multipart', () => {
const SAMPLE_FILE_PATH = join(__dirname, './sample.json')

const server = createServer(async (req, res) => {
const multipart = new Multipart(req)
const multipart = new Multipart(req, { maxFields: 1000 })

multipart.onFile('package', async (part) => {
part.pipe(createWriteStream(SAMPLE_FILE_PATH))
Expand All @@ -115,7 +115,7 @@ test.group('Multipart', () => {
const stack: string[] = []

const server = createServer(async (req, res) => {
const multipart = new Multipart(req)
const multipart = new Multipart(req, { maxFields: 1000 })
multipart.onFile('package', async (part) => {
stack.push('before')
part.resume()
Expand All @@ -136,7 +136,7 @@ test.group('Multipart', () => {
const stack: string[] = []

const server = createServer(async (req, res) => {
const multipart = new Multipart(req)
const multipart = new Multipart(req, { maxFields: 1000 })
multipart.onFile('package', async (part) => {
stack.push('before')
part.resume()
Expand All @@ -157,7 +157,7 @@ test.group('Multipart', () => {
const stack: string[] = []

const server = createServer(async (req, res) => {
const multipart = new Multipart(req)
const multipart = new Multipart(req, { maxFields: 1000 })
multipart.onFile('*', async (part) => {
stack.push('before')
part.resume()
Expand All @@ -179,7 +179,7 @@ test.group('Multipart', () => {
assert.plan(3)

const server = createServer(async (req, res) => {
const multipart = new Multipart(req)
const multipart = new Multipart(req, { maxFields: 1000 })
multipart.onFile('*', async (part) => {
stack.push('file')
part.resume()
Expand All @@ -206,7 +206,7 @@ test.group('Multipart', () => {

test('pass errors from field handler to upstream', async (assert) => {
const server = createServer(async (req, res) => {
const multipart = new Multipart(req)
const multipart = new Multipart(req, { maxFields: 1000 })
multipart.onField('name', () => {
throw new Error('bad name')
})
Expand All @@ -230,7 +230,7 @@ test.group('Multipart', () => {

test('raise error when process is invoked multipart times', async (assert) => {
const server = createServer(async (req, res) => {
const multipart = new Multipart(req)
const multipart = new Multipart(req, { maxFields: 1000 })
try {
await multipart.process()
await multipart.process()
Expand All @@ -247,4 +247,27 @@ test.group('Multipart', () => {

assert.equal(text, 'E_RUNTIME_EXCEPTION: multipart stream has already been consumed')
})

test('raise error when maxFields are crossed', async (assert) => {
const server = createServer(async (req, res) => {
const multipart = new Multipart(req, { maxFields: 1 })
multipart.onField('*', async () => {
})

try {
await multipart.process()
res.end()
} catch (error) {
res.writeHead(500)
res.end(error.message)
}
})

const { text } = await supertest(server)
.post('/')
.field('name', 'virk')
.field('age', '22')

assert.equal(text, 'E_REQUEST_ENTITY_TOO_LARGE: Max fields limit exceeded')
})
})

0 comments on commit 39e544d

Please sign in to comment.