Skip to content

Commit

Permalink
Merge pull request #382 from wxt2005/custom-stream
Browse files Browse the repository at this point in the history
Handle custom stream
  • Loading branch information
niftylettuce committed Feb 15, 2021
2 parents c72ad67 + e705c0a commit 8968e01
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 5 deletions.
4 changes: 3 additions & 1 deletion Readme.md
Expand Up @@ -218,7 +218,7 @@ form.append( 'my_file', fs.createReadStream('/foo/bar.jpg'), {filename: 'bar.jpg
```

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

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

- ```getLengthSync()``` method DOESN'T calculate length for streams, use ```knownLength``` options as workaround.
- ```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```).
- ```submit``` will not add `content-length` if form length is unknown or not calculable.
- Starting version `2.x` FormData has dropped support for `node@0.10.x`.
- Starting version `3.x` FormData has dropped support for `node@4.x`.

Expand Down
11 changes: 7 additions & 4 deletions lib/form_data.js
Expand Up @@ -5,6 +5,7 @@ var http = require('http');
var https = require('https');
var parseUrl = require('url').parse;
var fs = require('fs');
var Stream = require('stream').Stream;
var mime = require('mime-types');
var asynckit = require('asynckit');
var populate = require('./populate.js');
Expand Down Expand Up @@ -100,8 +101,8 @@ FormData.prototype._trackLength = function(header, value, options) {
Buffer.byteLength(header) +
FormData.LINE_BREAK.length;

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

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

// get content length and fire away
this.getLength(function(err, length) {
if (err) {
if (err && err !== 'Unknown stream') {

This comment has been minimized.

Copy link
@niftylettuce

niftylettuce Jan 27, 2022

Author Contributor

Shouldn't this be err.message? I was just going through - we probably didn't need to do major version bump and I could do a patched release.

this._error(err);
return;
}

// add content length
request.setHeader('Content-Length', length);
if (length) {
request.setHeader('Content-Length', length);
}

this.pipe(request);
if (cb) {
Expand Down
32 changes: 32 additions & 0 deletions test/integration/test-form-get-length-sync.js
Expand Up @@ -2,6 +2,7 @@ var common = require('../common');
var assert = common.assert;
var FormData = require(common.dir.lib + '/form_data');
var fs = require('fs');
var Readable = require('stream').Readable;

(function testGetLengthSync() {
var fields = [
Expand Down Expand Up @@ -73,3 +74,34 @@ var fs = require('fs');

assert.equal(expectedLength, calculatedLength);
})();

(function testReadableStreamData() {
var form = new FormData();

var util = require('util');
util.inherits(CustomReadable, Readable);

/**
* Custion readable constructor
* @param {Object} opt options
* @constructor
*/
function CustomReadable(opt) {
Readable.call(this, opt);
this._max = 2;
this._index = 1;
}

CustomReadable.prototype._read = function() {
var i = this._index++;
if (i > this._max) {
this.push(null);
} else {
this.push('' + i);
}
};
form.append('my_txt', new CustomReadable());

assert.throws(function() { form.getLengthSync(); }, /Cannot calculate proper length in synchronous way/);

})();
38 changes: 38 additions & 0 deletions test/integration/test-form-get-length.js
Expand Up @@ -3,6 +3,7 @@ var assert = common.assert;
var FormData = require(common.dir.lib + '/form_data');
var fake = require('fake').create();
var fs = require('fs');
var Readable = require('stream').Readable;

(function testEmptyForm() {
var form = new FormData();
Expand Down Expand Up @@ -89,3 +90,40 @@ var fs = require('fs');
fake.expectAnytime(callback, [null, expectedLength]);
form.getLength(callback);
})();

(function testReadableStreamData() {
var form = new FormData();
// var expectedLength = 0;

var util = require('util');
util.inherits(CustomReadable, Readable);

/**
* Custion readable constructor
* @param {Object} opt options
* @constructor
*/
function CustomReadable(opt) {
Readable.call(this, opt);
this._max = 2;
this._index = 1;
}

CustomReadable.prototype._read = function() {
var i = this._index++;
if (i > this._max) {
this.push(null);
} else {
this.push('' + i);
}
};
form.append('my_txt', new CustomReadable());

// expectedLength += form._overheadLength + form._lastBoundary().length;

// there is no way to determine the length of this readable stream.
var callback = fake.callback(arguments.callee.name + '-getLength');
fake.expectAnytime(callback, ['Unknown stream', undefined]);
form.getLength(function(err, len) { callback(err,len); });

})();
54 changes: 54 additions & 0 deletions test/integration/test-submit-readable-stream.js
@@ -0,0 +1,54 @@
var common = require('../common');
var assert = common.assert;
var http = require('http');
var FormData = require(common.dir.lib + '/form_data');
var Readable = require('stream').Readable;

var server = http.createServer(function(req, res) {
assert.strictEqual(req.headers['Content-Length'], undefined);
res.writeHead(200);
res.end('done');
});

server.listen(common.port, function() {
var form = new FormData();

var util = require('util');
util.inherits(CustomReadable, Readable);

/**
* Custion readable constructor
* @param {Object} opt options
* @constructor
*/
function CustomReadable(opt) {
Readable.call(this, opt);
this._max = 2;
this._index = 1;
}

CustomReadable.prototype._read = function() {
var i = this._index++;
// console.error('send back read data');
if (i > this._max) {
this.push(null);
} else {
this.push('' + i);
}
};
form.append('readable', new CustomReadable());

form.submit('http://localhost:' + common.port + '/', function(err, res) {
if (err) {
throw err;
}

assert.strictEqual(res.statusCode, 200);

// unstuck new streams
res.resume();

server.close();
});

});

0 comments on commit 8968e01

Please sign in to comment.