Skip to content

Commit

Permalink
Merge pull request #73 from alexindigo/71_read_range
Browse files Browse the repository at this point in the history
#71 Respect bytes range in a read stream.
  • Loading branch information
alexindigo committed Jun 24, 2014
2 parents 3f4949b + f00d9a2 commit cd4e41b
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 11 deletions.
38 changes: 31 additions & 7 deletions lib/form_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,38 @@ FormData.prototype._trackLength = function(header, value, options) {
this._lengthRetrievers.push(function(next) {

if (value.hasOwnProperty('fd')) {
fs.stat(value.path, function(err, stat) {
if (err) {
next(err);
return;
}

next(null, stat.size);
});
// take read range into a account
// `end` = Infinity –> read file till the end
//
// TODO: Looks like there is bug in Node fs.createReadStream
// it doesn't respect `end` options without `start` options
// Fix it when node fixes it.
// https://github.com/joyent/node/issues/7819
if (value.end != undefined && value.end != Infinity && value.start != undefined) {

// when end specified
// no need to calculate range
// inclusive, starts with 0
next(null, value.end+1 - (value.start ? value.start : 0));

// not that fast snoopy
} else {
// still need to fetch file size from fs
fs.stat(value.path, function(err, stat) {

var fileSize;

if (err) {
next(err);
return;
}

// update final size based on the range options
fileSize = stat.size - (value.start ? value.start : 0);
next(null, fileSize);
});
}

// or http response
} else if (value.hasOwnProperty('httpVersion')) {
Expand Down
1 change: 0 additions & 1 deletion test/fixture/bacon.txt

This file was deleted.

1 change: 1 addition & 0 deletions test/fixture/veggies.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Veggies are healthy.
4 changes: 1 addition & 3 deletions test/integration/test-form-get-length.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ var fs = require('fs');
},
{
name: 'my_txt',
value: fs.createReadStream(common.dir.fixture + '/bacon.txt'),
value: fs.createReadStream(common.dir.fixture + '/veggies.txt'),
},
];

Expand All @@ -89,5 +89,3 @@ var fs = require('fs');
fake.expectAnytime(callback, [null, expectedLength]);
form.getLength(callback);
})();


97 changes: 97 additions & 0 deletions test/integration/test-ranged-filestream.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
test ranged fs.createReadStream
re: https://github.com/felixge/node-form-data/issues/71
*/

var common = require('../common');
var assert = common.assert;
var http = require('http');
var fs = require('fs');

var FormData = require(common.dir.lib + '/form_data');
var IncomingForm = require('formidable').IncomingForm;

var testSubjects = {
'a_file': {
file: 'veggies.txt',
start: 8,
end: 18
}, 'b_file': {
file: 'veggies.txt',
start: 6
}, 'c_file': {
file: 'veggies.txt',
end: 16
}, 'd_file': {
file: 'veggies.txt',
start: 0,
end: 16
}, 'e_file': {
file: 'veggies.txt',
start: 0,
end: 0
}
};

var server = http.createServer(function(req, res) {
var requestBodyLength = 0;

// calculate actual length of the request body
req.on('data', function(data) {
requestBodyLength += data.length;
});

var form = new IncomingForm({uploadDir: common.dir.tmp});

form.parse(req);

form
.on('file', function(name, file) {

// make sure total Content-Length is properly calculated
assert.equal(req.headers['content-length'], requestBodyLength);
// make sure chunks are the same size
assert.equal(file.size, testSubjects[name].readSize);
})
.on('end', function() {
res.writeHead(200);
res.end('done');
});
});


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

// add test subjects to the form
for (name in testSubjects) {
if (!testSubjects.hasOwnProperty(name)) continue;

options = {encoding: 'utf8'};
testSubjects[name].start && (options.start = testSubjects[name].start);
testSubjects[name].end && (options.end = testSubjects[name].end);

form.append(name, testSubjects[name].fsStream = fs.createReadStream(common.dir.fixture + '/' + testSubjects[name].file, options));

// calculate data size
testSubjects[name].readSize = 0;
testSubjects[name].fsStream.on('data', function(data) {
this.readSize += data.length;
}.bind(testSubjects[name]));
}

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 cd4e41b

Please sign in to comment.