Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle custom stream #382

Merged
merged 4 commits into from
Feb 15, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ fetch('http://example.com', { method: 'POST', body: 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```).
- ```sbumit``` will not add `content-length` if form length is unknown or not calculable.
wxt2005 marked this conversation as resolved.
Show resolved Hide resolved
- Starting version `2.x` FormData has dropped support for `node@0.10.x`.

## License
Expand Down
11 changes: 7 additions & 4 deletions lib/form_data.js
Original file line number Diff line number Diff line change
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 @@ -426,13 +427,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._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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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();
});

});