[Pkg-javascript-commits] [node-multiparty] 04/13: Imported Upstream version 3.3.0
Andrew Kelley
andrewrk-guest at moszumanska.debian.org
Sat Jul 5 00:49:17 UTC 2014
This is an automated email from the git hooks/post-receive script.
andrewrk-guest pushed a commit to branch master
in repository node-multiparty.
commit 92d60f58e6f28047d15d2fa2af562910eddbf136
Author: Andrew Kelley <superjoe30 at gmail.com>
Date: Fri Jul 4 22:32:46 2014 +0000
Imported Upstream version 3.3.0
---
.npmignore | 2 +
CHANGELOG.md | 78 ++++++++
README.md | 97 ++++++++--
index.js | 211 +++++++++++++++------
package.json | 16 +-
test/fixture/file/blank.gif | Bin
.../charset-last.http} | 4 +-
.../plain.txt.http => content-type/charset.http} | 4 +-
.../custom-equal-sign.http} | 10 +-
.../plain.txt.http => content-type/custom.http} | 4 +-
test/fixture/http/encoding/plain.txt.http | 2 +-
test/fixture/http/no-filename/empty.http | 6 +
test/fixture/http/no-filename/filename-name.http | 2 +-
test/fixture/http/no-filename/generic.http | 2 +-
test/fixture/js/content-type.js | 44 +++++
test/fixture/js/no-filename.js | 3 +
test/standalone/test-chunked.js | 43 +++++
test/standalone/test-epilogue-last-chunk.js | 44 +++++
test/standalone/test-error-listen-after-parse.js | 20 ++
test/standalone/test-error-unpipe.js | 32 ++++
test/standalone/test-issue-15.js | 16 +-
test/standalone/test-issue-21.js | 39 +++-
test/standalone/test-issue-32.js | 38 ++++
.../{test-issue-5.js => test-issue-36.js} | 22 ++-
test/standalone/test-issue-4.js | 2 +
test/standalone/test-issue-5.js | 3 +-
test/standalone/test-max-fields.js | 52 +++++
...est-issue-4.js => test-max-files-size-exact.js} | 37 ++--
.../{test-issue-4.js => test-max-files-size.js} | 41 ++--
test/standalone/test-parse-type-error.js | 39 ++++
test/standalone/test-req-encoding.js | 39 ++++
test/test.js | 150 ++++++++++-----
32 files changed, 874 insertions(+), 228 deletions(-)
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..d457948
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,2 @@
+test/
+examples/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ea54d9a..5663f4a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,81 @@
+### 3.3.0
+
+ * Douglas Christopher Wilson (4):
+ - Expand form.parse in README
+ - Always emit close after all parts ended
+ - Remove execute bit from files
+ - Fix callback hang in node.js 0.8 on errors
+
+ * Andrew Kelley (1):
+ - tests refactor
+
+ * Thanasis Polychronakis (1):
+ - docs: fix code error in readme
+
+### 3.2.9
+
+ * Fix attaching error listeners directly after form.parse
+ * Fix to not synchronously invoke callback to form.parse on error
+
+### 3.2.8
+
+ * Fix developer accidentally corrupting data
+ * Fix handling epilogue in a separate chunk
+ * Fix initial check errors to use supplied callback
+
+### 3.2.7
+
+ * Fix errors hanging responses in callback-style
+
+### 3.2.6
+
+ * Fix maxFields to error on field after max
+
+### 3.2.5
+
+ * Support boundary containing equal sign (thanks [garel-a])
+
+### 3.2.4
+
+ * Keep part.byteCount undefined in chunked encoding (thanks [dougwilson])
+ * Fix temp files not always cleaned up (thanks [dougwilson])
+
+### 3.2.3
+
+ * improve parsing boundary attribute from Content-Type (thanks [dougwilson])
+
+### 3.2.2
+
+ * fix error on empty payloads (thanks [dougwilson])
+
+### 3.2.1
+
+ * fix maxFilesSize overcalculation bug (thanks [dougwilson] and
+ [timothysoehnlin])
+
+### 3.2.0
+
+ * add maxFilesSize for autoFiles (thanks [dougwilson])
+
+### 3.1.2
+
+ * exclude test files from npm package (thanks Dag Einar Monsen)
+ * fix incorrectly using autoFields value for autoFiles (thanks RG72)
+
+### 3.1.1
+
+ * fix not emitting 'close' after all part 'end' events
+
+### 3.1.0
+
+ * support UTF8 filename in Content-Disposition (thanks baoshan)
+
+### 3.0.0
+
+ * form.parse callback API changed in a compatibility-breaking manner.
+ sorry, I know it sucks but the way I had it before is misleading and
+ inconsistent.
+
### 2.2.0
* additional callback API to support multiple files with same field name
diff --git a/README.md b/README.md
index f149ac0..120e5c4 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
-[![Build Status](https://travis-ci.org/superjoe30/node-multiparty.png?branch=master)](https://travis-ci.org/superjoe30/node-multiparty)
-# multiparty
+# multiparty [![Build Status](https://travis-ci.org/andrewrk/node-multiparty.svg?branch=master)](https://travis-ci.org/andrewrk/node-multiparty) [![NPM version](https://badge.fury.io/js/multiparty.svg)](http://badge.fury.io/js/multiparty)
Parse http requests with content-type `multipart/form-data`, also known as file uploads.
@@ -71,12 +70,15 @@ var form = new multiparty.Form(options)
Creates a new form. Options:
* `encoding` - sets encoding for the incoming form fields. Defaults to `utf8`.
- * `maxFieldSize` - Limits the amount of memory a field (not a file) can
+ * `maxFieldsSize` - Limits the amount of memory a field (not a file) can
allocate in bytes. If this value is exceeded, an `error` event is emitted.
The default size is 2MB.
* `maxFields` - Limits the number of fields that will be parsed before
emitting an `error` event. A file counts as a field in this case.
Defaults to 1000.
+ * `maxFilesSize` - Only relevant when `autoFiles` is `true`. Limits the
+ total bytes accepted for all files combined. If this value is exceeded,
+ an `error` event is emitted. The default is `Infinity`.
* `autoFields` - Enables `field` events. This is automatically set to `true`
if you add a `field` listener.
* `autoFiles` - Enables `file` events. This is automatically set to `true`
@@ -90,25 +92,78 @@ Creates a new form. Options:
#### form.parse(request, [cb])
-Parses an incoming node.js `request` containing form data. If `cb` is
-provided, `autoFields` and `autoFiles` are set to `true` and all fields and
-files are collected and passed to the callback:
+Parses an incoming node.js `request` containing form data.This will cause
+`form` to emit events based off the incoming request.
```js
-form.parse(req, function(err, fieldsObject, filesObject, fieldsList, filesList) {
- // ...
+var count = 0;
+var form = new multiparty.Form();
+
+// Errors may be emitted
+form.on('error', function(err) {
+ console.log('Error parsing form: ' + err.stack);
+});
+
+// Parts are emitted when parsing the form
+form.on('part', function(part) {
+ // You *must* act on the part by reading it
+ // NOTE: if you want to ignore it, just call "part.resume()"
+
+ if (part.filename === null) {
+ // filename is "null" when this is a field and not a file
+ console.log('got field named ' + part.name);
+ // ignore field's content
+ part.resume();
+ }
+
+ if (part.filename !== null) {
+ // filename is not "null" when this is a file
+ count++;
+ console.log('got file named ' + part.name);
+ // ignore file's content here
+ part.resume();
+ }
+});
+
+// Close emitted after form parsed
+form.on('close', function() {
+ console.log('Upload completed!');
+ res.setHeader('text/plain');
+ res.end('Received ' + count + ' files');
});
+
+// Parse req
+form.parse(req);
+
```
-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`.
+If `cb` is provided, `autoFields` and `autoFiles` are set to `true` and all
+fields and files are collected and passed to the callback, removing the need to
+listen to any events on `form`. This is for convenience when wanted to read
+everything, but be careful as this will write all uploaded files to the disk,
+even ones you may not be interested in.
-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]`.
+```js
+form.parse(req, function(err, fields, files) {
+ Object.keys(fields).forEach(function(name) {
+ console.log('got field named ' + name);
+ });
+
+ Object.keys(files).forEach(function(name) {
+ console.log('got file named ' + name);
+ });
+
+ console.log('Upload completed!');
+ res.setHeader('text/plain');
+ res.end('Received ' + files.length + ' files');
+});
+```
+
+`fields` is an object where the property names are field names and the values
+are arrays of field values.
+
+`files` is an object where the property names are field names and the values
+are arrays of file objects.
#### form.bytesReceived
@@ -122,8 +177,12 @@ The expected number of bytes in this form.
#### 'error' (err)
-You definitely want to handle this event. If not your server *will* crash when
-users submit bogus multipart requests!
+Unless you supply a callback to `form.parse`, you definitely want to handle
+this event. Otherwise your server *will* crash when users submit bogus
+multipart requests!
+
+Only one 'error' event can ever be emitted, and if an 'error' event is
+emitted, then 'close' will not be emitted.
#### 'part' (part)
@@ -159,6 +218,8 @@ event is emitted. This is typically when you would send your response.
listener, multiparty automatically sets `form.autoFiles` to `true` and will
stream uploads to disk for you.
+**The max bytes accepted per request can be specified with `maxFilesSize`.**
+
* `name` - the field name for this file
* `file` - an object with these properties:
- `fieldName` - same as `name` - the field name for this file
diff --git a/index.js b/index.js
old mode 100755
new mode 100644
index 71c88ae..890dd37
--- a/index.js
+++ b/index.js
@@ -20,7 +20,8 @@ var START = 0
, PART_DATA_START = 8
, PART_DATA = 9
, PART_END = 10
- , END = 11
+ , CLOSE_BOUNDARY = 11
+ , END = 12
, LF = 10
, CR = 13
@@ -30,7 +31,8 @@ var START = 0
, A = 97
, Z = 122
-var CONTENT_TYPE_RE = /^multipart\/(form-data|related);\s*boundary=(?:"([^"]+)"|([^;]+))$/i;
+var CONTENT_TYPE_RE = /^multipart\/(?:form-data|related)(?:;|$)/i;
+var CONTENT_TYPE_PARAM_RE = /;\s*([^=]+)=(?:"([^"]+)"|([^;]+))/gi;
var FILE_EXT_RE = /(\.[_\-a-zA-Z0-9]{0,16}).*/;
var LAST_BOUNDARY_SUFFIX_LEN = 4; // --\r\n
@@ -45,10 +47,11 @@ function Form(options) {
self.finished = false;
self.autoFields = !!options.autoFields;
- self.autoFiles = !!options.autoFields;
+ self.autoFiles = !!options.autoFiles;
self.maxFields = options.maxFields || 1000;
self.maxFieldsSize = options.maxFieldsSize || 2 * 1024 * 1024;
+ self.maxFilesSize = options.maxFilesSize || Infinity;
self.uploadDir = options.uploadDir || os.tmpDir();
self.encoding = options.encoding || 'utf8';
self.hash = options.hash || false;
@@ -59,6 +62,7 @@ function Form(options) {
self.openedFiles = [];
self.totalFieldSize = 0;
self.totalFieldCount = 0;
+ self.totalFileSize = 0;
self.flushing = 0;
self.backpressure = false;
@@ -76,79 +80,115 @@ function Form(options) {
}
Form.prototype.parse = function(req, cb) {
+ var called = false;
var self = this;
+ var waitend = true;
- // if the user supplies a callback, this implies autoFields and autoFiles
if (cb) {
+ // if the user supplies a callback, this implies autoFields and autoFiles
self.autoFields = true;
self.autoFiles = true;
+
+ var fields = {};
+ var files = {};
+ self.on('error', function(err) {
+ if (called) return;
+
+ called = true;
+
+ // wait for req events to fire
+ process.nextTick(function() {
+ if (waitend && req.readable) {
+ // dump rest of request
+ req.resume();
+ req.once('end', function() {
+ cb(err);
+ });
+ return;
+ }
+
+ cb(err);
+ });
+ });
+ self.on('field', function(name, value) {
+ var fieldsArray = fields[name] || (fields[name] = []);
+ fieldsArray.push(value);
+ });
+ self.on('file', function(name, file) {
+ var filesArray = files[name] || (files[name] = []);
+ filesArray.push(file);
+ });
+ self.on('close', function() {
+ cb(null, fields, files);
+ });
}
self.handleError = handleError;
self.bytesExpected = getBytesExpected(req.headers);
- req.on('error', handleError);
+ req.on('end', onReqEnd);
+ req.on('error', function(err) {
+ waitend = false;
+ handleError(err);
+ });
req.on('aborted', onReqAborted);
+ var state = req._readableState;
+ if (req._decoder || (state && (state.encoding || state.decoder))) {
+ // this is a binary protocol
+ // if an encoding is set, input is likely corrupted
+ validationError(new Error('request encoding must not be set'));
+ return;
+ }
+
var contentType = req.headers['content-type'];
if (!contentType) {
- handleError(new Error('missing content-type header'));
+ validationError(new Error('missing content-type header'));
return;
}
- var m = contentType.match(CONTENT_TYPE_RE);
+ var m = CONTENT_TYPE_RE.exec(contentType);
if (!m) {
- handleError(new Error('unrecognized content-type: ' + contentType));
+ validationError(new Error('unrecognized content-type: ' + contentType));
return;
}
- var boundary = m[2] || m[3];
- setUpParser(self, boundary);
- req.pipe(self);
- if (cb) {
- var fieldsTable = {};
- var filesTable = {};
- var fieldsList = [];
- var filesList = [];
- self.on('error', function(err) {
- cb(err);
- });
- self.on('field', function(name, value) {
- fieldsTable[name] = value;
- fieldsList.push({name: name, value: value});
- });
- self.on('file', function(name, file) {
- filesTable[name] = file;
- filesList.push(file);
- });
- self.on('close', function() {
- cb(null, fieldsTable, filesTable, fieldsList, filesList);
- });
+ var boundary;
+ CONTENT_TYPE_PARAM_RE.lastIndex = m.index + m[0].length - 1;
+ while ((m = CONTENT_TYPE_PARAM_RE.exec(contentType))) {
+ if (m[1].toLowerCase() !== 'boundary') continue;
+ boundary = m[2] || m[3];
+ break;
+ }
+
+ if (!boundary) {
+ validationError(new Error('content-type missing boundary: ' + require('util').inspect(m)));
+ return;
}
+ setUpParser(self, boundary);
+ req.pipe(self);
+
function onReqAborted() {
+ waitend = false;
self.emit('aborted');
handleError(new Error("Request aborted"));
}
+ function onReqEnd() {
+ waitend = false;
+ }
+
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);
- }
+ req.removeListener('end', onReqEnd);
}
self.openedFiles.forEach(function(file) {
- file.ws.destroy();
- fs.unlink(file.path, function(err) {
- // this is already an error condition, ignore 2nd error
- });
+ destroyFile(self, file);
});
self.openedFiles = [];
@@ -157,9 +197,15 @@ Form.prototype.parse = function(req, cb) {
}
}
+ function validationError(err) {
+ // handle error on next tick for event listeners to attach
+ process.nextTick(handleError.bind(null, err))
+ }
};
Form.prototype._write = function(buffer, encoding, cb) {
+ if (this.error) return;
+
var self = this
, i = 0
, len = buffer.length
@@ -183,7 +229,11 @@ Form.prototype._write = function(buffer, encoding, cb) {
state = START_BOUNDARY;
/* falls through */
case START_BOUNDARY:
- if (index === boundaryLength - 2) {
+ if (index === boundaryLength - 2 && c === HYPHEN) {
+ index = 1;
+ state = CLOSE_BOUNDARY;
+ break;
+ } else if (index === boundaryLength - 2) {
if (c !== CR) return self.handleError(new Error("Expected CR Received " + c));
index++;
break;
@@ -288,8 +338,9 @@ Form.prototype._write = function(buffer, encoding, cb) {
// CR = part boundary
self.partBoundaryFlag = true;
} else if (c === HYPHEN) {
- // HYPHEN = end boundary
- self.lastBoundaryFlag = true;
+ index = 1;
+ state = CLOSE_BOUNDARY;
+ break;
} else {
index = 0;
}
@@ -303,14 +354,6 @@ Form.prototype._write = function(buffer, encoding, cb) {
state = HEADER_FIELD_START;
break;
}
- } else if (self.lastBoundaryFlag) {
- if (c === HYPHEN) {
- self.onParsePartEnd();
- self.end();
- state = END;
- } else {
- index = 0;
- }
} else {
index = 0;
}
@@ -333,6 +376,16 @@ Form.prototype._write = function(buffer, encoding, cb) {
}
break;
+ case CLOSE_BOUNDARY:
+ if (c !== HYPHEN) return self.handleError(new Error("Expected HYPHEN Received " + c));
+ if (index === 1) {
+ self.onParsePartEnd();
+ state = END;
+ } else if (index > 1) {
+ return self.handleError(new Error("Parser has invalid state."));
+ }
+ index++;
+ break;
case END:
break;
default:
@@ -433,7 +486,7 @@ Form.prototype.onParseHeadersEnd = function(offset) {
}
self.totalFieldCount += 1;
- if (self.totalFieldCount >= self.maxFields) {
+ if (self.totalFieldCount > self.maxFields) {
return new Error("maxFields " + self.maxFields + " exceeded.");
}
@@ -446,16 +499,21 @@ Form.prototype.onParseHeadersEnd = function(offset) {
self.destStream.filename = self.partFilename;
self.destStream.byteOffset = self.bytesReceived + offset;
var partContentLength = self.destStream.headers['content-length'];
- self.destStream.byteCount = partContentLength ?
- parseInt(partContentLength, 10) :
- (self.bytesExpected - self.destStream.byteOffset -
- self.boundary.length - LAST_BOUNDARY_SUFFIX_LEN);
+ self.destStream.byteCount = partContentLength ? parseInt(partContentLength, 10) :
+ self.bytesExpected ? (self.bytesExpected - self.destStream.byteOffset -
+ self.boundary.length - LAST_BOUNDARY_SUFFIX_LEN) :
+ undefined;
self.emit('part', self.destStream);
if (self.destStream.filename == null && self.autoFields) {
handleField(self, self.destStream);
} else if (self.destStream.filename != null && self.autoFiles) {
handleFile(self, self.destStream);
+ } else {
+ beginFlush(self);
+ self.destStream.on('end', function(){
+ endFlush(self);
+ });
}
}
@@ -493,18 +551,31 @@ function maybeClose(self) {
}
}
+function destroyFile(self, file) {
+ if (!file.ws) return;
+ file.ws.removeAllListeners('close');
+ file.ws.on('close', function() {
+ fs.unlink(file.path, function(err) {
+ if (err && !self.error) self.handleError(err);
+ });
+ });
+ file.ws.destroy();
+}
+
function handleFile(self, fileStream) {
- beginFlush(self);
+ if (self.error) return;
var file = {
fieldName: fileStream.name,
originalFilename: fileStream.filename,
path: uploadPath(self.uploadDir, fileStream.filename),
headers: fileStream.headers,
};
+ beginFlush(self); // flush to write stream
file.ws = fs.createWriteStream(file.path);
self.openedFiles.push(file);
fileStream.pipe(file.ws);
var counter = new StreamCounter();
+ var seenBytes = 0;
fileStream.pipe(counter);
var hashWorkaroundStream
, hash = null;
@@ -518,6 +589,17 @@ function handleFile(self, fileStream) {
};
fileStream.pipe(hashWorkaroundStream);
}
+ counter.on('progress', function() {
+ var deltaBytes = counter.bytes - seenBytes;
+ seenBytes += deltaBytes;
+ self.totalFileSize += deltaBytes;
+ if (self.totalFileSize > self.maxFilesSize) {
+ if (hashWorkaroundStream) fileStream.unpipe(hashWorkaroundStream);
+ fileStream.unpipe(counter);
+ fileStream.unpipe(file.ws);
+ self.handleError(new Error("maxFilesSize " + self.maxFilesSize + " exceeded"));
+ }
+ });
file.ws.on('error', function(err) {
if (!self.error) self.handleError(err);
});
@@ -527,6 +609,10 @@ function handleFile(self, fileStream) {
self.emit('file', fileStream.name, file);
endFlush(self);
});
+ beginFlush(self); // flush from file stream
+ fileStream.on('end', function(){
+ endFlush(self);
+ });
}
function handleField(self, fieldStream) {
@@ -578,7 +664,6 @@ function setUpParser(self, boundary) {
self.index = null;
self.partBoundaryFlag = false;
- self.lastBoundaryFlag = false;
self.on('finish', function() {
if ((self.state === HEADER_FIELD_START && self.index === 0) ||
@@ -602,7 +687,15 @@ function uploadPath(baseDir, filename) {
function parseFilename(headerValue) {
var m = headerValue.match(/\bfilename="(.*?)"($|; )/i);
- if (!m) return;
+ if (!m) {
+ m = headerValue.match(/\bfilename\*=utf-8\'\'(.*?)($|; )/i);
+ if (m) {
+ m[1] = decodeURI(m[1]);
+ }
+ else {
+ return;
+ }
+ }
var filename = m[1].substr(m[1].lastIndexOf('\\') + 1);
filename = filename.replace(/%22/g, '"');
diff --git a/package.json b/package.json
index 65e5757..260ae68 100644
--- a/package.json
+++ b/package.json
@@ -1,10 +1,10 @@
{
"name": "multiparty",
- "version": "2.2.0",
+ "version": "3.3.0",
"description": "multipart/form-data parser which supports streaming",
"repository": {
"type": "git",
- "url": "git at github.com:superjoe30/node-multiparty.git"
+ "url": "git at github.com:andrewrk/node-multiparty.git"
},
"keywords": [
"file",
@@ -14,15 +14,15 @@
"s3"
],
"devDependencies": {
- "findit": "0.1.1",
- "hashish": "0.0.4",
- "mocha": "~1.8.2",
+ "findit": "~2.0.0",
+ "mkdirp": "~0.5.0",
+ "pend": "~1.1.1",
"request": "~2.16.6",
- "mkdirp": "~0.3.5",
- "superagent": "~0.14.1"
+ "rimraf": "~2.2.8",
+ "superagent": "~0.18.0"
},
"scripts": {
- "test": "ulimit -n 500 && mocha --timeout 4000 --reporter spec --recursive test/test.js"
+ "test": "ulimit -n 500 && node test/test.js"
},
"engines": {
"node": ">=0.8.0"
diff --git a/test/fixture/file/blank.gif b/test/fixture/file/blank.gif
old mode 100755
new mode 100644
diff --git a/test/fixture/http/encoding/plain.txt.http b/test/fixture/http/content-type/charset-last.http
similarity index 85%
copy from test/fixture/http/encoding/plain.txt.http
copy to test/fixture/http/content-type/charset-last.http
index 5e85ad6..8ad9dd4 100644
--- a/test/fixture/http/encoding/plain.txt.http
+++ b/test/fixture/http/content-type/charset-last.http
@@ -1,7 +1,7 @@
POST /upload HTTP/1.1
Host: localhost:8080
-Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
-Content-Length: 221
+Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ; charset=utf-8
+Content-Length: 213
------TLV0SrKD4z1TRxRhAPUvZ
Content-Disposition: form-data; name="file"; filename="plain.txt"
diff --git a/test/fixture/http/encoding/plain.txt.http b/test/fixture/http/content-type/charset.http
similarity index 68%
copy from test/fixture/http/encoding/plain.txt.http
copy to test/fixture/http/content-type/charset.http
index 5e85ad6..18ebe4b 100644
--- a/test/fixture/http/encoding/plain.txt.http
+++ b/test/fixture/http/content-type/charset.http
@@ -1,7 +1,7 @@
POST /upload HTTP/1.1
Host: localhost:8080
-Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
-Content-Length: 221
+Content-Type: multipart/form-data; charset=utf-8; boundary=----TLV0SrKD4z1TRxRhAPUvZ
+Content-Length: 213
------TLV0SrKD4z1TRxRhAPUvZ
Content-Disposition: form-data; name="file"; filename="plain.txt"
diff --git a/test/fixture/http/encoding/plain.txt.http b/test/fixture/http/content-type/custom-equal-sign.http
similarity index 54%
copy from test/fixture/http/encoding/plain.txt.http
copy to test/fixture/http/content-type/custom-equal-sign.http
index 5e85ad6..f97c2e5 100644
--- a/test/fixture/http/encoding/plain.txt.http
+++ b/test/fixture/http/content-type/custom-equal-sign.http
@@ -1,13 +1,13 @@
POST /upload HTTP/1.1
Host: localhost:8080
-Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
-Content-Length: 221
+Content-Type: multipart/form-data; boundary=----=test; charset=utf-8
+Content-Length: 182
-------TLV0SrKD4z1TRxRhAPUvZ
+------=test
Content-Disposition: form-data; name="file"; filename="plain.txt"
Content-Type: text/plain
Content-Transfer-Encoding: 7bit
-I am a plain text file
+I am a plain text file
-------TLV0SrKD4z1TRxRhAPUvZ--
+------=test--
diff --git a/test/fixture/http/encoding/plain.txt.http b/test/fixture/http/content-type/custom.http
similarity index 68%
copy from test/fixture/http/encoding/plain.txt.http
copy to test/fixture/http/content-type/custom.http
index 5e85ad6..0a046f1 100644
--- a/test/fixture/http/encoding/plain.txt.http
+++ b/test/fixture/http/content-type/custom.http
@@ -1,7 +1,7 @@
POST /upload HTTP/1.1
Host: localhost:8080
-Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
-Content-Length: 221
+Content-Type: multipart/form-data; custom=stuff; boundary=----TLV0SrKD4z1TRxRhAPUvZ
+Content-Length: 213
------TLV0SrKD4z1TRxRhAPUvZ
Content-Disposition: form-data; name="file"; filename="plain.txt"
diff --git a/test/fixture/http/encoding/plain.txt.http b/test/fixture/http/encoding/plain.txt.http
index 5e85ad6..a230b3d 100644
--- a/test/fixture/http/encoding/plain.txt.http
+++ b/test/fixture/http/encoding/plain.txt.http
@@ -1,7 +1,7 @@
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
-Content-Length: 221
+Content-Length: 213
------TLV0SrKD4z1TRxRhAPUvZ
Content-Disposition: form-data; name="file"; filename="plain.txt"
diff --git a/test/fixture/http/no-filename/empty.http b/test/fixture/http/no-filename/empty.http
new file mode 100644
index 0000000..6b5448e
--- /dev/null
+++ b/test/fixture/http/no-filename/empty.http
@@ -0,0 +1,6 @@
+POST /upload HTTP/1.1
+Host: localhost:8080
+Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryi3Xz4TKrYpgIdIpf
+Content-Length: 43
+
+------WebKitFormBoundaryi3Xz4TKrYpgIdIpf--
diff --git a/test/fixture/http/no-filename/filename-name.http b/test/fixture/http/no-filename/filename-name.http
index 43672a3..65261bd 100644
--- a/test/fixture/http/no-filename/filename-name.http
+++ b/test/fixture/http/no-filename/filename-name.http
@@ -1,7 +1,7 @@
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarytyE4wkKlZ5CQJVTG
-Content-Length: 1000
+Content-Length: 209
------WebKitFormBoundarytyE4wkKlZ5CQJVTG
Content-Disposition: form-data; filename="plain.txt"; name="upload"
diff --git a/test/fixture/http/no-filename/generic.http b/test/fixture/http/no-filename/generic.http
index e0dee27..a3f22b4 100644
--- a/test/fixture/http/no-filename/generic.http
+++ b/test/fixture/http/no-filename/generic.http
@@ -1,7 +1,7 @@
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarytyE4wkKlZ5CQJVTG
-Content-Length: 1000
+Content-Length: 200
------WebKitFormBoundarytyE4wkKlZ5CQJVTG
Content-Disposition: form-data; name="upload"; filename=""
diff --git a/test/fixture/js/content-type.js b/test/fixture/js/content-type.js
new file mode 100644
index 0000000..4a23899
--- /dev/null
+++ b/test/fixture/js/content-type.js
@@ -0,0 +1,44 @@
+module.exports['charset.http'] = [
+ {
+ type: 'file',
+ name: 'file',
+ filename: 'plain.txt',
+ fixture: 'plain.txt',
+ sha1: 'b31d07bac24ac32734de88b3687dddb10e976872',
+ size: 23,
+ }
+];
+
+module.exports['charset-last.http'] = [
+ {
+ type: 'file',
+ name: 'file',
+ filename: 'plain.txt',
+ fixture: 'plain.txt',
+ sha1: 'b31d07bac24ac32734de88b3687dddb10e976872',
+ size: 23,
+ }
+];
+
+module.exports['custom.http'] = [
+ {
+ type: 'file',
+ name: 'file',
+ filename: 'plain.txt',
+ fixture: 'plain.txt',
+ sha1: 'b31d07bac24ac32734de88b3687dddb10e976872',
+ size: 23,
+ }
+];
+
+
+// to test the regexp modification : it should accepts equal sign in boundary
+module.exports['custom-equal-sign.http'] = [
+ {
+ type: 'file',
+ name: 'file',
+ filename: 'plain.txt',
+ fixture: 'plain.txt',
+ size: 24,
+ }
+];
\ No newline at end of file
diff --git a/test/fixture/js/no-filename.js b/test/fixture/js/no-filename.js
index f03b4f0..556710b 100644
--- a/test/fixture/js/no-filename.js
+++ b/test/fixture/js/no-filename.js
@@ -1,3 +1,6 @@
+module.exports['empty.http'] = [
+];
+
module.exports['generic.http'] = [
{type: 'file', name: 'upload', filename: '', fixture: 'plain.txt',
sha1: 'b31d07bac24ac32734de88b3687dddb10e976872'},
diff --git a/test/standalone/test-chunked.js b/test/standalone/test-chunked.js
new file mode 100644
index 0000000..b2eda3f
--- /dev/null
+++ b/test/standalone/test-chunked.js
@@ -0,0 +1,43 @@
+var multiparty = require('../../')
+ , assert = require('assert')
+ , http = require('http')
+ , net = require('net');
+
+var server = http.createServer(function(req, resp) {
+ var form = new multiparty.Form();
+
+ var partCount = 0;
+ form.on('part', function(part) {
+ part.resume();
+ partCount++;
+ assert.strictEqual(typeof part.byteCount, 'undefined');
+ });
+ form.on('close', function() {
+ assert.strictEqual(partCount, 1);
+ resp.end();
+ });
+
+ form.parse(req);
+});
+server.listen(function() {
+ var socket = net.connect(server.address().port, 'localhost', function () {
+ socket.write('POST / HTTP/1.1\r\n');
+ socket.write('Host: localhost\r\n');
+ socket.write('Connection: close\r\n');
+ socket.write('Content-Type: multipart/form-data; boundary=foo\r\n');
+ socket.write('Transfer-Encoding: chunked\r\n');
+ socket.write('\r\n');
+ socket.write('7\r\n');
+ socket.write('--foo\r\n\r\n');
+ socket.write('43\r\n');
+ socket.write('Content-Disposition: form-data; name="file"; filename="plain.txt"\r\n\r\n');
+ socket.write('12\r\n');
+ socket.write('\r\nsome text here\r\n\r\n');
+ socket.write('9\r\n');
+ socket.write('--foo--\r\n\r\n');
+ socket.write('0\r\n\r\n');
+ socket.on('close', function () {
+ server.close();
+ });
+ });
+});
diff --git a/test/standalone/test-epilogue-last-chunk.js b/test/standalone/test-epilogue-last-chunk.js
new file mode 100644
index 0000000..4e7320f
--- /dev/null
+++ b/test/standalone/test-epilogue-last-chunk.js
@@ -0,0 +1,44 @@
+var multiparty = require('../../')
+ , assert = require('assert')
+ , http = require('http')
+ , net = require('net');
+
+var server = http.createServer(function(req, res) {
+ var form = new multiparty.Form();
+
+ var partCount = 0;
+ form.on('part', function(part) {
+ part.resume();
+ partCount++;
+ });
+ form.on('close', function() {
+ assert.strictEqual(partCount, 1);
+ res.end();
+ });
+
+ form.parse(req);
+});
+server.listen(function() {
+ var socket = net.connect(server.address().port, 'localhost', function () {
+ socket.write('POST / HTTP/1.1\r\n');
+ socket.write('Host: localhost\r\n');
+ socket.write('Connection: close\r\n');
+ socket.write('Content-Type: multipart/form-data; boundary=foo\r\n');
+ socket.write('Transfer-Encoding: chunked\r\n');
+ socket.write('\r\n');
+ socket.write('7\r\n');
+ socket.write('--foo\r\n\r\n');
+ socket.write('43\r\n');
+ socket.write('Content-Disposition: form-data; name="file"; filename="plain.txt"\r\n\r\n');
+ socket.write('12\r\n');
+ socket.write('\r\nsome text here\r\n\r\n');
+ socket.write('7\r\n');
+ socket.write('--foo--\r\n');
+ socket.write('2\r\n');
+ socket.write('\r\n\r\n');
+ socket.write('0\r\n\r\n');
+ socket.on('close', function () {
+ server.close();
+ });
+ });
+});
diff --git a/test/standalone/test-error-listen-after-parse.js b/test/standalone/test-error-listen-after-parse.js
new file mode 100644
index 0000000..aad1d13
--- /dev/null
+++ b/test/standalone/test-error-listen-after-parse.js
@@ -0,0 +1,20 @@
+var multiparty = require('../../')
+ , assert = require('assert')
+ , http = require('http')
+ , net = require('net')
+ , stream = require('readable-stream');
+
+var form = new multiparty.Form();
+var req = new stream.Readable();
+
+req.headers = {};
+req._read = function(){
+ this.push(new Buffer('--foo!'));
+};
+
+form.parse(req);
+
+form.on('error', function(err){
+ // verification that error emitter when attached after form.parse
+ assert.ok(err);
+});
diff --git a/test/standalone/test-error-unpipe.js b/test/standalone/test-error-unpipe.js
new file mode 100644
index 0000000..2271230
--- /dev/null
+++ b/test/standalone/test-error-unpipe.js
@@ -0,0 +1,32 @@
+var multiparty = require('../../')
+ , assert = require('assert')
+ , http = require('http')
+ , net = require('net')
+ , stream = require('readable-stream');
+
+var form = new multiparty.Form();
+var req = new stream.Readable();
+var unpiped = false;
+
+req.headers = {
+ 'content-type': 'multipart/form-data; boundary=foo'
+};
+req._read = function(){
+ this.push(new Buffer('--foo!'));
+};
+
+form.on('error', function(err){
+ // verification that error event implies unpipe call
+ assert.ok(err);
+ assert.ok(unpiped, 'req was unpiped');
+ assert.equal(req._readableState.flowing, false, 'req not flowing');
+ assert.equal(req._readableState.pipesCount, 0, 'req has 0 pipes');
+});
+
+form.on('unpipe', function(){
+ unpiped = true;
+});
+
+form.parse(req)
+assert.equal(req._readableState.flowing, true, 'req flowing');
+assert.equal(req._readableState.pipesCount, 1, 'req has 1 pipe');
diff --git a/test/standalone/test-issue-15.js b/test/standalone/test-issue-15.js
index 43982fa..fdec5f6 100644
--- a/test/standalone/test-issue-15.js
+++ b/test/standalone/test-issue-15.js
@@ -3,6 +3,7 @@ var http = require('http')
, assert = require('assert')
, superagent = require('superagent')
, path = require('path')
+ , fs = require('fs')
var server = http.createServer(function(req, res) {
assert.strictEqual(req.url, '/upload');
@@ -20,6 +21,7 @@ var server = http.createServer(function(req, res) {
var fileCount = 0;
form.on('file', function(name, file) {
fileCount += 1;
+ fs.unlink(file.path, function () {});
});
form.parse(req, function(err, fields, files) {
@@ -40,13 +42,6 @@ server.listen(function() {
req.attach('files[]', fixture('pf1y5.png'), 'SOG2.JPG');
req.attach('files[]', fixture('binaryfile.tar.gz'), 'BenF364_LIB353.zip');
- // Get the existing boundary.
- var contentType = req.get('content-type');
- var split = contentType.split(' ');
-
- // Set the content-type.
- req.set('content-type', split.join(''));
-
req.end(function(err, resp) {
assert.ifError(err);
resp.on('end', function() {
@@ -70,13 +65,6 @@ function createRequest(separator) {
req.attach('files[]', fixture('pf1y5.png'), 'SOG2.JPG');
req.attach('files[]', fixture('binaryfile.tar.gz'), 'BenF364_LIB353.zip');
- // Get the existing boundary.
- var contentType = req.get('content-type');
- var split = contentType.split(' ');
-
- // Set the content-type.
- req.set('content-type', split.join(separator));
-
req.end(function(err, resp) {
assert.ifError(err);
// We don't close the server, to allow other requests to pass.
diff --git a/test/standalone/test-issue-21.js b/test/standalone/test-issue-21.js
index 155fba0..ea0fddf 100644
--- a/test/standalone/test-issue-21.js
+++ b/test/standalone/test-issue-21.js
@@ -1,4 +1,5 @@
var assert = require('assert');
+var fs = require('fs');
var http = require('http');
var net = require('net');
var multiparty = require('../../');
@@ -7,19 +8,37 @@ var client;
var server = http.createServer(function(req, res) {
var form = new multiparty.Form();
- form.parse(req, function(err, fieldsTable, filesTable, fieldsList, filesList) {
+ form.parse(req, function(err, fields, files) {
if (err) {
console.error(err.stack);
return;
}
- assert.strictEqual(fieldsList.length, 1);
- assert.strictEqual(fieldsList[0].name, "title");
- assert.strictEqual(fieldsList[0].value, "foofoo");
- assert.strictEqual(filesList.length, 4);
- assert.strictEqual(filesList[0].fieldName, "upload");
- assert.strictEqual(filesList[1].fieldName, "upload");
- assert.strictEqual(filesList[2].fieldName, "upload");
- assert.strictEqual(filesList[3].fieldName, "upload");
+ var nameCount = 0;
+ var name;
+ for (name in fields) {
+ assert.strictEqual(name, "title");
+ nameCount += 1;
+
+ var values = fields[name];
+ assert.strictEqual(values.length, 1);
+ assert.strictEqual(values[0], "foofoo");
+ }
+ assert.strictEqual(nameCount, 1);
+
+ nameCount = 0;
+ for (name in files) {
+ assert.strictEqual(name, "upload");
+ nameCount += 1;
+
+ var filesList = files[name];
+ assert.strictEqual(filesList.length, 4);
+ filesList.forEach(function(file){
+ assert.strictEqual(file.fieldName, "upload");
+ fs.unlinkSync(file.path);
+ });
+ }
+ assert.strictEqual(nameCount, 1);
+
res.end();
client.end();
server.close();
@@ -29,7 +48,7 @@ server.listen(function() {
client = net.connect(server.address().port);
client.write("POST /upload HTTP/1.1\r\n" +
- "Content-Length: 728\r\n" +
+ "Content-Length: 726\r\n" +
"Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryvfUZhxgsZDO7FXLF\r\n" +
"\r\n" +
"------WebKitFormBoundaryvfUZhxgsZDO7FXLF\r\n" +
diff --git a/test/standalone/test-issue-32.js b/test/standalone/test-issue-32.js
new file mode 100644
index 0000000..6e70c71
--- /dev/null
+++ b/test/standalone/test-issue-32.js
@@ -0,0 +1,38 @@
+var assert = require('assert');
+var fs = require('fs');
+var http = require('http');
+var net = require('net');
+var multiparty = require('../../');
+
+var client;
+var server = http.createServer(function(req, res) {
+ var form = new multiparty.Form();
+
+ form.parse(req, function(err, fields, files) {
+ if (err) {
+ console.error(err.stack);
+ return;
+ }
+ assert.strictEqual(files.image[0].originalFilename, "测试文档")
+ fs.unlinkSync(files.image[0].path);
+ res.end();
+ client.end();
+ server.close();
+ });
+});
+server.listen(function() {
+ client = net.connect(server.address().port);
+
+ client.write(
+ "POST /upload HTTP/1.1\r\n" +
+ "Accept: */*\r\n" +
+ "Content-Type: multipart/form-data; boundary=\"893e5556-f402-4fec-8180-c59333354c6f\"\r\n" +
+ "Content-Length: 187\r\n" +
+ "\r\n" +
+ "--893e5556-f402-4fec-8180-c59333354c6f\r\n" +
+ "Content-Disposition: form-data; name=\"image\"; filename*=utf-8''%E6%B5%8B%E8%AF%95%E6%96%87%E6%A1%A3\r\n" +
+ "\r\n" +
+ "\r\n" +
+ "--893e5556-f402-4fec-8180-c59333354c6f--\r\n"
+ );
+});
diff --git a/test/standalone/test-issue-5.js b/test/standalone/test-issue-36.js
similarity index 68%
copy from test/standalone/test-issue-5.js
copy to test/standalone/test-issue-36.js
index 80eadf2..6959e7c 100644
--- a/test/standalone/test-issue-5.js
+++ b/test/standalone/test-issue-36.js
@@ -4,16 +4,22 @@ var net = require('net');
var multiparty = require('../../');
var client;
-var attachmentCount = 510;
var server = http.createServer(function(req, res) {
- var form = new multiparty.Form({maxFields: 10000});
-
- form.parse(req, function(err, fieldsTable, filesTable, fieldsList, filesList) {
- assert.strictEqual(err.code, "EMFILE");
+ var form = new multiparty.Form();
+ var endCalled = false;
+ form.on('part', function(part) {
+ part.on('end', function() {
+ endCalled = true;
+ });
+ part.resume();
+ });
+ form.on('close', function() {
+ assert.ok(endCalled);
res.end();
client.end();
server.close();
});
+ form.parse(req);
});
server.listen(function() {
client = net.connect(server.address().port);
@@ -25,15 +31,13 @@ server.listen(function() {
"\r\n" +
"hi1\r\n" +
"\r\n";
- var payloadSize = oneAttachment.length * attachmentCount + boundary.length;
+ var payloadSize = oneAttachment.length + boundary.length;
client.write("POST /upload HTTP/1.1\r\n" +
"Content-Length: " + payloadSize + "\r\n" +
"Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryvfUZhxgsZDO7FXLF\r\n" +
"\r\n");
- for (var i = 0; i < attachmentCount; i += 1) {
- client.write(oneAttachment);
- }
+ client.write(oneAttachment);
client.write(boundary);
});
diff --git a/test/standalone/test-issue-4.js b/test/standalone/test-issue-4.js
index 66b2a69..de38125 100644
--- a/test/standalone/test-issue-4.js
+++ b/test/standalone/test-issue-4.js
@@ -3,6 +3,7 @@ var http = require('http')
, assert = require('assert')
, superagent = require('superagent')
, path = require('path')
+ , fs = require('fs')
var server = http.createServer(function(req, res) {
assert.strictEqual(req.url, '/upload');
@@ -20,6 +21,7 @@ var server = http.createServer(function(req, res) {
var fileCount = 0;
form.on('file', function(name, file) {
fileCount += 1;
+ fs.unlink(file.path, function () {});
});
form.parse(req, function(err, fields, files) {
diff --git a/test/standalone/test-issue-5.js b/test/standalone/test-issue-5.js
index 80eadf2..2525280 100644
--- a/test/standalone/test-issue-5.js
+++ b/test/standalone/test-issue-5.js
@@ -1,4 +1,5 @@
var assert = require('assert');
+var fs = require('fs');
var http = require('http');
var net = require('net');
var multiparty = require('../../');
@@ -8,7 +9,7 @@ var attachmentCount = 510;
var server = http.createServer(function(req, res) {
var form = new multiparty.Form({maxFields: 10000});
- form.parse(req, function(err, fieldsTable, filesTable, fieldsList, filesList) {
+ form.parse(req, function(err) {
assert.strictEqual(err.code, "EMFILE");
res.end();
client.end();
diff --git a/test/standalone/test-max-fields.js b/test/standalone/test-max-fields.js
new file mode 100644
index 0000000..29f8b35
--- /dev/null
+++ b/test/standalone/test-max-fields.js
@@ -0,0 +1,52 @@
+var http = require('http')
+ , multiparty = require('../../')
+ , assert = require('assert')
+ , superagent = require('superagent')
+ , path = require('path')
+ , fs = require('fs')
+
+var server = http.createServer(function(req, res) {
+ assert.strictEqual(req.url, '/upload');
+ assert.strictEqual(req.method, 'POST');
+
+ var form = new multiparty.Form({autoFiles:true,maxFields:2});
+
+ var first = true;
+ form.on('error', function (err) {
+ assert.ok(first);
+ first = false;
+ assert.ok(/maxFields/.test(err.message));
+ });
+
+ var fieldCount = 0;
+ form.on('field', function() {
+ fieldCount += 1;
+ });
+
+ form.parse(req, function(err, fields, files) {
+ assert.ok(!first);
+ assert.ok(fieldCount <= 2);
+ res.statusCode = 413;
+ res.end('too many fields');
+ });
+});
+server.listen(function() {
+ var url = 'http://localhost:' + server.address().port + '/upload';
+ var req = superagent.post(url);
+ var val = new Buffer(10 * 1024);
+ req.field('a', val);
+ req.field('b', val);
+ req.field('c', val);
+ req.on('error', function(err) {
+ assert.ifError(err);
+ });
+ req.end();
+ req.on('response', function(res) {
+ assert.equal(res.statusCode, 413);
+ server.close();
+ });
+});
+
+function fixture(name) {
+ return path.join(__dirname, '..', 'fixture', 'file', name)
+}
diff --git a/test/standalone/test-issue-4.js b/test/standalone/test-max-files-size-exact.js
similarity index 51%
copy from test/standalone/test-issue-4.js
copy to test/standalone/test-max-files-size-exact.js
index 66b2a69..57892ec 100644
--- a/test/standalone/test-issue-4.js
+++ b/test/standalone/test-max-files-size-exact.js
@@ -3,49 +3,40 @@ var http = require('http')
, assert = require('assert')
, superagent = require('superagent')
, path = require('path')
+ , fs = require('fs')
var server = http.createServer(function(req, res) {
assert.strictEqual(req.url, '/upload');
assert.strictEqual(req.method, 'POST');
- var form = new multiparty.Form({autoFields:true,autoFiles:true});
-
- form.on('error', function(err) {
- console.log(err);
- });
-
- form.on('close', function() {
- });
+ var form = new multiparty.Form({autoFiles:true,maxFilesSize:768323}); // exact size of pf1y5.png
var fileCount = 0;
form.on('file', function(name, file) {
fileCount += 1;
+ fs.unlink(file.path, function() {});
});
form.parse(req, function(err, fields, files) {
- var objFileCount = 0;
- for (var file in files) {
- objFileCount += 1;
- }
- // multiparty does NOT try to do intelligent things based on
- // the part name.
- assert.strictEqual(fileCount, 2);
- assert.strictEqual(objFileCount, 1);
- res.end();
+ assert.ifError(err);
+ assert.ok(fileCount === 1);
+ res.end('OK');
});
});
server.listen(function() {
var url = 'http://localhost:' + server.address().port + '/upload';
var req = superagent.post(url);
- req.attach('files[]', fixture('pf1y5.png'), 'SOG2.JPG');
- req.attach('files[]', fixture('binaryfile.tar.gz'), 'BenF364_LIB353.zip');
- req.end(function(err, resp) {
+ req.attach('file0', fixture('pf1y5.png'), 'SOG1.JPG');
+ req.on('error', function(err) {
assert.ifError(err);
- resp.on('end', function() {
- server.close();
- });
+ });
+ req.end();
+ req.on('response', function(res) {
+ assert.equal(res.statusCode, 200);
+ server.close();
});
});
+
function fixture(name) {
return path.join(__dirname, '..', 'fixture', 'file', name)
}
diff --git a/test/standalone/test-issue-4.js b/test/standalone/test-max-files-size.js
similarity index 52%
copy from test/standalone/test-issue-4.js
copy to test/standalone/test-max-files-size.js
index 66b2a69..a8e1b51 100644
--- a/test/standalone/test-issue-4.js
+++ b/test/standalone/test-max-files-size.js
@@ -3,49 +3,48 @@ var http = require('http')
, assert = require('assert')
, superagent = require('superagent')
, path = require('path')
+ , fs = require('fs')
var server = http.createServer(function(req, res) {
assert.strictEqual(req.url, '/upload');
assert.strictEqual(req.method, 'POST');
- var form = new multiparty.Form({autoFields:true,autoFiles:true});
+ var form = new multiparty.Form({autoFiles:true,maxFilesSize:800*1024});
- form.on('error', function(err) {
- console.log(err);
- });
-
- form.on('close', function() {
+ var first = true;
+ form.on('error', function (err) {
+ assert.ok(first);
+ first = false;
+ assert.ok(/maxFilesSize/.test(err.message));
});
var fileCount = 0;
form.on('file', function(name, file) {
fileCount += 1;
+ fs.unlinkSync(file.path);
});
form.parse(req, function(err, fields, files) {
- var objFileCount = 0;
- for (var file in files) {
- objFileCount += 1;
- }
- // multiparty does NOT try to do intelligent things based on
- // the part name.
- assert.strictEqual(fileCount, 2);
- assert.strictEqual(objFileCount, 1);
- res.end();
+ assert.ok(fileCount <= 1);
+ res.statusCode = 413;
+ res.end('files too large');
});
});
server.listen(function() {
var url = 'http://localhost:' + server.address().port + '/upload';
var req = superagent.post(url);
- req.attach('files[]', fixture('pf1y5.png'), 'SOG2.JPG');
- req.attach('files[]', fixture('binaryfile.tar.gz'), 'BenF364_LIB353.zip');
- req.end(function(err, resp) {
+ req.attach('file0', fixture('pf1y5.png'), 'SOG1.JPG');
+ req.attach('file1', fixture('pf1y5.png'), 'SOG2.JPG');
+ req.on('error', function(err) {
assert.ifError(err);
- resp.on('end', function() {
- server.close();
- });
+ });
+ req.end();
+ req.on('response', function(res) {
+ assert.equal(res.statusCode, 413);
+ server.close();
});
});
+
function fixture(name) {
return path.join(__dirname, '..', 'fixture', 'file', name)
}
diff --git a/test/standalone/test-parse-type-error.js b/test/standalone/test-parse-type-error.js
new file mode 100644
index 0000000..42437c8
--- /dev/null
+++ b/test/standalone/test-parse-type-error.js
@@ -0,0 +1,39 @@
+var http = require('http')
+ , multiparty = require('../../')
+ , assert = require('assert')
+ , superagent = require('superagent')
+ , path = require('path');
+
+var server = http.createServer(function(req, res) {
+ assert.strictEqual(req.url, '/upload');
+ assert.strictEqual(req.method, 'POST');
+
+ var form = new multiparty.Form();
+
+ // this is invalid
+ delete req.headers['content-type'];
+
+ form.parse(req, function(err, fields, files) {
+ assert.ok(err);
+ assert.equal(err.message, 'missing content-type header');
+ res.statusCode = 415;
+ res.end();
+ });
+});
+server.listen(function() {
+ var url = 'http://localhost:' + server.address().port + '/upload';
+ var req = superagent.post(url);
+ req.attach('file0', fixture('pf1y5.png'), 'SOG1.JPG');
+ req.on('error', function(err) {
+ assert.ifError(err);
+ });
+ req.end();
+ req.on('response', function(res) {
+ assert.equal(res.statusCode, 415);
+ server.close();
+ });
+});
+
+function fixture(name) {
+ return path.join(__dirname, '..', 'fixture', 'file', name)
+}
diff --git a/test/standalone/test-req-encoding.js b/test/standalone/test-req-encoding.js
new file mode 100644
index 0000000..aebd935
--- /dev/null
+++ b/test/standalone/test-req-encoding.js
@@ -0,0 +1,39 @@
+var http = require('http')
+ , multiparty = require('../../')
+ , assert = require('assert')
+ , superagent = require('superagent')
+ , path = require('path');
+
+var server = http.createServer(function(req, res) {
+ assert.strictEqual(req.url, '/upload');
+ assert.strictEqual(req.method, 'POST');
+
+ var form = new multiparty.Form();
+
+ // this is invalid
+ req.setEncoding('utf8');
+
+ form.parse(req, function(err, fields, files) {
+ assert.ok(err);
+ assert.equal(err.message, 'request encoding must not be set');
+ res.statusCode = 500;
+ res.end();
+ });
+});
+server.listen(function() {
+ var url = 'http://localhost:' + server.address().port + '/upload';
+ var req = superagent.post(url);
+ req.attach('file0', fixture('pf1y5.png'), 'SOG1.JPG');
+ req.on('error', function(err) {
+ assert.ifError(err);
+ });
+ req.end();
+ req.on('response', function(res) {
+ assert.equal(res.statusCode, 500);
+ server.close();
+ });
+});
+
+function fixture(name) {
+ return path.join(__dirname, '..', 'fixture', 'file', name)
+}
diff --git a/test/test.js b/test/test.js
index 199d5cd..83f65bc 100644
--- a/test/test.js
+++ b/test/test.js
@@ -1,64 +1,93 @@
-var spawn = require('child_process').spawn
- , findit = require('findit')
- , path = require('path')
- , hashish = require('hashish')
- , fs = require('fs')
- , http = require('http')
- , net = require('net')
- , assert = require('assert')
- , multiparty = require('../')
- , mkdirp = require('mkdirp')
- , STANDALONE_PATH = path.join(__dirname, 'standalone')
- , server = http.createServer()
- , PORT = 13532
- , FIXTURE_PATH = path.join(__dirname, 'fixture')
- , TMP_PATH = path.join(__dirname, 'tmp')
+var spawn = require('child_process').spawn;
+var findit = require('findit');
+var path = require('path');
+var Pend = require('pend');
+var rimraf = require('rimraf');
+var fs = require('fs');
+var http = require('http');
+var net = require('net');
+var assert = require('assert');
+var multiparty = require('../');
+var mkdirp = require('mkdirp');
+var STANDALONE_PATH = path.join(__dirname, 'standalone');
+var server = http.createServer();
+var PORT = 13532;
+var FIXTURE_PATH = path.join(__dirname, 'fixture');
+var TMP_PATH = path.join(__dirname, 'tmp');
-mkdirp.sync(TMP_PATH);
+resetTempDir(startFixtureTests);
-describe("fixtures", function() {
- before(function(done) {
- server.listen(PORT, done);
+function startFixtureTests() {
+ console.log("Fixture tests:");
+ var walker = findit(path.join(FIXTURE_PATH, 'js'));
+ var pend = new Pend();
+ pend.max = 1;
+ pend.go(function(cb) {
+ server.listen(PORT, cb);
});
- var fixtures = [];
- findit
- .sync(path.join(FIXTURE_PATH, 'js'))
- .forEach(function(jsPath) {
- if (!/\.js$/.test(jsPath)) return;
- var group = path.basename(jsPath, '.js');
- hashish.forEach(require(jsPath), function(fixture, name) {
- it(group + '/' + name, createTest({
- name : group + '/' + name,
- fixture : fixture,
- }));
- });
+ walker.on('file', function(jsPath) {
+ if (!/\.js$/.test(jsPath)) return;
+ var group = path.basename(jsPath, '.js');
+ var testInfo = require(jsPath);
+ for (var name in testInfo) {
+ var fixture = testInfo[name];
+ pend.go(createFixtureTest(group + '/' + name, fixture));
+ }
+ });
+ walker.on('end', function() {
+ pend.wait(function(err) {
+ if (err) throw err;
+ server.close(startStandaloneTests);
});
-});
+ });
+}
-describe("standalone", function() {
- findit
- .sync(STANDALONE_PATH)
- .forEach(function(jsPath) {
- if (!/\.js$/.test(jsPath)) return;
- it(path.basename(jsPath, '.js'), function(done) {
- var child = spawn(process.execPath, [jsPath], { stdio: 'inherit' });
- child.on('error', function(err) {
- done(err);
+function startStandaloneTests() {
+ console.log("\nStandalone tests:");
+ var walker = findit(STANDALONE_PATH);
+ var pend = new Pend();
+ pend.max = 1;
+ walker.on('file', function(jsPath) {
+ if (!/\.js$/.test(jsPath)) return;
+ pend.go(function(cb) {
+ var name = path.basename(jsPath, '.js');
+ process.stdout.write(name + "...");
+ var child = spawn(process.execPath, [jsPath], { env: { TMPDIR: TMP_PATH }, stdio: 'inherit' });
+ child.on('error', function(err) {
+ throw err;
+ });
+ child.on('exit', function(code) {
+ if (code) throw new Error("exited with code " + code);
+ var tmpWalker = findit(TMP_PATH);
+ var fileNames = [];
+ tmpWalker.on('file', function(file) {
+ fileNames.push(file);
});
- child.on('exit', function(code) {
- if (code) return done(new Error("exited with code " + code));
- done();
+ tmpWalker.on('end', function() {
+ if (fileNames.length) {
+ cleanFiles(fileNames);
+ throw new Error("failed to clean up auto files: " + fileNames.join(', '));
+ } else {
+ console.log("OK");
+ cb();
+ }
});
});
});
-});
+ });
+ walker.on('end', function() {
+ pend.wait(function(err) {
+ if (err) throw err;
+ console.log("\nAll tests passed.");
+ });
+ });
+}
-function createTest(fixture) {
- var name = fixture.name;
- fixture = fixture.fixture;
- return function(done) {
+function createFixtureTest(name, fixture) {
+ return function(cb) {
+ process.stdout.write(name + "...");
uploadFixture(name, function(err, parts) {
- if (err) return done(err);
+ if (err) throw err;
fixture.forEach(function(expectedPart, i) {
var parsedPart = parts[i];
assert.equal(parsedPart.type, expectedPart.type);
@@ -71,15 +100,32 @@ function createTest(fixture) {
if(expectedPart.size) assert.strictEqual(file.size, expectedPart.size);
}
});
- done();
+ console.log("OK");
+ cb();
});
};
+}
+
+function cleanFiles(fileNames) {
+ fileNames.forEach(function(fileName) {
+ fs.unlinkSync(fileName);
+ });
+}
+function resetTempDir(cb) {
+ rimraf(TMP_PATH, function(err) {
+ if (err) throw err;
+ mkdirp(TMP_PATH, function(err) {
+ if (err) throw err;
+ cb();
+ });
+ });
}
function uploadFixture(name, cb) {
server.once('request', function(req, res) {
var parts = [];
+ var fileNames = [];
var form = new multiparty.Form({
autoFields: true,
autoFiles: true,
@@ -90,6 +136,7 @@ function uploadFixture(name, cb) {
form.on('error', callback);
form.on('file', function(name, value) {
parts.push({type: 'file', name: name, value: value});
+ fileNames.push(value.path);
});
form.on('field', function(name, value) {
parts.push({type: 'field', name: name, value: value});
@@ -104,6 +151,7 @@ function uploadFixture(name, cb) {
var realCallback = cb;
cb = function() {};
realCallback.apply(null, arguments);
+ cleanFiles(fileNames);
}
});
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-multiparty.git
More information about the Pkg-javascript-commits
mailing list