Skip to content

Commit 9a59d08

Browse files
committed
Got initial test to pass
Submitting a form with a string, buffer and file stream
1 parent 1deae47 commit 9a59d08

4 files changed

Lines changed: 67 additions & 84 deletions

File tree

Readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ var request = http.request({
3737
method: 'post',
3838
host: 'example.org',
3939
path: '/upload',
40-
headers: {'Content-Type': 'multipart/form-data'}
40+
headers: form.getHeaders()
4141
});
4242

4343
form.pipe(request);

lib/form_data.js

Lines changed: 51 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,79 @@
1-
var http = require('http');
2-
var oop = require('oop');
3-
var Stream = require('stream').Stream;
1+
var CombinedStream = require('combined-stream');
2+
var util = require('util');
3+
var path = require('path');
4+
var mime = require('mime');
45

56
module.exports = FormData;
6-
oop.extend(FormData, Stream);
7-
var formData = FormData.prototype;
8-
97
function FormData() {
10-
Stream.call(this);
11-
12-
this.readable = true;
13-
this.paused = true;
14-
15-
this._parts = [];
16-
this._streamPart = null;
17-
18-
this._boundary = null;
19-
this._partBegin = null;
8+
CombinedStream.call(this);
209
}
10+
util.inherits(FormData, CombinedStream);
2111

22-
formData._init = function() {
23-
// This generates a 50 character boundary similar to those used by Firefox.
24-
// They are optimized for boyer-moore parsing.
25-
var boundary = '--------------------------';
26-
for (var i = 0; i < 24; i++) {
27-
boundary += Math.floor(Math.random() * 10).toString(16);
28-
}
12+
FormData.prototype.append = function(field, value) {
13+
var parentAppend = CombinedStream.prototype.append;
2914

30-
this._partBegin = new Buffer('--' + boundary + '\r\n');
31-
this._boundary = boundary;
15+
parentAppend.call(this, this._multiPartHeader.bind(this, field, value));
16+
parentAppend.call(this, value);
17+
parentAppend.call(this, this._multiPartFooter.bind(this, field, value));
3218
};
3319

34-
formData.getBoundary = function() {
35-
if (!this._boundary) {
36-
this._init();
20+
FormData.prototype._multiPartHeader = function(field, value, next) {
21+
var boundary = this.getBoundary();
22+
23+
var header =
24+
'--' + boundary
25+
+ '\r\n'
26+
+ 'Content-Disposition: form-data; name="' + field + '"';
27+
28+
if (value.path) {
29+
header +=
30+
'; filename="' + path.basename(value.path) + '"\r\n'
31+
+ 'Content-Type: ' + mime.lookup(value.path);
3732
}
3833

39-
return this._boundary;
40-
};
34+
header += '\r\n\r\n';
4135

42-
formData.append = function(name, value) {
43-
this._parts.push({
44-
name: name,
45-
value: value,
46-
});
36+
next(header);
4737
};
4838

49-
formData.pipe = function(destination) {
50-
var r = Stream.prototype.pipe.call(this, destination);
51-
this.resume();
52-
return r;
53-
};
39+
FormData.prototype._multiPartFooter = function(field, value, next) {
40+
var footer = '\r\n';
5441

55-
formData.resume = function() {
56-
if (!this._boundary) {
57-
this._init();
42+
var lastPart = (this._streams.length === 0);
43+
if (lastPart) {
44+
footer += '--' + this.getBoundary() + '--';
5845
}
5946

60-
this.paused = false;
61-
process.nextTick(this._emitData.bind(this));
47+
next(footer);
6248
};
6349

64-
formData.pause = function() {
65-
this.paused = true;
66-
//if (this._streamPart) {
67-
//this._streamPart.pause();
68-
//}
69-
};
50+
FormData.prototype.getHeaders = function(userHeaders) {
51+
var formHeaders = {
52+
'content-type': 'multipart/form-data; boundary=' + this.getBoundary(),
53+
};
7054

71-
formData._emitData = function() {
72-
if (this.paused) {
73-
return;
55+
for (var header in userHeaders) {
56+
formHeaders[header.toLowerCase()] = userHeaders[header];
7457
}
7558

76-
var part = this._parts.shift();
77-
if (!part) {
78-
return;
79-
}
59+
return formHeaders;
60+
}
8061

81-
if (part.value instanceof Stream) {
82-
this._emitStreamPart(part);
83-
} else {
84-
this._emitRegularPart(part);
62+
FormData.prototype.getBoundary = function() {
63+
if (!this._boundary) {
64+
this._generateBoundary();
8565
}
86-
};
87-
88-
formData._emitRegularPart = function(part) {
89-
var header = 'Content-Disposition: form-data; name="' + part.name + '"';
90-
header += '\r\n\r\n';
9166

92-
this.emit('data', this._partBegin);
93-
this.emit('data', new Buffer(header + part.value + '\r\n', 'utf8'));
67+
return this._boundary;
68+
};
9469

95-
if (this._parts.length === 0) {
96-
this._emitEndBoundary();
97-
this.readable = false;
98-
} else {
99-
this._emitData();
70+
FormData.prototype._generateBoundary = function() {
71+
// This generates a 50 character boundary similar to those used by Firefox.
72+
// They are optimized for boyer-moore parsing.
73+
var boundary = '--------------------------';
74+
for (var i = 0; i < 24; i++) {
75+
boundary += Math.floor(Math.random() * 10).toString(16);
10076
}
101-
};
10277

103-
formData._emitEndBoundary = function() {
104-
this.emit('data', new Buffer('--' + this._boundary + '--\r\n'));
78+
this._boundary = boundary;
10579
};

test/fixture/unicycle.jpg

19.3 KB
Loading

test/integration/test-pipe.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,34 @@
11
var common = require('../common');
22
var assert = common.assert;
33
var http = require('http');
4+
var path = require('path');
5+
var mime = require('mime');
6+
var fs = require('fs');
47
var FormData = require(common.dir.lib + '/form_data');
58
var IncomingForm = require('formidable').IncomingForm;
69

710
var FIELDS = [
811
{name: 'my_field', value: 'my_value'},
912
{name: 'my_buffer', value: new Buffer([1, 2, 3])},
13+
{name: 'my_file', value: fs.createReadStream(common.dir.fixture + '/unicycle.jpg')},
1014
];
1115

1216
var server = http.createServer(function(req, res) {
1317
var form = new IncomingForm();
18+
form.uploadDir = common.dir.tmp;
1419
form.parse(req);
1520
form
1621
.on('field', function(name, value) {
1722
var field = FIELDS.shift();
1823
assert.strictEqual(name, field.name);
1924
assert.strictEqual(value, field.value+'');
2025
})
26+
.on('file', function(name, file) {
27+
var field = FIELDS.shift();
28+
assert.strictEqual(name, field.name);
29+
assert.strictEqual(file.name, path.basename(field.value.path));
30+
assert.strictEqual(file.type, mime.lookup(file.name));
31+
})
2132
.on('end', function() {
2233
res.writeHead(200);
2334
res.end('done');
@@ -36,18 +47,16 @@ server.listen(common.port, function() {
3647
method: 'post',
3748
port: common.port,
3849
path: '/upload',
39-
headers: {
40-
'Content-Type': 'multipart/form-data; boundary=' + form.getBoundary(),
41-
}
50+
headers: form.getHeaders()
4251
});
4352

4453
form.pipe(request);
45-
form.on('data', function(chunk) {
46-
//process.stderr.write(chunk+'');
47-
});
4854

4955
request.on('response', function(res) {
5056
server.close();
5157
});
5258
});
5359

60+
process.on('exit', function() {
61+
assert.strictEqual(FIELDS.length, 0);
62+
});

0 commit comments

Comments
 (0)