From 13312680d9a1263ad4a6be38ccf1335302f3a771 Mon Sep 17 00:00:00 2001 From: Jan Scheurer Date: Wed, 13 Jul 2016 02:34:48 +0200 Subject: [PATCH] Make sure headers are always lowercase to avoid duplicate headers Some methods inject headers like 'Content-Length' or 'Content-Type', however in cases where the user provides headers that are not the exact same casing(eg. 'Content-type' instead of 'Content-Type') both headers were sent, in cases where their content did not match this resulted in S3 resetting the connection without a clear error message. --- lib/client.js | 46 +++++++++++++++++++++++++++++----------------- lib/utils.js | 18 ++++++++++++++++++ 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/lib/client.js b/lib/client.js index ea2b3ed..b6a70ab 100644 --- a/lib/client.js +++ b/lib/client.js @@ -152,9 +152,9 @@ function autoDetermineStyle(options) { function getCopyHeaders(sourceBucket, sourceFilename, headers) { sourceFilename = encodeSpecialCharacters(ensureLeadingSlash(sourceFilename)); - headers = utils.merge({}, headers || {}); + headers = headers ? utils.lowercase({}, headers) : {}; headers['x-amz-copy-source'] = '/' + sourceBucket + sourceFilename; - headers['Content-Length'] = 0; // to avoid Node's automatic chunking if omitted + headers['content-length'] = 0; // to avoid Node's automatic chunking if omitted return headers; } @@ -322,7 +322,7 @@ Client.prototype.request = function(method, filename, headers){ */ Client.prototype.put = function(filename, headers){ - headers = utils.merge({}, headers || {}); + headers = headers ? utils.lowercase({}, headers) : {}; return this.request('PUT', encodeSpecialCharacters(filename), headers); }; @@ -354,7 +354,8 @@ Client.prototype.putFile = function(src, filename, headers, fn){ if ('function' == typeof headers) { fn = headers; headers = {}; - } + } else + headers = utils.lowercase({}, headers); debug('put %s', src); fs.stat(src, function (err, stat) { @@ -369,8 +370,8 @@ Client.prototype.putFile = function(src, filename, headers, fn){ } headers = utils.merge({ - 'Content-Length': stat.size - , 'Content-Type': contentType + 'content-length': stat.size + , 'content-type': contentType }, headers); var stream = fs.createReadStream(src); @@ -441,9 +442,10 @@ Client.prototype.putBuffer = function(buffer, filename, headers, fn){ if ('function' == typeof headers) { fn = headers; headers = {}; - } + } else + headers = utils.lowercase({}, headers); - headers['Content-Length'] = buffer.length; + headers['content-length'] = buffer.length; var req = this.put(filename, headers); fn = once(fn); @@ -481,7 +483,8 @@ Client.prototype.copyFile = function(sourceFilename, destFilename, headers, fn){ if ('function' == typeof headers) { fn = headers; headers = {}; - } + } else + headers = utils.lowercase({}, headers); var req = this.copy(sourceFilename, destFilename, headers); fn = once(fn); @@ -529,7 +532,8 @@ Client.prototype.copyFileTo = function(sourceFilename, destBucket, destFilename, if ('function' == typeof headers) { fn = headers; headers = {}; - } + } else + headers = utils.lowercase({}, headers); var req = this.copyTo(sourceFilename, destBucket, destFilename, headers); fn = once(fn); @@ -565,7 +569,8 @@ Client.prototype.getFile = function(filename, headers, fn){ if ('function' == typeof headers) { fn = headers; headers = {}; - } + } else + headers = utils.lowercase({}, headers); var req = this.get(filename, headers); registerReqListeners(req, fn); @@ -600,7 +605,9 @@ Client.prototype.headFile = function(filename, headers, fn){ if ('function' == typeof headers) { fn = headers; headers = {}; - } + } else + headers = utils.lowercase({}, headers); + var req = this.head(filename, headers); fn = once(fn); registerReqListeners(req, fn); @@ -635,7 +642,9 @@ Client.prototype.deleteFile = function(filename, headers, fn){ if ('function' == typeof headers) { fn = headers; headers = {}; - } + } else + headers = utils.lowercase({}, headers); + var req = this.del(filename, headers); fn = once(fn); registerReqListeners(req, fn); @@ -680,12 +689,13 @@ Client.prototype.deleteMultiple = function(filenames, headers, fn){ if ('function' == typeof headers) { fn = headers; headers = {}; - } + } else + headers = utils.lowercase({}, headers); var xml = makeDeleteXmlBuffer(filenames); - headers['Content-Length'] = xml.length; - headers['Content-MD5'] = crypto.createHash('md5').update(xml).digest('base64'); + headers['content-length'] = xml.length; + headers['content-md5'] = crypto.createHash('md5').update(xml).digest('base64'); var req = this.request('POST', '/?delete', headers); fn = once(fn); @@ -769,7 +779,9 @@ Client.prototype.list = function(params, headers, fn){ if ('function' == typeof headers) { fn = headers; headers = {}; - } + } else + headers = utils.lowercase({}, headers); + if ('function' == typeof params) { fn = params; diff --git a/lib/utils.js b/lib/utils.js index 9d8a3e9..d10b429 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -24,6 +24,24 @@ exports.merge = function(a, b){ return a; }; +/** + * Transform all object properties to lowercase + * + * @param {Object} out + * @param {Object} input + * @return {Object} out + * @api private + */ + +exports.lowercase = function (out, input) { + var keys = Object.keys(input); + for (var i = 0, len = keys.length; i < len; ++i) { + var key = keys[i]; + out[key.toLowerCase()] = input[key]; + } + return out; +} + /** * Base64. */