Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added support for https destination, http-response and mikeal's reque…

…st streams.

Use cases:

- https destination: upload photos to api.500px.com

- remote file streams (http-response, mikeal's request): Upload photos from Flickr to 500px.com or vice versa.
  • Loading branch information...
commit 383cdd473de6d8cb5180a90c14238b59115c4aca 1 parent 83ab6b7
@alexindigo alexindigo authored
View
85 lib/form_data.js
@@ -2,6 +2,7 @@ var CombinedStream = require('combined-stream');
var util = require('util');
var path = require('path');
var http = require('http');
+var https = require('https');
var parseUrl = require('url').parse;
var fs = require('fs');
var mime = require('mime');
@@ -22,6 +23,9 @@ FormData.LINE_BREAK = '\r\n';
FormData.prototype.append = function(field, value) {
var append = CombinedStream.prototype.append.bind(this);
+ // all that streamy business can't handle numbers
+ if (typeof value == 'number') value = ''+value;
+
var header = this._multiPartHeader(field, value);
var footer = this._multiPartFooter(field, value);
@@ -45,19 +49,46 @@ FormData.prototype._trackLength = function(header, value) {
Buffer.byteLength(header) +
+ FormData.LINE_BREAK.length;
- if (!value || !value.path) {
+ // empty or ethier doesn't have path or not an http response
+ if (!value || ( !value.path && !(value.readable && value.hasOwnProperty('httpVersion')) )) {
return;
}
this._lengthRetrievers.push(function(next) {
- fs.stat(value.path, function(err, stat) {
- if (err) {
- next(err);
- return;
- }
- next(null, stat.size);
- });
+ // check if it's local file
+ if (value.hasOwnProperty('fd'))
+ {
+ fs.stat(value.path, function(err, stat) {
+ if (err) {
+ next(err);
+ return;
+ }
+
+ next(null, stat.size);
+ });
+ }
+ // or http response
+ else if (value.hasOwnProperty('httpVersion'))
+ {
+ next(null, +value.headers['content-length']);
+ }
+ // or request stream http://github.com/mikeal/request
+ else if (value.hasOwnProperty('httpModule'))
+ {
+ // wait till response come back
+ value.on('response', function(response)
+ {
+ value.pause();
+ next(null, +response.headers['content-length']);
+ });
+ value.resume();
+ }
+ else
+ {
+ // something else
+ next('Unknown stream');
+ }
});
};
@@ -67,11 +98,20 @@ FormData.prototype._multiPartHeader = function(field, value) {
'--' + boundary + FormData.LINE_BREAK +
'Content-Disposition: form-data; name="' + field + '"';
+ // fs- and request- streams have path property
+ // TODO: Use request's response mime-type
if (value.path) {
header +=
'; filename="' + path.basename(value.path) + '"' + FormData.LINE_BREAK +
'Content-Type: ' + mime.lookup(value.path);
}
+ // http response has not
+ else if (value.readable && value.hasOwnProperty('httpVersion'))
+ {
+ header +=
+ '; filename="' + path.basename(value.client._httpMessage.path) + '"' + FormData.LINE_BREAK +
+ 'Content-Type: ' + value.headers['content-type'];
+ }
header += FormData.LINE_BREAK + FormData.LINE_BREAK;
return header;
@@ -174,15 +214,26 @@ FormData.prototype.getLength = function(cb) {
FormData.prototype.submit = function(url, cb) {
this.getLength(function(err, length) {
- var parsedUrl = parseUrl(url);
-
- var request = http.request({
- method: 'post',
- port: parsedUrl.port || 80,
- path: parsedUrl.pathname,
- headers: this.getHeaders({'Content-Length': length}),
- host: parsedUrl.hostname
- });
+ var request
+ , parsedUrl = parseUrl(url)
+ , options = {
+ method: 'post',
+ port: parsedUrl.port || 80,
+ path: parsedUrl.pathname,
+ headers: this.getHeaders({'Content-Length': length}),
+ host: parsedUrl.hostname
+ };
+
+ if (parsedUrl.protocol == 'https:')
+ {
+ // override default port
+ if (!parsedUrl.port) options.port = 443;
+ request = https.request(options);
+ }
+ else
+ {
+ request = http.request(options);
+ }
this.pipe(request);
if (cb) {
View
3  package.json
@@ -19,6 +19,7 @@
"devDependencies": {
"fake": "0.2.1",
"far": "0.0.1",
- "formidable": "1.0.2"
+ "formidable": "1.0.2",
+ "request": "~2.9.203"
}
}
View
123 test/integration/test-http-respone.js
@@ -0,0 +1,123 @@
+var common = require('../common');
+var assert = common.assert;
+var http = require('http');
+var path = require('path');
+var mime = require('mime');
+var request = require('request');
+var parseUrl = require('url').parse;
+var fs = require('fs');
+var FormData = require(common.dir.lib + '/form_data');
+var IncomingForm = require('formidable').IncomingForm;
+
+var remoteFile = 'http://nodejs.org/images/logo.png';
+
+var FIELDS;
+var server;
+
+var parsedUrl = parseUrl(remoteFile)
+ , options =
+ {
+ method: 'get',
+ port: parsedUrl.port || 80,
+ path: parsedUrl.pathname,
+ host: parsedUrl.hostname
+ }
+ ;
+
+http.request(options, function(res)
+{
+
+ FIELDS = [
+ {name: 'my_field', value: 'my_value'},
+ {name: 'my_buffer', value: new Buffer([1, 2, 3])},
+ {name: 'remote_file', value: res }
+ ];
+
+ var form = new FormData();
+ FIELDS.forEach(function(field) {
+ form.append(field.name, field.value);
+ });
+
+ server.listen(common.port, function() {
+
+ form.submit('http://localhost:' + common.port + '/', function(err, res) {
+
+ if (err) {
+ throw err;
+ }
+
+ assert.strictEqual(res.statusCode, 200);
+ server.close();
+ });
+
+ });
+
+
+}).end();
+
+server = http.createServer(function(req, res) {
+
+ // formidable is broken so let's do it manual way
+ //
+ // var form = new IncomingForm();
+ // form.uploadDir = common.dir.tmp;
+ // form.parse(req);
+ // form
+ // .on('field', function(name, value) {
+ // var field = FIELDS.shift();
+ // assert.strictEqual(name, field.name);
+ // assert.strictEqual(value, field.value+'');
+ // })
+ // .on('file', function(name, file) {
+ // var field = FIELDS.shift();
+ // assert.strictEqual(name, field.name);
+ // assert.strictEqual(file.name, path.basename(field.value.path));
+ // // mime.lookup file.NAME == 'my_file' ?
+ // assert.strictEqual(file.type, mime.lookup(file.name));
+ // })
+ // .on('end', function() {
+ // res.writeHead(200);
+ // res.end('done');
+ // });
+
+ // temp workaround
+ var data = '';
+ req.setEncoding('utf8');
+ req.on('data', function(d)
+ {
+ data += d;
+ });
+ req.on('end', function()
+ {
+
+ // check for the fields' traces
+
+ // 1st field : my_field
+ var field = FIELDS.shift();
+ assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
+ assert.ok( data.indexOf(field.value) != -1 );
+
+ // 2nd field : my_buffer
+ var field = FIELDS.shift();
+ assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
+ assert.ok( data.indexOf(field.value) != -1 );
+
+ // 3rd field : remote_file
+ var field = FIELDS.shift();
+ assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
+ assert.ok( data.indexOf('; filename="'+path.basename(remoteFile)+'"') != -1 );
+ // check for http://nodejs.org/images/logo.png traces
+ assert.ok( data.indexOf('ImageReady') != -1 );
+ assert.ok( data.indexOf('Content-Type: '+mime.lookup(remoteFile) ) != -1 );
+
+ res.writeHead(200);
+ res.end('done');
+
+ });
+
+});
+
+
+process.on('exit', function() {
+ assert.strictEqual(FIELDS.length, 0);
+});
View
89 test/integration/test-pipe.js
@@ -3,36 +3,87 @@ var assert = common.assert;
var http = require('http');
var path = require('path');
var mime = require('mime');
+var request = require('request');
var fs = require('fs');
var FormData = require(common.dir.lib + '/form_data');
var IncomingForm = require('formidable').IncomingForm;
+var remoteFile = 'http://nodejs.org/images/logo.png';
+
var FIELDS = [
{name: 'my_field', value: 'my_value'},
{name: 'my_buffer', value: new Buffer([1, 2, 3])},
{name: 'my_file', value: fs.createReadStream(common.dir.fixture + '/unicycle.jpg')},
+ {name: 'remote_file', value: request(remoteFile) }
];
var server = http.createServer(function(req, res) {
- var form = new IncomingForm();
- form.uploadDir = common.dir.tmp;
- form.parse(req);
- form
- .on('field', function(name, value) {
- var field = FIELDS.shift();
- assert.strictEqual(name, field.name);
- assert.strictEqual(value, field.value+'');
- })
- .on('file', function(name, file) {
- var field = FIELDS.shift();
- assert.strictEqual(name, field.name);
- assert.strictEqual(file.name, path.basename(field.value.path));
- assert.strictEqual(file.type, mime.lookup(file.name));
- })
- .on('end', function() {
- res.writeHead(200);
- res.end('done');
- });
+
+ // formidable is broken so let's do it manual way
+ //
+ // var form = new IncomingForm();
+ // form.uploadDir = common.dir.tmp;
+ // form.parse(req);
+ // form
+ // .on('field', function(name, value) {
+ // var field = FIELDS.shift();
+ // assert.strictEqual(name, field.name);
+ // assert.strictEqual(value, field.value+'');
+ // })
+ // .on('file', function(name, file) {
+ // var field = FIELDS.shift();
+ // assert.strictEqual(name, field.name);
+ // assert.strictEqual(file.name, path.basename(field.value.path));
+ // assert.strictEqual(file.type, mime.lookup(file.name));
+ // })
+ // .on('end', function() {
+ // res.writeHead(200);
+ // res.end('done');
+ // });
+
+ // temp workaround
+ var data = '';
+ req.setEncoding('utf8');
+ req.on('data', function(d)
+ {
+ data += d;
+ });
+ req.on('end', function()
+ {
+ // check for the fields' traces
+
+ // 1st field : my_field
+ var field = FIELDS.shift();
+ assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
+ assert.ok( data.indexOf(field.value) != -1 );
+
+ // 2nd field : my_buffer
+ var field = FIELDS.shift();
+ assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
+ assert.ok( data.indexOf(field.value) != -1 );
+
+ // 3rd field : my_file
+ var field = FIELDS.shift();
+ assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
+ assert.ok( data.indexOf('; filename="'+path.basename(field.value.path)+'"') != -1 );
+ // check for unicycle.jpg traces
+ assert.ok( data.indexOf('2005:06:21 01:44:12') != -1 );
+ assert.ok( data.indexOf('Content-Type: '+mime.lookup(field.value.path) ) != -1 );
+
+ // 4th field : remote_file
+ var field = FIELDS.shift();
+ assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
+ assert.ok( data.indexOf('; filename="'+path.basename(field.value.path)+'"') != -1 );
+ // check for http://nodejs.org/images/logo.png traces
+ assert.ok( data.indexOf('ImageReady') != -1 );
+ assert.ok( data.indexOf('Content-Type: '+mime.lookup(remoteFile) ) != -1 );
+
+ res.writeHead(200);
+ res.end('done');
+
+ });
+
+
});
server.listen(common.port, function() {
View
93 test/integration/test-submit.js
@@ -3,36 +3,87 @@ var assert = common.assert;
var http = require('http');
var path = require('path');
var mime = require('mime');
+var request = require('request');
var fs = require('fs');
var FormData = require(common.dir.lib + '/form_data');
var IncomingForm = require('formidable').IncomingForm;
+var remoteFile = 'http://nodejs.org/images/logo.png';
+
var FIELDS = [
{name: 'my_field', value: 'my_value'},
{name: 'my_buffer', value: new Buffer([1, 2, 3])},
- {name: 'my_file', value: fs.createReadStream(common.dir.fixture + '/unicycle.jpg')},
+ {name: 'my_file', value: fs.createReadStream(common.dir.fixture + '/unicycle.jpg') },
+ {name: 'remote_file', value: request(remoteFile) }
];
var server = http.createServer(function(req, res) {
- var form = new IncomingForm();
- form.uploadDir = common.dir.tmp;
- form.parse(req);
- form
- .on('field', function(name, value) {
- var field = FIELDS.shift();
- assert.strictEqual(name, field.name);
- assert.strictEqual(value, field.value+'');
- })
- .on('file', function(name, file) {
- var field = FIELDS.shift();
- assert.strictEqual(name, field.name);
- assert.strictEqual(file.name, path.basename(field.value.path));
- assert.strictEqual(file.type, mime.lookup(file.name));
- })
- .on('end', function() {
- res.writeHead(200);
- res.end('done');
- });
+
+ // formidable is broken so let's do it manual way
+ //
+ // var form = new IncomingForm();
+ // form.uploadDir = common.dir.tmp;
+ // form.parse(req);
+ // form
+ // .on('field', function(name, value) {
+ // var field = FIELDS.shift();
+ // assert.strictEqual(name, field.name);
+ // assert.strictEqual(value, field.value+'');
+ // })
+ // .on('file', function(name, file) {
+ // var field = FIELDS.shift();
+ // assert.strictEqual(name, field.name);
+ // assert.strictEqual(file.name, path.basename(field.value.path));
+ // // mime.lookup file.NAME == 'my_file' ?
+ // assert.strictEqual(file.type, mime.lookup(file.name));
+ // })
+ // .on('end', function() {
+ // res.writeHead(200);
+ // res.end('done');
+ // });
+
+ // temp workaround
+ var data = '';
+ req.setEncoding('utf8');
+ req.on('data', function(d)
+ {
+ data += d;
+ });
+ req.on('end', function()
+ {
+ // check for the fields' traces
+
+ // 1st field : my_field
+ var field = FIELDS.shift();
+ assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
+ assert.ok( data.indexOf(field.value) != -1 );
+
+ // 2nd field : my_buffer
+ var field = FIELDS.shift();
+ assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
+ assert.ok( data.indexOf(field.value) != -1 );
+
+ // 3rd field : my_file
+ var field = FIELDS.shift();
+ assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
+ assert.ok( data.indexOf('; filename="'+path.basename(field.value.path)+'"') != -1 );
+ // check for unicycle.jpg traces
+ assert.ok( data.indexOf('2005:06:21 01:44:12') != -1 );
+ assert.ok( data.indexOf('Content-Type: '+mime.lookup(field.value.path) ) != -1 );
+
+ // 4th field : remote_file
+ var field = FIELDS.shift();
+ assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 );
+ assert.ok( data.indexOf('; filename="'+path.basename(field.value.path)+'"') != -1 );
+ // check for http://nodejs.org/images/logo.png traces
+ assert.ok( data.indexOf('ImageReady') != -1 );
+ assert.ok( data.indexOf('Content-Type: '+mime.lookup(remoteFile) ) != -1 );
+
+ res.writeHead(200);
+ res.end('done');
+
+ });
+
});
server.listen(common.port, function() {
@@ -42,6 +93,7 @@ server.listen(common.port, function() {
});
form.submit('http://localhost:' + common.port + '/', function(err, res) {
+
if (err) {
throw err;
}
@@ -49,6 +101,7 @@ server.listen(common.port, function() {
assert.strictEqual(res.statusCode, 200);
server.close();
});
+
});
process.on('exit', function() {
Please sign in to comment.
Something went wrong with that request. Please try again.