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
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