Skip to content

Commit 85d7801

Browse files
committed
Allow global multipart. Closes #4027
1 parent 8656725 commit 85d7801

File tree

3 files changed

+60
-7
lines changed

3 files changed

+60
-7
lines changed

lib/config.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,7 @@ internals.routeBase = Joi.object({
145145
output: Joi.valid('data', 'stream', 'file', 'annotated').required()
146146
})
147147
.default(false)
148-
.allow(true, false)
149-
.when('.', { is: true, then: Joi.object().strip() }),
148+
.allow(true, false),
150149
allow: Joi.array().items(Joi.string()).single(),
151150
override: Joi.string(),
152151
protoAction: Joi.valid('error', 'remove', 'ignore').default('error'),

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
"web"
1616
],
1717
"dependencies": {
18-
"@hapi/accept": "5.x.x",
19-
"@hapi/ammo": "5.x.x",
18+
"@hapi/accept": "^5.0.1",
19+
"@hapi/ammo": "^5.0.1",
2020
"@hapi/boom": "9.x.x",
2121
"@hapi/bounce": "2.x.x",
2222
"@hapi/call": "8.x.x",
@@ -29,8 +29,8 @@
2929
"@hapi/podium": "4.x.x",
3030
"@hapi/shot": "5.x.x",
3131
"@hapi/somever": "3.x.x",
32-
"@hapi/statehood": "7.x.x",
33-
"@hapi/subtext": "7.x.x",
32+
"@hapi/statehood": "^7.0.2",
33+
"@hapi/subtext": "^7.0.3",
3434
"@hapi/teamwork": "4.x.x",
3535
"@hapi/topo": "5.x.x"
3636
},

test/payload.js

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ describe('Payload', () => {
531531
expect(res.statusCode).to.equal(415);
532532
});
533533

534-
it('returns parsed multipart data', async () => {
534+
it('returns parsed multipart data (route)', async () => {
535535

536536
const multipartPayload =
537537
'--AaB03x\r\n' +
@@ -585,6 +585,60 @@ describe('Payload', () => {
585585
expect(res.result.pics).to.exist();
586586
});
587587

588+
it('returns parsed multipart data (server)', async () => {
589+
590+
const multipartPayload =
591+
'--AaB03x\r\n' +
592+
'content-disposition: form-data; name="x"\r\n' +
593+
'\r\n' +
594+
'First\r\n' +
595+
'--AaB03x\r\n' +
596+
'content-disposition: form-data; name="x"\r\n' +
597+
'\r\n' +
598+
'Second\r\n' +
599+
'--AaB03x\r\n' +
600+
'content-disposition: form-data; name="x"\r\n' +
601+
'\r\n' +
602+
'Third\r\n' +
603+
'--AaB03x\r\n' +
604+
'content-disposition: form-data; name="field1"\r\n' +
605+
'\r\n' +
606+
'Joe Blow\r\nalmost tricked you!\r\n' +
607+
'--AaB03x\r\n' +
608+
'content-disposition: form-data; name="field1"\r\n' +
609+
'\r\n' +
610+
'Repeated name segment\r\n' +
611+
'--AaB03x\r\n' +
612+
'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n' +
613+
'Content-Type: text/plain\r\n' +
614+
'\r\n' +
615+
'... contents of file1.txt ...\r\r\n' +
616+
'--AaB03x--\r\n';
617+
618+
const handler = (request) => {
619+
620+
const result = {};
621+
const keys = Object.keys(request.payload);
622+
for (let i = 0; i < keys.length; ++i) {
623+
const key = keys[i];
624+
const value = request.payload[key];
625+
result[key] = value._readableState ? true : value;
626+
}
627+
628+
return result;
629+
};
630+
631+
const server = Hapi.server({ routes: { payload: { multipart: true } } });
632+
server.route({ method: 'POST', path: '/echo', handler });
633+
634+
const res = await server.inject({ method: 'POST', url: '/echo', payload: multipartPayload, headers: { 'content-type': 'multipart/form-data; boundary=AaB03x' } });
635+
expect(Object.keys(res.result).length).to.equal(3);
636+
expect(res.result.field1).to.exist();
637+
expect(res.result.field1.length).to.equal(2);
638+
expect(res.result.field1[1]).to.equal('Repeated name segment');
639+
expect(res.result.pics).to.exist();
640+
});
641+
588642
it('signals connection close when payload is unconsumed', async () => {
589643

590644
const payload = Buffer.alloc(1024);

0 commit comments

Comments
 (0)