Permalink
Browse files

POST request file attachments support

  • Loading branch information...
1 parent 8ddd78b commit 7c184f202fd187c61d1a574323c1c455e72db2ae @harrisiirak committed Jul 11, 2012
Showing with 77 additions and 13 deletions.
  1. +77 −13 lib/adapters/oauth1.js
View
@@ -1,4 +1,5 @@
var util = require('util');
+var fs = require('fs');
var crypto = require('crypto');
var URL = require('url');
var querystring = require('querystring');
@@ -116,7 +117,9 @@ AuthorizationHeader.prototype._generateSignature = function(parameters) {
}
var hmac = crypto.createHmac('sha1', signatureKey).update(signatureValue);
- return hmac.digest('base64');
+ var signature = hmac.digest('base64');
+
+ return signature;
} else { // Asume 'PLAINTEXT' method
return signatureKey;
}
@@ -177,7 +180,7 @@ AuthorizationCredentials.prototype.setClient = function(token, secret) {
function Adapter(options) {
// TODO: Validate options
this._options = options;
- this._options.version = '1.0';
+ this._options.version = '1.0A';
if (!this._options.callback) {
this._options.callback = 'oob';
@@ -193,7 +196,7 @@ function Adapter(options) {
util.inherits(Adapter, adapter.Adapter); // Inherit base adapter
-Adapter.prototype._sendRequest = function(url, method, params, fields, credentials, callback) {
+Adapter.prototype._sendRequest = function(url, method, params, files, fields, credentials, callback) {
var that = this;
var requestHeaders = {};
var queryParams = [];
@@ -259,7 +262,7 @@ Adapter.prototype._sendRequest = function(url, method, params, fields, credentia
res.on('end', function() {
if (res.statusCode >= 300 && res.statusCode <= 307) { // Redirect
- that._sendRequest(res.headers.location, method, params, fields, credentials, callback);
+ that._sendRequest(res.headers.location, method, params, null, fields, credentials, callback);
} else if ((res.statusCode >= 200 && res.statusCode <= 206)) { // Normal response
if (callback) {
callback(null, response);
@@ -270,11 +273,72 @@ Adapter.prototype._sendRequest = function(url, method, params, fields, credentia
});
});
- if (body.length > 0) {
- req.write(body);
- }
+ // Files
+ if (files) {
+ var boundaryKey = Math.random().toString(16); // random string
+ var multipartHeader = '--' + boundaryKey + '\r\n'
+ + 'Content-Type: application/octet-stream\r\n'
+ + 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n'
+ + 'Content-Transfer-Encoding: binary\r\n\r\n';
+ var multipartFooter = '\r\n--' + boundaryKey + '--';
+
+ req.setHeader('Content-Type', 'multipart/form-data; boundary="' + boundaryKey + '"');
+
+ // Convert files dict into more sutaibel format
+ var _files = [];
+ for (var name in files) {
+ _files.push({ key: name, path: files[name] });
+ }
+
+ // Find attachments total size
+ var attachmentsSize = 0;
+
+ // Headers size
+ function calculateHeadersSize() {
+ for (var i = 0, c = _files.length; i < c; i++) {
+ attachmentsSize += util.format(multipartHeader, _files[i].key, _files[i].key).length;
+ attachmentsSize += multipartFooter.length;
+ }
+
+ req.setHeader('Content-Length', attachmentsSize); // Set content length
+ writeFile(0); // Start writing
+ }
+
+ // Write file
+ function writeFile(index) {
+ if (index >= _files.length) {
+ req.end();
+ return;
+ }
- req.end();
+ var file = _files[index];
+ if (file) {
+ req.write(util.format(multipartHeader, file.key, file.key));
+
+ fs.createReadStream(file.path, { bufferSize: 4 * 1024 }).on('end', function() {
+ req.write(multipartFooter);
+ writeFile(++index);
+ }).pipe(req, { end: false });
+ }
+ }
+
+ // File sizes
+ var count = _files.length;
+ for (var i = 0, c = _files.length; i < c; i++) {
+ fs.lstat(_files[i].path, function(err, stats) {
+ attachmentsSize += stats.size;
+ count--;
+
+ if (count === 0) calculateHeadersSize();
+ });
+ }
+ } else {
+ if (body.length > 0) {
+ req.write(body);
+ }
+
+ req.end();
+ }
};
Adapter.prototype.getCredentials = function() {
@@ -294,7 +358,7 @@ Adapter.prototype.getRequestToken = function(callback) {
fields[AuthorizationHeader.Fields.CALLBACK] = this._options.callback;
// Acquire request token
- this._sendRequest(this._options.requestURL, 'POST', null, fields, this._credentials, function(error, data) {
+ this._sendRequest(this._options.requestURL, 'POST', null, null, fields, this._credentials, function(error, data) {
if (!error) {
var params = querystring.parse(data);
if (params['oauth_callback_confirmed'] === 'true') {
@@ -320,7 +384,7 @@ Adapter.prototype.getAccessToken = function(token, verifier, callback) {
fields[AuthorizationHeader.Fields.VERIFIER] = verifier;
// Acquire access token
- this._sendRequest(this._options.accessURL, 'POST', null, fields, this._credentials, function(error, data) {
+ this._sendRequest(this._options.accessURL, 'POST', null, null, fields, this._credentials, function(error, data) {
if (!error) {
var params = querystring.parse(data);
if (callback) {
@@ -341,7 +405,7 @@ Adapter.prototype.get = function(url, params, callback) {
fields[AuthorizationHeader.Fields.TOKEN] = this._credentials.client.token;
}
- this._sendRequest(url, 'GET', params, fields, this._credentials, function(error, data) {
+ this._sendRequest(url, 'GET', params, null, fields, this._credentials, function(error, data) {
if (!error) {
if (callback) {
callback(error, data);
@@ -354,14 +418,14 @@ Adapter.prototype.get = function(url, params, callback) {
});
};
-Adapter.prototype.post = function() {
+Adapter.prototype.post = function(url, params, files, callback) {
var fields = {};
if (this._credentials.client.token) {
fields[AuthorizationHeader.Fields.TOKEN] = this._credentials.client.token;
}
- this._sendRequest(url, 'POST', params, fields, this._credentials, function(error, data) {
+ this._sendRequest(url, 'POST', params, files, fields, this._credentials, function(error, data) {
if (!error) {
if (callback) {
callback(error, data);

0 comments on commit 7c184f2

Please sign in to comment.