Skip to content

Commit 8968e01

Browse files
authored
Merge pull request #382 from wxt2005/custom-stream
Handle custom stream
2 parents c72ad67 + e705c0a commit 8968e01

5 files changed

Lines changed: 134 additions & 5 deletions

File tree

Readme.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ form.append( 'my_file', fs.createReadStream('/foo/bar.jpg'), {filename: 'bar.jpg
218218
```
219219

220220
#### _Headers_ getHeaders( [**Headers** _userHeaders_] )
221-
This method adds the correct `content-type` header to the provided array of `userHeaders`.
221+
This method adds the correct `content-type` header to the provided array of `userHeaders`.
222222

223223
#### _String_ getBoundary()
224224
Return the boundary of the formData. By default, the boundary consists of 26 `-` followed by 24 numbers
@@ -348,6 +348,8 @@ axios.post('http://example.com', form, {
348348
## Notes
349349

350350
- ```getLengthSync()``` method DOESN'T calculate length for streams, use ```knownLength``` options as workaround.
351+
- ```getLength(cb)``` will send an error as first parameter of callback if stream length cannot be calculated (e.g. send in custom streams w/o using ```knownLength```).
352+
- ```submit``` will not add `content-length` if form length is unknown or not calculable.
351353
- Starting version `2.x` FormData has dropped support for `[email protected]`.
352354
- Starting version `3.x` FormData has dropped support for `[email protected]`.
353355

lib/form_data.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var http = require('http');
55
var https = require('https');
66
var parseUrl = require('url').parse;
77
var fs = require('fs');
8+
var Stream = require('stream').Stream;
89
var mime = require('mime-types');
910
var asynckit = require('asynckit');
1011
var populate = require('./populate.js');
@@ -100,8 +101,8 @@ FormData.prototype._trackLength = function(header, value, options) {
100101
Buffer.byteLength(header) +
101102
FormData.LINE_BREAK.length;
102103

103-
// empty or either doesn't have path or not an http response
104-
if (!value || ( !value.path && !(value.readable && value.hasOwnProperty('httpVersion')) )) {
104+
// empty or either doesn't have path or not an http response or not a stream
105+
if (!value || ( !value.path && !(value.readable && value.hasOwnProperty('httpVersion')) && !(value instanceof Stream))) {
105106
return;
106107
}
107108

@@ -456,13 +457,15 @@ FormData.prototype.submit = function(params, cb) {
456457

457458
// get content length and fire away
458459
this.getLength(function(err, length) {
459-
if (err) {
460+
if (err && err !== 'Unknown stream') {
460461
this._error(err);
461462
return;
462463
}
463464

464465
// add content length
465-
request.setHeader('Content-Length', length);
466+
if (length) {
467+
request.setHeader('Content-Length', length);
468+
}
466469

467470
this.pipe(request);
468471
if (cb) {

test/integration/test-form-get-length-sync.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ var common = require('../common');
22
var assert = common.assert;
33
var FormData = require(common.dir.lib + '/form_data');
44
var fs = require('fs');
5+
var Readable = require('stream').Readable;
56

67
(function testGetLengthSync() {
78
var fields = [
@@ -73,3 +74,34 @@ var fs = require('fs');
7374

7475
assert.equal(expectedLength, calculatedLength);
7576
})();
77+
78+
(function testReadableStreamData() {
79+
var form = new FormData();
80+
81+
var util = require('util');
82+
util.inherits(CustomReadable, Readable);
83+
84+
/**
85+
* Custion readable constructor
86+
* @param {Object} opt options
87+
* @constructor
88+
*/
89+
function CustomReadable(opt) {
90+
Readable.call(this, opt);
91+
this._max = 2;
92+
this._index = 1;
93+
}
94+
95+
CustomReadable.prototype._read = function() {
96+
var i = this._index++;
97+
if (i > this._max) {
98+
this.push(null);
99+
} else {
100+
this.push('' + i);
101+
}
102+
};
103+
form.append('my_txt', new CustomReadable());
104+
105+
assert.throws(function() { form.getLengthSync(); }, /Cannot calculate proper length in synchronous way/);
106+
107+
})();

test/integration/test-form-get-length.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ var assert = common.assert;
33
var FormData = require(common.dir.lib + '/form_data');
44
var fake = require('fake').create();
55
var fs = require('fs');
6+
var Readable = require('stream').Readable;
67

78
(function testEmptyForm() {
89
var form = new FormData();
@@ -89,3 +90,40 @@ var fs = require('fs');
8990
fake.expectAnytime(callback, [null, expectedLength]);
9091
form.getLength(callback);
9192
})();
93+
94+
(function testReadableStreamData() {
95+
var form = new FormData();
96+
// var expectedLength = 0;
97+
98+
var util = require('util');
99+
util.inherits(CustomReadable, Readable);
100+
101+
/**
102+
* Custion readable constructor
103+
* @param {Object} opt options
104+
* @constructor
105+
*/
106+
function CustomReadable(opt) {
107+
Readable.call(this, opt);
108+
this._max = 2;
109+
this._index = 1;
110+
}
111+
112+
CustomReadable.prototype._read = function() {
113+
var i = this._index++;
114+
if (i > this._max) {
115+
this.push(null);
116+
} else {
117+
this.push('' + i);
118+
}
119+
};
120+
form.append('my_txt', new CustomReadable());
121+
122+
// expectedLength += form._overheadLength + form._lastBoundary().length;
123+
124+
// there is no way to determine the length of this readable stream.
125+
var callback = fake.callback(arguments.callee.name + '-getLength');
126+
fake.expectAnytime(callback, ['Unknown stream', undefined]);
127+
form.getLength(function(err, len) { callback(err,len); });
128+
129+
})();
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
var common = require('../common');
2+
var assert = common.assert;
3+
var http = require('http');
4+
var FormData = require(common.dir.lib + '/form_data');
5+
var Readable = require('stream').Readable;
6+
7+
var server = http.createServer(function(req, res) {
8+
assert.strictEqual(req.headers['Content-Length'], undefined);
9+
res.writeHead(200);
10+
res.end('done');
11+
});
12+
13+
server.listen(common.port, function() {
14+
var form = new FormData();
15+
16+
var util = require('util');
17+
util.inherits(CustomReadable, Readable);
18+
19+
/**
20+
* Custion readable constructor
21+
* @param {Object} opt options
22+
* @constructor
23+
*/
24+
function CustomReadable(opt) {
25+
Readable.call(this, opt);
26+
this._max = 2;
27+
this._index = 1;
28+
}
29+
30+
CustomReadable.prototype._read = function() {
31+
var i = this._index++;
32+
// console.error('send back read data');
33+
if (i > this._max) {
34+
this.push(null);
35+
} else {
36+
this.push('' + i);
37+
}
38+
};
39+
form.append('readable', new CustomReadable());
40+
41+
form.submit('http://localhost:' + common.port + '/', function(err, res) {
42+
if (err) {
43+
throw err;
44+
}
45+
46+
assert.strictEqual(res.statusCode, 200);
47+
48+
// unstuck new streams
49+
res.resume();
50+
51+
server.close();
52+
});
53+
54+
});

0 commit comments

Comments
 (0)