Skip to content
Browse files

adding ability to no encode - in case the files are submitted directl…

…y from client, then the URL might have to be encoded on the client itself so the form can be submitted properly.
  • Loading branch information...
1 parent c8d7f92 commit 7b5b4a052b0e04308b3dd2d8323c2f25a084f7c8 @satb committed Jan 11, 2014
Showing with 458 additions and 442 deletions.
  1. +2 −0 .gitignore
  2. +456 −442 lib/client.js
View
2 .gitignore
@@ -1,3 +1,5 @@
/test/auth.json
/node_modules/
/npm-debug.log
+/knox.iml
+/.idea
View
898 lib/client.js
@@ -11,18 +11,18 @@
*/
var Emitter = require('events').EventEmitter
- , debug = require('debug')('knox')
- , utils = require('./utils')
- , auth = require('./auth')
- , http = require('http')
- , https = require('https')
- , url = require('url')
- , mime = require('mime')
- , fs = require('fs')
- , crypto = require('crypto')
- , xml2js = require('xml2js')
- , StreamCounter = require('stream-counter')
- , qs = require('querystring');
+ , debug = require('debug')('knox')
+ , utils = require('./utils')
+ , auth = require('./auth')
+ , http = require('http')
+ , https = require('https')
+ , url = require('url')
+ , mime = require('mime')
+ , fs = require('fs')
+ , crypto = require('crypto')
+ , xml2js = require('xml2js')
+ , StreamCounter = require('stream-counter')
+ , qs = require('querystring');
// The max for multi-object delete, bucket listings, etc.
var BUCKET_OPS_MAX = 1000;
@@ -46,119 +46,127 @@ var IPV4_ADDRESS = /^(\d{1,3}\.){3}(\d{1,3})$/;
* @api private
*/
function registerReqListeners(req, fn){
- var hasCalledBack = false;
+ var hasCalledBack = false;
- function cleanup() {
- hasCalledBack = true;
- req.removeListener('response', onResponse);
- req.removeListener('error', onError);
- }
+ function cleanup() {
+ hasCalledBack = true;
+ req.removeListener('response', onResponse);
+ req.removeListener('error', onError);
+ }
- function onResponse(res){
- if (!hasCalledBack) {
- cleanup();
- fn(null, res);
+ function onResponse(res){
+ if (!hasCalledBack) {
+ cleanup();
+ fn(null, res);
+ }
}
- }
- function onError(err){
- if (!hasCalledBack) {
- cleanup();
- fn(err);
+ function onError(err){
+ if (!hasCalledBack) {
+ cleanup();
+ fn(err);
+ }
}
- }
- req.on('response', onResponse);
- req.on('error', onError);
+ req.on('response', onResponse);
+ req.on('error', onError);
}
function ensureLeadingSlash(filename) {
- return filename[0] !== '/' ? '/' + filename : filename;
+ return filename[0] !== '/' ? '/' + filename : filename;
}
function removeLeadingSlash(filename) {
- return filename[0] === '/' ? filename.substring(1) : filename;
+ return filename[0] === '/' ? filename.substring(1) : filename;
}
-function encodeSpecialCharacters(filename) {
- // Note: these characters are valid in URIs, but S3 does not like them for
- // some reason.
- return encodeURIComponent(filename).replace(/[!'()* ]/g, function (char) {
- return '%' + char.charCodeAt(0).toString(16);
- });
+function encodeSpecialCharacters(filename, options) {
+ // Note: these characters are valid in URIs, but S3 does not like them for
+ // some reason.
+ options = options || {}
+ if(options['no_encoding'] ){
+ return filename.replace(/[!'()* ]/g, function (char) {
+ return '%' + char.charCodeAt(0).toString(16);
+ });
+ }else{
+ return encodeURI(filename).replace(/[!'()* ]/g, function (char) {
+ return '%' + char.charCodeAt(0).toString(16);
+ });
+ }
+
}
function getHeader(headers, headerNameLowerCase) {
- for (var header in headers) {
- if (header.toLowerCase() === headerNameLowerCase) {
- return headers[header];
+ for (var header in headers) {
+ if (header.toLowerCase() === headerNameLowerCase) {
+ return headers[header];
+ }
}
- }
- return null;
+ return null;
}
function isNotDnsCompliant(bucket) {
- if (bucket.length > MAX_NON_US_STANDARD_BUCKET_LENGTH) {
- return 'is more than ' + MAX_NON_US_STANDARD_BUCKET_LENGTH + ' characters';
- }
+ if (bucket.length > MAX_NON_US_STANDARD_BUCKET_LENGTH) {
+ return 'is more than ' + MAX_NON_US_STANDARD_BUCKET_LENGTH + ' characters';
+ }
- if (IPV4_ADDRESS.test(bucket)) {
- return 'is formatted as an IPv4 address';
- }
+ if (IPV4_ADDRESS.test(bucket)) {
+ return 'is formatted as an IPv4 address';
+ }
- var bucketLabels = bucket.split('.');
- var bucketLabelsAreValid = bucketLabels.every(function (label) {
- return BUCKET_LABEL.test(label);
- });
+ var bucketLabels = bucket.split('.');
+ var bucketLabelsAreValid = bucketLabels.every(function (label) {
+ return BUCKET_LABEL.test(label);
+ });
- if (!bucketLabelsAreValid) {
- return 'does not consist of valid period-separated labels';
- }
+ if (!bucketLabelsAreValid) {
+ return 'does not consist of valid period-separated labels';
+ }
- return false;
+ return false;
}
function isInvalid(bucket) {
- if (bucket.length < MIN_BUCKET_LENGTH) {
- return 'is less than ' + MIN_BUCKET_LENGTH + ' characters';
- }
- if (bucket.length > MAX_US_STANDARD_BUCKET_LENGTH) {
- return 'is more than ' + MAX_US_STANDARD_BUCKET_LENGTH + ' characters';
- }
-
- if (!US_STANDARD_BUCKET.test(bucket)) {
- return 'contains invalid characters';
- }
-
- return false;
+ if (bucket.length < MIN_BUCKET_LENGTH) {
+ return 'is less than ' + MIN_BUCKET_LENGTH + ' characters';
+ }
+ if (bucket.length > MAX_US_STANDARD_BUCKET_LENGTH) {
+ return 'is more than ' + MAX_US_STANDARD_BUCKET_LENGTH + ' characters';
+ }
+
+ if (!US_STANDARD_BUCKET.test(bucket)) {
+ return 'contains invalid characters';
+ }
+
+ return false;
}
function containsPeriod(bucket) {
- return bucket.indexOf('.') !== -1;
+ return bucket.indexOf('.') !== -1;
}
function autoDetermineStyle(options) {
- if (!options.style && options.secure !== false &&
- containsPeriod(options.bucket)) {
- options.style = 'path';
- return;
- }
-
- var dnsUncompliance = isNotDnsCompliant(options.bucket);
- if (dnsUncompliance) {
- if (options.style === 'virtualHosted') {
- throw new Error('Cannot use "virtualHosted" style with a ' +
- 'DNS-uncompliant bucket name: "' + options.bucket +
- '" is ' + dnsUncompliance + '.');
+ if (!options.style && options.secure !== false &&
+ containsPeriod(options.bucket)) {
+ options.style = 'path';
+ return;
}
- options.style = 'path';
- return;
- }
+ var dnsUncompliance = isNotDnsCompliant(options.bucket);
+ if (dnsUncompliance) {
+ if (options.style === 'virtualHosted') {
+ throw new Error('Cannot use "virtualHosted" style with a ' +
+ 'DNS-uncompliant bucket name: "' + options.bucket +
+ '" is ' + dnsUncompliance + '.');
+ }
+
+ options.style = 'path';
+ return;
+ }
- if (!options.style) {
- options.style = 'virtualHosted';
- }
+ if (!options.style) {
+ options.style = 'virtualHosted';
+ }
}
/**
@@ -170,13 +178,13 @@ function autoDetermineStyle(options) {
*/
function getCopyHeaders(sourceBucket, sourceFilename, headers) {
- sourceFilename = encodeSpecialCharacters(ensureLeadingSlash(sourceFilename));
- headers = utils.merge({
- Expect: '100-continue'
- }, headers || {});
- headers['x-amz-copy-source'] = '/' + sourceBucket + sourceFilename;
- headers['Content-Length'] = 0; // to avoid Node's automatic chunking if omitted
- return headers;
+ sourceFilename = encodeSpecialCharacters(ensureLeadingSlash(sourceFilename));
+ headers = utils.merge({
+ Expect: '100-continue'
+ }, headers || {});
+ headers['x-amz-copy-source'] = '/' + sourceBucket + sourceFilename;
+ headers['Content-Length'] = 0; // to avoid Node's automatic chunking if omitted
+ return headers;
}
@@ -194,74 +202,74 @@ function getCopyHeaders(sourceBucket, sourceFilename, headers) {
*/
var Client = module.exports = exports = function Client(options) {
- if (!options.key) throw new Error('aws "key" required');
- if (!options.secret) throw new Error('aws "secret" required');
- if (!options.bucket) throw new Error('aws "bucket" required');
+ if (!options.key) throw new Error('aws "key" required');
+ if (!options.secret) throw new Error('aws "secret" required');
+ if (!options.bucket) throw new Error('aws "bucket" required');
- if (options.style && options.style !== 'virtualHosted' &&
- options.style !== 'path') {
- throw new Error('style must be "virtualHosted" or "path"');
- }
+ if (options.style && options.style !== 'virtualHosted' &&
+ options.style !== 'path') {
+ throw new Error('style must be "virtualHosted" or "path"');
+ }
- if (options.port !== undefined && isNaN(parseInt(options.port))) {
- throw new Error('port must be a number.');
- }
+ if (options.port !== undefined && isNaN(parseInt(options.port))) {
+ throw new Error('port must be a number.');
+ }
+
+ var invalidness = isInvalid(options.bucket);
+ var dnsUncompliance = isNotDnsCompliant(options.bucket);
- var invalidness = isInvalid(options.bucket);
- var dnsUncompliance = isNotDnsCompliant(options.bucket);
+ if (invalidness) {
+ throw new Error('Bucket name "' + options.bucket + '" ' + invalidness + '.');
+ }
- if (invalidness) {
- throw new Error('Bucket name "' + options.bucket + '" ' + invalidness + '.');
- }
+ // Save original options, we will need them for Client#copyTo
+ this.options = utils.merge({}, options);
+
+ // Make sure we don't override options the user passes in.
+ options = utils.merge({}, options);
+ autoDetermineStyle(options);
+
+ if (!options.endpoint) {
+ if (!options.region) {
+ options.endpoint = 's3.amazonaws.com';
+ options.region = 'us-standard';
+ } else {
+ options.endpoint = options.region === 'us-standard' ?
+ 's3.amazonaws.com' :
+ 's3-' + options.region + '.amazonaws.com';
+ }
- // Save original options, we will need them for Client#copyTo
- this.options = utils.merge({}, options);
+ if (options.region !== 'us-standard') {
+ if (dnsUncompliance) {
+ throw new Error('Outside of the us-standard region, bucket names must' +
+ ' be DNS-compliant. The name "' + options.bucket +
+ '" ' + dnsUncompliance + '.');
+ }
+ }
+ } else {
+ options.region = undefined;
+ }
- // Make sure we don't override options the user passes in.
- options = utils.merge({}, options);
- autoDetermineStyle(options);
+ var portSuffix = 'undefined' == typeof options.port ? "" : ":" + options.port;
+ this.secure = 'undefined' == typeof options.port;
- if (!options.endpoint) {
- if (!options.region) {
- options.endpoint = 's3.amazonaws.com';
- options.region = 'us-standard';
+ if (options.style === 'virtualHosted') {
+ this.host = options.bucket + '.' + options.endpoint;
+ this.urlBase = options.bucket + '.' + options.endpoint + portSuffix;
} else {
- options.endpoint = options.region === 'us-standard' ?
- 's3.amazonaws.com' :
- 's3-' + options.region + '.amazonaws.com';
- }
-
- if (options.region !== 'us-standard') {
- if (dnsUncompliance) {
- throw new Error('Outside of the us-standard region, bucket names must' +
- ' be DNS-compliant. The name "' + options.bucket +
- '" ' + dnsUncompliance + '.');
- }
- }
- } else {
- options.region = undefined;
- }
-
- var portSuffix = 'undefined' == typeof options.port ? "" : ":" + options.port;
- this.secure = 'undefined' == typeof options.port;
-
- if (options.style === 'virtualHosted') {
- this.host = options.bucket + '.' + options.endpoint;
- this.urlBase = options.bucket + '.' + options.endpoint + portSuffix;
- } else {
- this.host = options.endpoint;
- this.urlBase = options.endpoint + portSuffix + '/' + options.bucket;
- }
-
- // HTTP in Node.js < 0.12 is horribly broken, and leads to lots of "socket
- // hang up" errors: https://github.com/LearnBoost/knox/issues/116. See
- // https://github.com/LearnBoost/knox/issues/116#issuecomment-15045187 and
- // https://github.com/substack/hyperquest#rant
- this.agent = false;
-
- utils.merge(this, options);
-
- this.url = this.secure ? this.https : this.http;
+ this.host = options.endpoint;
+ this.urlBase = options.endpoint + portSuffix + '/' + options.bucket;
+ }
+
+ // HTTP in Node.js < 0.12 is horribly broken, and leads to lots of "socket
+ // hang up" errors: https://github.com/LearnBoost/knox/issues/116. See
+ // https://github.com/LearnBoost/knox/issues/116#issuecomment-15045187 and
+ // https://github.com/substack/hyperquest#rant
+ this.agent = false;
+
+ utils.merge(this, options);
+
+ this.url = this.secure ? this.https : this.http;
};
/**
@@ -270,48 +278,50 @@ var Client = module.exports = exports = function Client(options) {
* @param {String} method
* @param {String} filename
* @param {Object} headers
+ * @param {Object} otherOptions
* @return {ClientRequest}
* @api private
*/
-Client.prototype.request = function(method, filename, headers){
- var options = { hostname: this.host, agent: this.agent, port: this.port }
- , date = new Date
- , headers = headers || {}
- , fixedFilename = encodeSpecialCharacters(ensureLeadingSlash(filename));
-
- // Default headers
- headers.Date = date.toUTCString()
- if (this.style === 'virtualHosted') {
- headers.Host = this.host;
- }
-
- if ('undefined' != typeof this.token)
- headers['x-amz-security-token'] = this.token;
-
- // Authorization header
- headers.Authorization = auth.authorization({
- key: this.key
- , secret: this.secret
- , verb: method
- , date: date
- , resource: auth.canonicalizeResource('/' + this.bucket + fixedFilename)
- , contentType: getHeader(headers, 'content-type')
- , md5: getHeader(headers, 'content-md5') || ''
- , amazonHeaders: auth.canonicalizeHeaders(headers)
- });
-
- var pathPrefix = this.style === 'path' ? '/' + this.bucket : '';
-
- // Issue request
- options.method = method;
- options.path = pathPrefix + fixedFilename;
- options.headers = headers;
- var req = (this.secure ? https : http).request(options);
- req.url = this.url(filename);
- debug('%s %s', method, req.url);
-
- return req;
+Client.prototype.request = function(method, filename, headers, otherOptions){
+ var options = { hostname: this.host, agent: this.agent, port: this.port }
+ , date = new Date;
+
+ headers = headers || {};
+ var fixedFilename = (otherOptions && otherOptions['no_encoding']) ? ensureLeadingSlash(filename) : encodeSpecialCharacters(ensureLeadingSlash(filename));
+
+ // Default headers
+ headers.Date = date.toUTCString()
+ if (this.style === 'virtualHosted') {
+ headers.Host = this.host;
+ }
+
+ if ('undefined' != typeof this.token)
+ headers['x-amz-security-token'] = this.token;
+
+ // Authorization header
+ headers.Authorization = auth.authorization({
+ key: this.key
+ , secret: this.secret
+ , verb: method
+ , date: date
+ , resource: auth.canonicalizeResource('/' + this.bucket + fixedFilename)
+ , contentType: getHeader(headers, 'content-type')
+ , md5: getHeader(headers, 'content-md5') || ''
+ , amazonHeaders: auth.canonicalizeHeaders(headers)
+ });
+
+ var pathPrefix = this.style === 'path' ? '/' + this.bucket : '';
+
+ // Issue request
+ options.method = method;
+ options.path = pathPrefix + fixedFilename;
+ options.headers = headers;
+ var req = (this.secure ? https : http).request(options);
+ req.url = this.url(filename, otherOptions);
+ debug('%s %s', method, req.url);
+
+ return req;
};
/**
@@ -345,10 +355,10 @@ Client.prototype.request = function(method, filename, headers){
*/
Client.prototype.put = function(filename, headers){
- headers = utils.merge({
- Expect: '100-continue'
- }, headers || {});
- return this.request('PUT', filename, headers);
+ headers = utils.merge({
+ Expect: '100-continue'
+ }, headers || {});
+ return this.request('PUT', filename, headers);
};
/**
@@ -373,51 +383,51 @@ Client.prototype.put = function(filename, headers){
*/
Client.prototype.putFile = function(src, filename, headers, fn){
- var self = this;
- var emitter = new Emitter;
-
- if ('function' == typeof headers) {
- fn = headers;
- headers = {};
- }
-
- debug('put %s', src);
- fs.stat(src, function (err, stat) {
- if (err) return fn(err);
+ var self = this;
+ var emitter = new Emitter;
- var contentType = mime.lookup(src);
-
- // Add charset if it's known.
- var charset = mime.charsets.lookup(contentType);
- if (charset) {
- contentType += '; charset=' + charset;
+ if ('function' == typeof headers) {
+ fn = headers;
+ headers = {};
}
- headers = utils.merge({
- 'Content-Length': stat.size
- , 'Content-Type': contentType
- }, headers);
+ debug('put %s', src);
+ fs.stat(src, function (err, stat) {
+ if (err) return fn(err);
- var stream = fs.createReadStream(src);
+ var contentType = mime.lookup(src);
- var fnCalled = false;
- function wrappedFn(err, result) {
- if (fnCalled) {
- if (err) {
- emitter.emit('error', err);
+ // Add charset if it's known.
+ var charset = mime.charsets.lookup(contentType);
+ if (charset) {
+ contentType += '; charset=' + charset;
}
- } else {
- fnCalled = true;
- fn(err, result);
- }
- }
- var req = self.putStream(stream, filename, headers, wrappedFn);
- req.on('progress', emitter.emit.bind(emitter, 'progress'));
- req.on('error', wrappedFn);
- });
+ headers = utils.merge({
+ 'Content-Length': stat.size
+ , 'Content-Type': contentType
+ }, headers);
+
+ var stream = fs.createReadStream(src);
+
+ var fnCalled = false;
+ function wrappedFn(err, result) {
+ if (fnCalled) {
+ if (err) {
+ emitter.emit('error', err);
+ }
+ } else {
+ fnCalled = true;
+ fn(err, result);
+ }
+ }
+ var req = self.putStream(stream, filename, headers, wrappedFn);
+
+ req.on('progress', emitter.emit.bind(emitter, 'progress'));
+ req.on('error', wrappedFn);
+ });
- return emitter;
+ return emitter;
};
/**
@@ -433,33 +443,33 @@ Client.prototype.putFile = function(src, filename, headers, fn){
*/
Client.prototype.putStream = function(stream, filename, headers, fn){
- var contentLength = getHeader(headers, 'content-length');
- if (contentLength === null) {
- process.nextTick(function () {
- fn(new Error('You must specify a Content-Length header.'));
- });
- return;
- }
+ var contentLength = getHeader(headers, 'content-length');
+ if (contentLength === null) {
+ process.nextTick(function () {
+ fn(new Error('You must specify a Content-Length header.'));
+ });
+ return;
+ }
- var self = this;
- var req = self.put(filename, headers);
+ var self = this;
+ var req = self.put(filename, headers);
- registerReqListeners(req, fn);
- stream.on('error', fn);
+ registerReqListeners(req, fn);
+ stream.on('error', fn);
- var counter = new StreamCounter();
- counter.on('progress', function(){
- req.emit('progress', {
- percent: counter.bytes / contentLength * 100 | 0
- , written: counter.bytes
- , total: contentLength
+ var counter = new StreamCounter();
+ counter.on('progress', function(){
+ req.emit('progress', {
+ percent: counter.bytes / contentLength * 100 | 0
+ , written: counter.bytes
+ , total: contentLength
+ });
});
- });
- stream.pipe(counter);
- stream.pipe(req);
- return req;
+ stream.pipe(counter);
+ stream.pipe(req);
+ return req;
};
/**
@@ -475,17 +485,17 @@ Client.prototype.putStream = function(stream, filename, headers, fn){
*/
Client.prototype.putBuffer = function(buffer, filename, headers, fn){
- if ('function' == typeof headers) {
- fn = headers;
- headers = {};
- }
+ if ('function' == typeof headers) {
+ fn = headers;
+ headers = {};
+ }
- headers['Content-Length'] = buffer.length;
+ headers['Content-Length'] = buffer.length;
- var req = this.put(filename, headers);
- registerReqListeners(req, fn);
- req.end(buffer);
- return req;
+ var req = this.put(filename, headers);
+ registerReqListeners(req, fn);
+ req.end(buffer);
+ return req;
};
/**
@@ -499,7 +509,7 @@ Client.prototype.putBuffer = function(buffer, filename, headers, fn){
*/
Client.prototype.copy = function(sourceFilename, destFilename, headers){
- return this.put(destFilename, getCopyHeaders(this.bucket, sourceFilename, headers));
+ return this.put(destFilename, getCopyHeaders(this.bucket, sourceFilename, headers));
};
/**
@@ -514,15 +524,15 @@ Client.prototype.copy = function(sourceFilename, destFilename, headers){
*/
Client.prototype.copyFile = function(sourceFilename, destFilename, headers, fn){
- if ('function' == typeof headers) {
- fn = headers;
- headers = {};
- }
-
- var req = this.copy(sourceFilename, destFilename, headers);
- registerReqListeners(req, fn);
- req.end();
- return req;
+ if ('function' == typeof headers) {
+ fn = headers;
+ headers = {};
+ }
+
+ var req = this.copy(sourceFilename, destFilename, headers);
+ registerReqListeners(req, fn);
+ req.end();
+ return req;
};
/**
@@ -538,14 +548,14 @@ Client.prototype.copyFile = function(sourceFilename, destFilename, headers, fn){
*/
Client.prototype.copyTo = function(sourceFilename, destBucket, destFilename, headers){
- var options = utils.merge({}, this.options);
- if (typeof destBucket == 'string') {
- options.bucket = destBucket;
- } else {
- utils.merge(options, destBucket);
- }
- var client = exports.createClient(options);
- return client.put(destFilename, getCopyHeaders(this.bucket, sourceFilename, headers));
+ var options = utils.merge({}, this.options);
+ if (typeof destBucket == 'string') {
+ options.bucket = destBucket;
+ } else {
+ utils.merge(options, destBucket);
+ }
+ var client = exports.createClient(options);
+ return client.put(destFilename, getCopyHeaders(this.bucket, sourceFilename, headers));
};
/**
@@ -561,28 +571,29 @@ Client.prototype.copyTo = function(sourceFilename, destBucket, destFilename, hea
*/
Client.prototype.copyFileTo = function(sourceFilename, destBucket, destFilename, headers, fn){
- if ('function' == typeof headers) {
- fn = headers;
- headers = {};
- }
-
- var req = this.copyTo(sourceFilename, destBucket, destFilename, headers);
- registerReqListeners(req, fn);
- req.end();
- return req;
+ if ('function' == typeof headers) {
+ fn = headers;
+ headers = {};
+ }
+
+ var req = this.copyTo(sourceFilename, destBucket, destFilename, headers);
+ registerReqListeners(req, fn);
+ req.end();
+ return req;
};
/**
* GET `filename` with optional `headers`.
*
* @param {String} filename
* @param {Object} headers
+ * @param {Object} options
* @return {ClientRequest}
* @api public
*/
-Client.prototype.get = function(filename, headers){
- return this.request('GET', filename, headers);
+Client.prototype.get = function(filename, headers, options){
+ return this.request('GET', filename, headers, options);
};
/**
@@ -596,15 +607,15 @@ Client.prototype.get = function(filename, headers){
*/
Client.prototype.getFile = function(filename, headers, fn){
- if ('function' == typeof headers) {
- fn = headers;
- headers = {};
- }
-
- var req = this.get(filename, headers);
- registerReqListeners(req, fn);
- req.end();
- return req;
+ if ('function' == typeof headers) {
+ fn = headers;
+ headers = {};
+ }
+
+ var req = this.get(filename, headers);
+ registerReqListeners(req, fn);
+ req.end();
+ return req;
};
/**
@@ -617,7 +628,7 @@ Client.prototype.getFile = function(filename, headers, fn){
*/
Client.prototype.head = function(filename, headers){
- return this.request('HEAD', filename, headers);
+ return this.request('HEAD', filename, headers);
};
/**
@@ -631,14 +642,14 @@ Client.prototype.head = function(filename, headers){
*/
Client.prototype.headFile = function(filename, headers, fn){
- if ('function' == typeof headers) {
- fn = headers;
- headers = {};
- }
- var req = this.head(filename, headers);
- registerReqListeners(req, fn);
- req.end();
- return req;
+ if ('function' == typeof headers) {
+ fn = headers;
+ headers = {};
+ }
+ var req = this.head(filename, headers);
+ registerReqListeners(req, fn);
+ req.end();
+ return req;
};
/**
@@ -651,7 +662,7 @@ Client.prototype.headFile = function(filename, headers, fn){
*/
Client.prototype.del = function(filename, headers){
- return this.request('DELETE', filename, headers);
+ return this.request('DELETE', filename, headers);
};
/**
@@ -665,31 +676,31 @@ Client.prototype.del = function(filename, headers){
*/
Client.prototype.deleteFile = function(filename, headers, fn){
- if ('function' == typeof headers) {
- fn = headers;
- headers = {};
- }
- var req = this.del(filename, headers);
- registerReqListeners(req, fn);
- req.end();
+ if ('function' == typeof headers) {
+ fn = headers;
+ headers = {};
+ }
+ var req = this.del(filename, headers);
+ registerReqListeners(req, fn);
+ req.end();
};
function xmlEscape(string) {
- return string
- .replace(/&/g, '&amp;')
- .replace(/</g, '&lt;')
- .replace(/>/g, '&gt;')
- .replace(/"/g, '&quot;');
+ return string
+ .replace(/&/g, '&amp;')
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;')
+ .replace(/"/g, '&quot;');
}
function makeDeleteXmlBuffer(keys) {
var tags = keys.map(function(key){
- return '<Object><Key>' +
- xmlEscape(removeLeadingSlash(key)) +
- '</Key></Object>';
+ return '<Object><Key>' +
+ xmlEscape(removeLeadingSlash(key)) +
+ '</Key></Object>';
});
return new Buffer('<?xml version="1.0" encoding="UTF-8"?>' +
- '<Delete>' + tags.join('') + '</Delete>', 'utf8');
+ '<Delete>' + tags.join('') + '</Delete>', 'utf8');
}
/**
@@ -703,29 +714,29 @@ function makeDeleteXmlBuffer(keys) {
*/
Client.prototype.deleteMultiple = function(filenames, headers, fn){
- if (filenames.length > BUCKET_OPS_MAX) {
- throw new Error('Can only delete up to ' + BUCKET_OPS_MAX + ' files ' +
- 'at a time. You\'ll need to batch them.');
- }
-
- if ('function' == typeof headers) {
- fn = headers;
- headers = {};
- }
-
- var xml = makeDeleteXmlBuffer(filenames);
-
- headers['Content-Length'] = xml.length;
- headers['Content-MD5'] = crypto.createHash('md5').update(xml).digest('base64');
-
- return this.request('POST', '/?delete', headers)
- .on('response', function(res){
- fn(null, res);
- })
- .on('error', function(err){
- fn(err);
- })
- .end(xml);
+ if (filenames.length > BUCKET_OPS_MAX) {
+ throw new Error('Can only delete up to ' + BUCKET_OPS_MAX + ' files ' +
+ 'at a time. You\'ll need to batch them.');
+ }
+
+ if ('function' == typeof headers) {
+ fn = headers;
+ headers = {};
+ }
+
+ var xml = makeDeleteXmlBuffer(filenames);
+
+ headers['Content-Length'] = xml.length;
+ headers['Content-MD5'] = crypto.createHash('md5').update(xml).digest('base64');
+
+ return this.request('POST', '/?delete', headers)
+ .on('response', function(res){
+ fn(null, res);
+ })
+ .on('error', function(err){
+ fn(err);
+ })
+ .end(xml);
};
/**
@@ -736,9 +747,9 @@ Client.prototype.deleteMultiple = function(filenames, headers, fn){
var LIST_PARAMS = {
delimiter: true
- , marker: true
- ,'max-keys': true
- , prefix: true
+ , marker: true
+ ,'max-keys': true
+ , prefix: true
};
/**
@@ -764,29 +775,29 @@ var RESPONSE_NORMALIZATION = {
*/
function normalizeResponse(data) {
- for (var key in data) {
- var Constr = RESPONSE_NORMALIZATION[key];
-
- if (Constr) {
- if (Constr === Date) {
- data[key] = new Date(data[key]);
- } else if (Constr === Array) {
- // If there's only one element in the array xml2js doesn't know that
- // it should be an array; array-ify it.
- if (!Array.isArray(data[key])) {
- data[key] = [data[key]];
+ for (var key in data) {
+ var Constr = RESPONSE_NORMALIZATION[key];
+
+ if (Constr) {
+ if (Constr === Date) {
+ data[key] = new Date(data[key]);
+ } else if (Constr === Array) {
+ // If there's only one element in the array xml2js doesn't know that
+ // it should be an array; array-ify it.
+ if (!Array.isArray(data[key])) {
+ data[key] = [data[key]];
+ }
+ } else if (Constr === Boolean) {
+ data[key] = data[key] === 'true';
+ } else {
+ data[key] = Constr(data[key]);
+ }
}
- } else if (Constr === Boolean) {
- data[key] = data[key] === 'true';
- } else {
- data[key] = Constr(data[key]);
- }
- }
- if (Array.isArray(data[key])) {
- data[key].forEach(normalizeResponse);
+ if (Array.isArray(data[key])) {
+ data[key].forEach(normalizeResponse);
+ }
}
- }
}
/**
@@ -800,78 +811,80 @@ function normalizeResponse(data) {
*/
Client.prototype.list = function(params, headers, fn){
- if ('function' == typeof headers) {
- fn = headers;
- headers = {};
- }
+ if ('function' == typeof headers) {
+ fn = headers;
+ headers = {};
+ }
- if ('function' == typeof params) {
- fn = params;
- params = null;
- }
+ if ('function' == typeof params) {
+ fn = params;
+ params = null;
+ }
- if (params && !LIST_PARAMS[Object.keys(params)[0]]) {
- headers = params;
- params = null;
- }
+ if (params && !LIST_PARAMS[Object.keys(params)[0]]) {
+ headers = params;
+ params = null;
+ }
- // Stringify the list params but don't encode them yet, since ::request
- // already encodes the value, leading to double-encoding issues
- var url = params ? '?' + decodeURIComponent(qs.stringify(params)) : '';
+ // Stringify the list params but don't encode them yet, since ::request
+ // already encodes the value, leading to double-encoding issues
+ var url = params ? '?' + decodeURIComponent(qs.stringify(params)) : '';
- return this.getFile(url, headers, function(err, res){
- if (err) return fn(err);
+ return this.getFile(url, headers, function(err, res){
+ if (err) return fn(err);
- var xmlStr = '';
+ var xmlStr = '';
- res.on('data', function(chunk){
- xmlStr += chunk;
- });
+ res.on('data', function(chunk){
+ xmlStr += chunk;
+ });
- res.on('end', function(){
- new xml2js.Parser({explicitArray: false, explicitRoot: false})
- .parseString(xmlStr, function(err, data){
- if (err) return fn(err);
+ res.on('end', function(){
+ new xml2js.Parser({explicitArray: false, explicitRoot: false})
+ .parseString(xmlStr, function(err, data){
+ if (err) return fn(err);
- delete data.$;
- normalizeResponse(data);
+ delete data.$;
+ normalizeResponse(data);
- if (!('Contents' in data)) {
- data.Contents = [];
- }
+ if (!('Contents' in data)) {
+ data.Contents = [];
+ }
- fn(null, data);
- });
- }).on('error', fn);
- });
+ fn(null, data);
+ });
+ }).on('error', fn);
+ });
};
/**
* Return a url to the given `filename`.
*
* @param {String} filename
+ * @param {Object} options
* @return {String}
* @api public
*/
-Client.prototype.http = function(filename){
- filename = encodeSpecialCharacters(ensureLeadingSlash(filename));
+Client.prototype.http = function(filename, options){
+ filename = encodeSpecialCharacters(ensureLeadingSlash(filename), options);
- return 'http://' + this.urlBase + filename;
+ return 'http://' + this.urlBase + filename;
};
/**
* Return an HTTPS url to the given `filename`.
*
* @param {String} filename
+ * @param {Object} options
* @return {String}
* @api public
*/
-Client.prototype.https = function(filename){
- filename = encodeSpecialCharacters(ensureLeadingSlash(filename));
+Client.prototype.https = function(filename, options){
+ filename = encodeSpecialCharacters(ensureLeadingSlash(filename), options);
- return 'https://' + this.urlBase + filename;
+ return 'https://' + this.urlBase + filename;
};
/**
@@ -885,33 +898,33 @@ Client.prototype.https = function(filename){
*/
Client.prototype.signedUrl = function(filename, expiration, options){
- var epoch = Math.floor(expiration.getTime()/1000)
- , pathname = url.parse(filename).pathname
- , resource = '/' + this.bucket + ensureLeadingSlash(pathname);
-
- if (options && options.qs) {
- resource += '?' + decodeURIComponent(qs.stringify(options.qs));
- }
-
- var signature = auth.signQuery({
- secret: this.secret
- , date: epoch
- , resource: resource
- , verb: (options && options.verb) || 'GET'
- , contentType: options && options.contentType
- , token: this.token
- });
-
- var queryString = qs.stringify(utils.merge({
- Expires: epoch,
- AWSAccessKeyId: this.key,
- Signature: signature
- }, (options && options.qs) || {}));
-
- if (typeof this.token !== 'undefined')
- queryString += '&x-amz-security-token=' + encodeURIComponent(this.token);
-
- return this.url(filename) + '?' + queryString;
+ var epoch = Math.floor(expiration.getTime()/1000)
+ , pathname = url.parse(filename).pathname
+ , resource = '/' + this.bucket + ensureLeadingSlash(pathname);
+
+ if (options && options.qs) {
+ resource += '?' + decodeURIComponent(qs.stringify(options.qs));
+ }
+
+ var signature = auth.signQuery({
+ secret: this.secret
+ , date: epoch
+ , resource: resource
+ , verb: (options && options.verb) || 'GET'
+ , contentType: options && options.contentType
+ , token: this.token
+ });
+
+ var queryString = qs.stringify(utils.merge({
+ Expires: epoch,
+ AWSAccessKeyId: this.key,
+ Signature: signature
+ }, (options && options.qs) || {}));
+
+ if (typeof this.token !== 'undefined')
+ queryString += '&x-amz-security-token=' + encodeURIComponent(this.token);
+
+ return this.url(filename) + '?' + queryString;
};
/**
@@ -923,5 +936,6 @@ Client.prototype.signedUrl = function(filename, expiration, options){
*/
exports.createClient = function(options){
- return new Client(options);
+ return new Client(options);
};
+

0 comments on commit 7b5b4a0

Please sign in to comment.
Something went wrong with that request. Please try again.