Skip to content

Commit 9f0042b

Browse files
committed
Merge pull request #9 from cjihrig/maxsize
add maxBytes setting to detect oversized payloads
2 parents 3b84f32 + c78e7be commit 9f0042b

File tree

3 files changed

+69
-4
lines changed

3 files changed

+69
-4
lines changed

lib/index.js

+25-3
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,20 @@ internals.state = {
5151
};
5252

5353

54-
exports.Dispenser = internals.Dispenser = function (contentType) {
54+
internals.defaults = {
55+
maxBytes: Infinity
56+
};
57+
58+
59+
exports.Dispenser = internals.Dispenser = function (options) {
5560

5661
Stream.Writable.call(this);
5762

58-
this._boundary = contentType.boundary;
63+
Hoek.assert(options !== null && typeof options === 'object',
64+
'options must be an object');
65+
const settings = Hoek.applyToDefaults(internals.defaults, options);
66+
67+
this._boundary = settings.boundary;
5968
this._state = internals.state.preamble;
6069
this._held = '';
6170

@@ -64,8 +73,10 @@ exports.Dispenser = internals.Dispenser = function (contentType) {
6473
this._name = '';
6574
this._pendingHeader = '';
6675
this._error = null;
76+
this._bytes = 0;
77+
this._maxBytes = settings.maxBytes;
6778

68-
this._parts = new Nigel.Stream(new Buffer('--' + contentType.boundary));
79+
this._parts = new Nigel.Stream(new Buffer('--' + settings.boundary));
6980
this._lines = new Nigel.Stream(new Buffer('\r\n'));
7081

7182
this._parts.on('needle', () => {
@@ -102,6 +113,7 @@ exports.Dispenser = internals.Dispenser = function (contentType) {
102113
let finish = (err) => {
103114

104115
if (piper) {
116+
piper.removeListener('data', onReqData);
105117
piper.removeListener('error', finish);
106118
piper.removeListener('aborted', onReqAborted);
107119
}
@@ -143,9 +155,19 @@ exports.Dispenser = internals.Dispenser = function (contentType) {
143155
finish(Boom.badRequest('Client request aborted'));
144156
};
145157

158+
const onReqData = (data) => {
159+
160+
this._bytes += Buffer.byteLength(data);
161+
162+
if (this._bytes > this._maxBytes) {
163+
finish(Boom.badRequest('Maximum size exceeded'));
164+
}
165+
};
166+
146167
this.once('pipe', (req) => {
147168

148169
piper = req;
170+
req.on('data', onReqData);
149171
req.once('error', finish);
150172
req.once('aborted', onReqAborted);
151173
});

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"devDependencies": {
2323
"code": "2.x.x",
2424
"form-data": "0.2.x",
25-
"lab": "9.x.x",
25+
"lab": "10.x.x",
2626
"shot": "3.x.x",
2727
"wreck": "7.x.x"
2828
},

test/index.js

+43
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,24 @@ describe('Dispenser', () => {
4444
req.pipe(dispenser);
4545
};
4646

47+
it('throws on invalid options', (done) => {
48+
49+
const fail = (options) => {
50+
51+
expect(() => {
52+
53+
return new Pez.Dispenser(options);
54+
}).to.throw(Error, 'options must be an object');
55+
};
56+
57+
fail();
58+
fail(null);
59+
fail('foo');
60+
fail(1);
61+
fail(false);
62+
done();
63+
});
64+
4765
it('parses RFC1867 payload', (done) => {
4866

4967
const payload =
@@ -587,6 +605,31 @@ describe('Dispenser', () => {
587605
});
588606
});
589607

608+
it('errors if the payload size exceeds the byte limit', (done) => {
609+
610+
const payload =
611+
'--AaB03x\r\n' +
612+
'content-disposition: form-data; name="file"; filename="file1.txt"\r\n' +
613+
'Content-Type: text/plain\r\n' +
614+
'\r\n' +
615+
'I am a plain text file\r\n' +
616+
'--AaB03x--\r\n';
617+
618+
const req = new internals.Payload(payload, true);
619+
req.headers = { 'content-type': 'multipart/form-data; boundary="AaB03x"' };
620+
621+
const dispenser = new Pez.Dispenser({ boundary: 'AaB03x', maxBytes: payload.length - 1 });
622+
623+
dispenser.once('error', (err) => {
624+
625+
expect(err).to.exist();
626+
expect(err.message).to.equal('Maximum size exceeded');
627+
done();
628+
});
629+
630+
req.pipe(dispenser);
631+
});
632+
590633
it('parses a request with "=" in the boundary', (done) => {
591634

592635
const payload =

0 commit comments

Comments
 (0)