Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ http.createServer((req, res) => {

* **fileSize** - _integer_ - For multipart forms, the max file size (in bytes). **Default:** `Infinity`.

* **totalSize** - _integer_ - For multipart forms, the max total size (in bytes) of all file data combined. **Default:** `Infinity`.

* **files** - _integer_ - For multipart forms, the max number of file fields. **Default:** `Infinity`.

* **parts** - _integer_ - For multipart forms, the max number of parts (fields + files). **Default:** `Infinity`.
Expand All @@ -178,6 +180,8 @@ This function can throw exceptions if there is something wrong with the values i
However, if you aren't accepting files, you can either simply not listen for the `'file'` event at all or set `limits.files` to `0`, and any/all files will be automatically skipped (these skipped files will still count towards any configured `limits.files` and `limits.parts` limits though).

**Note:** If a configured `limits.fileSize` limit was reached for a file, `stream` will both have a boolean property `truncated` set to `true` (best checked at the end of the stream) and emit a `'limit'` event to notify you when this happens.

Similarly, if a configured `limits.totalSize` limit (total size of all files) was reached, the affected file `stream` will have `truncated` set to `true` and emit a `'totalSizeLimit'` event. Once this limit is reached, all subsequent file uploads will be automatically skipped.

* **field**(< _string_ >name, < _string_ >value, < _object_ >info) - Emitted for each new non-file field found. `name` contains the form field name. `value` contains the string value of the field. `info` contains the following properties:

Expand All @@ -194,3 +198,5 @@ This function can throw exceptions if there is something wrong with the values i
* **filesLimit**() - Emitted when the configured `limits.files` limit has been reached. No more `'file'` events will be emitted.

* **fieldsLimit**() - Emitted when the configured `limits.fields` limit has been reached. No more `'field'` events will be emitted.

* **totalSizeLimit**() - Emitted when the configured `limits.totalSize` limit has been reached. No more file data will be processed, and any subsequent file uploads will be automatically skipped. This event is emitted both on the affected file stream and on the busboy parser instance.
31 changes: 28 additions & 3 deletions lib/types/multipart.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,9 @@ class Multipart extends Writable {
const fileSizeLimit = (limits && typeof limits.fileSize === 'number'
? limits.fileSize
: Infinity);
const totalSizeLimit = (limits && typeof limits.totalSize === 'number'
? limits.totalSize
: Infinity);
const filesLimit = (limits && typeof limits.files === 'number'
? limits.files
: Infinity);
Expand All @@ -273,6 +276,7 @@ class Multipart extends Writable {
this._fileStream = undefined;
this._complete = false;
let fileSize = 0;
let totalFilesSize = 0;

let field;
let fieldSize = 0;
Expand All @@ -284,6 +288,7 @@ class Multipart extends Writable {

let hitFilesLimit = false;
let hitFieldsLimit = false;
let hitTotalSizeLimit = false;

this._hparser = null;
const hparser = new HeaderParser((header) => {
Expand Down Expand Up @@ -345,6 +350,12 @@ class Multipart extends Writable {
skipPart = true;
return;
}

if (hitTotalSizeLimit) {
skipPart = true;
return;
}

++files;

if (this.listenerCount('file') === 0) {
Expand Down Expand Up @@ -464,7 +475,11 @@ retrydata:
if (!skipPart) {
if (this._fileStream) {
let chunk;
const actualLen = Math.min(end - start, fileSizeLimit - fileSize);
const actualLen = Math.min(
end - start,
fileSizeLimit - fileSize,
totalSizeLimit - totalFilesSize
);
if (!isDataSafe) {
chunk = Buffer.allocUnsafe(actualLen);
data.copy(chunk, 0, start, start + actualLen);
Expand All @@ -473,12 +488,22 @@ retrydata:
}

fileSize += chunk.length;
if (fileSize === fileSizeLimit) {
totalFilesSize += chunk.length;

if (fileSize === fileSizeLimit || totalFilesSize === totalSizeLimit) {
if (chunk.length > 0)
this._fileStream.push(chunk);
this._fileStream.emit('limit');
this._fileStream.truncated = true;
skipPart = true;
if (totalFilesSize === totalSizeLimit) {
if (!hitTotalSizeLimit) {
hitTotalSizeLimit = true;
this.emit('totalSizeLimit');
this._fileStream.emit('totalSizeLimit');
}
} else {
this._fileStream.emit('limit');
}
} else if (!this._fileStream.push(chunk)) {
if (this._writecb)
this._fileStream._readcb = this._writecb;
Expand Down
Loading