multiparty
Advanced tools
Comparing version 2.1.9 to 2.2.0
@@ -0,1 +1,11 @@ | ||
### 2.2.0 | ||
* additional callback API to support multiple files with same field name | ||
* fix assertion crash when max field count is exceeded | ||
* fix assertion crash when client aborts an invalid request | ||
* (>=v0.10 only) unpipe the request when an error occurs to save resources. | ||
* update readable-stream to ~1.1.9 | ||
* fix assertion crash when EMFILE occurrs | ||
* (no more assertions - only 'error' events) | ||
### 2.1.9 | ||
@@ -2,0 +12,0 @@ |
@@ -37,3 +37,3 @@ var http = require('http') | ||
res.send('File uploaded successfully'); | ||
} | ||
}); | ||
@@ -40,0 +40,0 @@ server.listen(PORT, function() { |
114
index.js
@@ -9,3 +9,2 @@ exports.Form = Form; | ||
, os = require('os') | ||
, assert = require('assert') | ||
, StringDecoder = require('string_decoder').StringDecoder | ||
@@ -89,15 +88,11 @@ , StreamCounter = require('stream-counter') | ||
req.on('error', function(err) { | ||
error(self, err); | ||
}); | ||
req.on('aborted', function() { | ||
self.emit('aborted'); | ||
error(self, new Error("Request aborted")); | ||
}); | ||
self.handleError = handleError; | ||
self.bytesExpected = getBytesExpected(req.headers); | ||
req.on('error', handleError); | ||
req.on('aborted', onReqAborted); | ||
var contentType = req.headers['content-type']; | ||
if (!contentType) { | ||
error(self, new Error('missing content-type header')); | ||
handleError(new Error('missing content-type header')); | ||
return; | ||
@@ -108,3 +103,3 @@ } | ||
if (!m) { | ||
error(self, new Error('unrecognized content-type: ' + contentType)); | ||
handleError(new Error('unrecognized content-type: ' + contentType)); | ||
return; | ||
@@ -117,4 +112,6 @@ } | ||
if (cb) { | ||
var fields = {} | ||
, files = {}; | ||
var fieldsTable = {}; | ||
var filesTable = {}; | ||
var fieldsList = []; | ||
var filesList = []; | ||
self.on('error', function(err) { | ||
@@ -124,11 +121,45 @@ cb(err); | ||
self.on('field', function(name, value) { | ||
fields[name] = value; | ||
fieldsTable[name] = value; | ||
fieldsList.push({name: name, value: value}); | ||
}); | ||
self.on('file', function(name, file) { | ||
files[name] = file; | ||
filesTable[name] = file; | ||
filesList.push(file); | ||
}); | ||
self.on('close', function() { | ||
cb(null, fields, files); | ||
cb(null, fieldsTable, filesTable, fieldsList, filesList); | ||
}); | ||
} | ||
function onReqAborted() { | ||
self.emit('aborted'); | ||
handleError(new Error("Request aborted")); | ||
} | ||
function handleError(err) { | ||
var first = !self.error; | ||
if (first) { | ||
self.error = err; | ||
req.removeListener('aborted', onReqAborted); | ||
// welp. 0.8 doesn't support unpipe, too bad so sad. | ||
// let's drop support for 0.8 soon. | ||
if (req.unpipe) { | ||
req.unpipe(self); | ||
} | ||
} | ||
self.openedFiles.forEach(function(file) { | ||
file.ws.destroy(); | ||
fs.unlink(file.path, function(err) { | ||
// this is already an error condition, ignore 2nd error | ||
}); | ||
}); | ||
self.openedFiles = []; | ||
if (first) { | ||
self.emit('error', err); | ||
} | ||
} | ||
}; | ||
@@ -161,7 +192,7 @@ | ||
if (index === boundaryLength - 2) { | ||
if (c !== CR) return error(self, new Error("Expected CR Received " + c)); | ||
if (c !== CR) return self.handleError(new Error("Expected CR Received " + c)); | ||
index++; | ||
break; | ||
} else if (index === boundaryLength - 1) { | ||
if (c !== LF) return error(self, new Error("Expected LF Received " + c)); | ||
if (c !== LF) return self.handleError(new Error("Expected LF Received " + c)); | ||
index = 0; | ||
@@ -194,3 +225,3 @@ self.onParsePartBegin(); | ||
// empty header field | ||
error(self, new Error("Empty header field")); | ||
self.handleError(new Error("Empty header field")); | ||
return; | ||
@@ -206,3 +237,3 @@ } | ||
if (cl < A || cl > Z) { | ||
error(self, new Error("Expected alphabetic character, received " + c)); | ||
self.handleError(new Error("Expected alphabetic character, received " + c)); | ||
return; | ||
@@ -226,9 +257,9 @@ } | ||
case HEADER_VALUE_ALMOST_DONE: | ||
if (c !== LF) return error(self, new Error("Expected LF Received " + c)); | ||
if (c !== LF) return self.handleError(new Error("Expected LF Received " + c)); | ||
state = HEADER_FIELD_START; | ||
break; | ||
case HEADERS_ALMOST_DONE: | ||
if (c !== LF) return error(self, new Error("Expected LF Received " + c)); | ||
if (c !== LF) return self.handleError(new Error("Expected LF Received " + c)); | ||
var err = self.onParseHeadersEnd(i + 1); | ||
if (err) return error(self, err); | ||
if (err) return self.handleError(err); | ||
state = PART_DATA_START; | ||
@@ -317,3 +348,3 @@ break; | ||
default: | ||
error(self, new Error("Parser has invalid state.")); | ||
self.handleError(new Error("Parser has invalid state.")); | ||
return; | ||
@@ -414,2 +445,7 @@ } | ||
self.totalFieldCount += 1; | ||
if (self.totalFieldCount >= self.maxFields) { | ||
return new Error("maxFields " + self.maxFields + " exceeded."); | ||
} | ||
self.destStream = new stream.PassThrough(); | ||
@@ -428,7 +464,2 @@ self.destStream.on('drain', function() { | ||
self.boundary.length - LAST_BOUNDARY_SUFFIX_LEN); | ||
self.totalFieldCount += 1; | ||
if (self.totalFieldCount >= self.maxFields) { | ||
error(self, new Error("maxFields " + self.maxFields + " exceeded.")); | ||
return; | ||
} | ||
@@ -462,14 +493,2 @@ self.emit('part', self.destStream); | ||
function error(self, err) { | ||
assert.ok(!self.error, err.stack); | ||
self.error = err; | ||
self.emit('error', err); | ||
self.openedFiles.forEach(function(file) { | ||
file.ws.destroy(); | ||
fs.unlink(file.path, function(err) { | ||
// this is already an error condition, ignore 2nd error | ||
}); | ||
}); | ||
} | ||
function beginFlush(self) { | ||
@@ -493,2 +512,3 @@ self.flushing += 1; | ||
var file = { | ||
fieldName: fileStream.name, | ||
originalFilename: fileStream.filename, | ||
@@ -516,3 +536,3 @@ path: uploadPath(self.uploadDir, fileStream.filename), | ||
file.ws.on('error', function(err) { | ||
error(self, err); | ||
if (!self.error) self.handleError(err); | ||
}); | ||
@@ -528,4 +548,4 @@ file.ws.on('close', function() { | ||
function handleField(self, fieldStream) { | ||
var value = '' | ||
, decoder = new StringDecoder(self.encoding); | ||
var value = ''; | ||
var decoder = new StringDecoder(self.encoding); | ||
@@ -535,5 +555,7 @@ beginFlush(self); | ||
var buffer = fieldStream.read(); | ||
if (!buffer) return; | ||
self.totalFieldSize += buffer.length; | ||
if (self.totalFieldSize > self.maxFieldsSize) { | ||
error(self, new Error("maxFieldsSize " + self.maxFieldsSize + " exceeded")); | ||
self.handleError(new Error("maxFieldsSize " + self.maxFieldsSize + " exceeded")); | ||
return; | ||
@@ -584,3 +606,3 @@ } | ||
} else if (self.state !== END) { | ||
error(self, new Error('stream ended unexpectedly')); | ||
self.handleError(new Error('stream ended unexpectedly')); | ||
} | ||
@@ -587,0 +609,0 @@ self.finished = true; |
{ | ||
"name": "multiparty", | ||
"version": "2.1.9", | ||
"version": "2.2.0", | ||
"description": "multipart/form-data parser which supports streaming", | ||
@@ -25,3 +25,3 @@ "repository": { | ||
"scripts": { | ||
"test": "mocha --reporter spec --recursive test/test.js" | ||
"test": "ulimit -n 500 && mocha --timeout 4000 --reporter spec --recursive test/test.js" | ||
}, | ||
@@ -33,5 +33,5 @@ "engines": { | ||
"dependencies": { | ||
"readable-stream": "~1.0.2", | ||
"stream-counter": "~0.1.0" | ||
"readable-stream": "~1.1.9", | ||
"stream-counter": "~0.2.0" | ||
} | ||
} |
@@ -6,2 +6,6 @@ [![Build Status](https://travis-ci.org/superjoe30/node-multiparty.png?branch=master)](https://travis-ci.org/superjoe30/node-multiparty) | ||
See also [busboy](https://github.com/mscdex/busboy) - a | ||
[faster](https://github.com/mscdex/dicer/wiki/Benchmarks) alternative | ||
which may be worth looking into. | ||
### Why the fork? | ||
@@ -28,3 +32,2 @@ | ||
* See [examples](examples). | ||
* Using express or connect? See [connect-multiparty](https://github.com/superjoe30/connect-multiparty) | ||
@@ -97,3 +100,3 @@ Parse an incoming `multipart/form-data` request. | ||
```js | ||
form.parse(req, function(err, fields, files) { | ||
form.parse(req, function(err, fieldsObject, filesObject, fieldsList, filesList) { | ||
// ... | ||
@@ -103,2 +106,12 @@ }); | ||
It is often convenient to access a field or file by name. In this situation, | ||
use `fieldsObject` or `filesObject`. However sometimes, as in the case of a | ||
`<input type="file" multiple="multiple">` the multipart stream will contain | ||
multiple files of the same input name, and you are interested in all of them. | ||
In this case, use `filesList`. | ||
Another example is when you do not care what the field name of a file is; you | ||
are merely interested in a single upload. In this case, set `maxFields` to 1 | ||
(assuming no other fields expected besides the file) and use `filesList[0]`. | ||
#### form.bytesReceived | ||
@@ -155,2 +168,3 @@ | ||
* `file` - an object with these properties: | ||
- `fieldName` - same as `name` - the field name for this file | ||
- `originalFilename` - the filename that the user reports for the file | ||
@@ -157,0 +171,0 @@ - `path` - the absolute path of the uploaded file on disk |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3572670
57
1475
178
20
+ Addedreadable-stream@1.1.14(transitive)
+ Addedstream-counter@0.2.0(transitive)
- Removedreadable-stream@1.0.34(transitive)
- Removedstream-counter@0.1.0(transitive)
Updatedreadable-stream@~1.1.9
Updatedstream-counter@~0.2.0