From 85d7801cc8bc38c5ad30b5e29ed36e328617fb28 Mon Sep 17 00:00:00 2001 From: Eran Hammer Date: Thu, 13 Feb 2020 11:14:52 -0800 Subject: [PATCH] Allow global multipart. Closes #4027 --- lib/config.js | 3 +-- package.json | 8 +++---- test/payload.js | 56 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/lib/config.js b/lib/config.js index dd121b00d..7732201e5 100755 --- a/lib/config.js +++ b/lib/config.js @@ -145,8 +145,7 @@ internals.routeBase = Joi.object({ output: Joi.valid('data', 'stream', 'file', 'annotated').required() }) .default(false) - .allow(true, false) - .when('.', { is: true, then: Joi.object().strip() }), + .allow(true, false), allow: Joi.array().items(Joi.string()).single(), override: Joi.string(), protoAction: Joi.valid('error', 'remove', 'ignore').default('error'), diff --git a/package.json b/package.json index d7a61921f..8da91128f 100755 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "web" ], "dependencies": { - "@hapi/accept": "5.x.x", - "@hapi/ammo": "5.x.x", + "@hapi/accept": "^5.0.1", + "@hapi/ammo": "^5.0.1", "@hapi/boom": "9.x.x", "@hapi/bounce": "2.x.x", "@hapi/call": "8.x.x", @@ -29,8 +29,8 @@ "@hapi/podium": "4.x.x", "@hapi/shot": "5.x.x", "@hapi/somever": "3.x.x", - "@hapi/statehood": "7.x.x", - "@hapi/subtext": "7.x.x", + "@hapi/statehood": "^7.0.2", + "@hapi/subtext": "^7.0.3", "@hapi/teamwork": "4.x.x", "@hapi/topo": "5.x.x" }, diff --git a/test/payload.js b/test/payload.js index bce515a7d..4cb97abcb 100755 --- a/test/payload.js +++ b/test/payload.js @@ -531,7 +531,7 @@ describe('Payload', () => { expect(res.statusCode).to.equal(415); }); - it('returns parsed multipart data', async () => { + it('returns parsed multipart data (route)', async () => { const multipartPayload = '--AaB03x\r\n' + @@ -585,6 +585,60 @@ describe('Payload', () => { expect(res.result.pics).to.exist(); }); + it('returns parsed multipart data (server)', async () => { + + const multipartPayload = + '--AaB03x\r\n' + + 'content-disposition: form-data; name="x"\r\n' + + '\r\n' + + 'First\r\n' + + '--AaB03x\r\n' + + 'content-disposition: form-data; name="x"\r\n' + + '\r\n' + + 'Second\r\n' + + '--AaB03x\r\n' + + 'content-disposition: form-data; name="x"\r\n' + + '\r\n' + + 'Third\r\n' + + '--AaB03x\r\n' + + 'content-disposition: form-data; name="field1"\r\n' + + '\r\n' + + 'Joe Blow\r\nalmost tricked you!\r\n' + + '--AaB03x\r\n' + + 'content-disposition: form-data; name="field1"\r\n' + + '\r\n' + + 'Repeated name segment\r\n' + + '--AaB03x\r\n' + + 'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n' + + 'Content-Type: text/plain\r\n' + + '\r\n' + + '... contents of file1.txt ...\r\r\n' + + '--AaB03x--\r\n'; + + const handler = (request) => { + + const result = {}; + const keys = Object.keys(request.payload); + for (let i = 0; i < keys.length; ++i) { + const key = keys[i]; + const value = request.payload[key]; + result[key] = value._readableState ? true : value; + } + + return result; + }; + + const server = Hapi.server({ routes: { payload: { multipart: true } } }); + server.route({ method: 'POST', path: '/echo', handler }); + + const res = await server.inject({ method: 'POST', url: '/echo', payload: multipartPayload, headers: { 'content-type': 'multipart/form-data; boundary=AaB03x' } }); + expect(Object.keys(res.result).length).to.equal(3); + expect(res.result.field1).to.exist(); + expect(res.result.field1.length).to.equal(2); + expect(res.result.field1[1]).to.equal('Repeated name segment'); + expect(res.result.pics).to.exist(); + }); + it('signals connection close when payload is unconsumed', async () => { const payload = Buffer.alloc(1024);