diff --git a/src/bindings/request.ts b/src/bindings/request.ts index c89a514..afc8e5d 100644 --- a/src/bindings/request.ts +++ b/src/bindings/request.ts @@ -90,7 +90,7 @@ Request.macro( Request.macro('allFiles', function allFiles(this: Request) { if (!this.__raw_files) { throw new RuntimeException( - 'Cannot read files. Make sure the bodyparser middleware is registered' + 'Cannot read files. Make sure the bodyparser middleware is registered and enabled' ) } diff --git a/src/bodyparser_middleware.ts b/src/bodyparser_middleware.ts index 2b4489f..32da499 100644 --- a/src/bodyparser_middleware.ts +++ b/src/bodyparser_middleware.ts @@ -140,6 +140,13 @@ export class BodyParserMiddleware { if (this.#isType(ctx.request, multipartConfig.types)) { debug('detected multipart request "%s:%s"', requestMethod, requestUrl) + if (!multipartConfig.enabled) { + throw new Exception('request content-type not supported', { + status: 415, + code: 'E_REQUEST_UNSUPPORTED_MEDIA_TYPE', + }) + } + ctx.request.multipart = new Multipart(ctx, { maxFields: multipartConfig.maxFields, limit: multipartConfig.limit, diff --git a/src/define_config.ts b/src/define_config.ts index 016debc..5b5c444 100644 --- a/src/define_config.ts +++ b/src/define_config.ts @@ -48,6 +48,7 @@ export function defineConfig(config: BodyParserOptionalConfig): BodyParserConfig }, multipart: { + enabled: true, autoProcess: true, processManually: [], encoding: 'utf-8', diff --git a/src/types.ts b/src/types.ts index e472db0..c5f4f06 100644 --- a/src/types.ts +++ b/src/types.ts @@ -63,6 +63,7 @@ export type BodyParserRawConfig = BodyParserBaseConfig * Parser config for parsing multipart requests */ export type BodyParserMultipartConfig = BodyParserBaseConfig & { + enabled: boolean autoProcess: boolean | string[] maxFields: number processManually: string[] diff --git a/tests/body_parser.spec.ts b/tests/body_parser.spec.ts index 370caec..50faeaa 100644 --- a/tests/body_parser.spec.ts +++ b/tests/body_parser.spec.ts @@ -201,6 +201,44 @@ test.group('BodyParser Middleware | form data', () => { }) }) + test('abort when multipart is not enabled', async ({ assert, cleanup }) => { + const server = createServer(async (req, res) => { + const request = new RequestFactory().merge({ req, res }).create() + const response = new ResponseFactory().merge({ req, res }).create() + const ctx = new HttpContextFactory().merge({ request, response }).create() + const middleware = new BodyParserMiddlewareFactory() + .merge({ + multipart: { + enabled: false, + }, + }) + .create() + + try { + await middleware.handle(ctx, async () => {}) + } catch (error) { + res.writeHead(error.status) + res.end(error.message) + } + }) + cleanup(() => { + server.close() + }) + + await new Promise((resolve) => server.listen(3333, 'localhost', () => resolve())) + + const response = await fetch('http://localhost:3333', { + method: 'POST', + headers: { + 'Content-type': `multipart/form-data; boundary=9d01a3fb93deedb4d0a81389271d097f28fd67e2fcbff2932befc0458ad7`, + }, + body: '--9d01a3fb93deedb4d0a81389271d097f28fd67e2fcbff2932befc0458ad7\x0d\x0aContent-Disposition: form-data; name="test"; filename="csv_files/test.csv"\x0d\x0aContent-Type: application/octet-stream\x0d\x0a\x0d\x0atest123', + }) + + assert.equal(response.status, 415) + assert.equal(await response.text(), 'request content-type not supported') + }) + test('abort when multipart body is invalid', async ({ assert, cleanup }) => { const server = createServer(async (req, res) => { const request = new RequestFactory().merge({ req, res }).create()