UNPKG

core

Version:

Core Libs

393 lines (345 loc) 10.8 kB
var Response = require("../http/Response.js").Response; var knox = require('knox'); var xml2js = require('xml2js'); /** * @class S3Client * @classdesc S3 client * @param {Object} configuration S3 connection configuration * @param {Object} configuration.key access key ID * @param {Object} configuration.secret secret access key * @param {Object} configuration.bucket bucket * @param {Object} options connection options * @param {Object} options.timeout timeout before aborting request * @param {Object} options.retry number of retries when connection problem occurs */ var S3Client = function (configuration, options) { this.retry = options.retry || 0; this.timeout = options.timeout || 10000; /* odwolanie przez bucket */ configuration.style = 'path'; this._client = knox.createClient(configuration); }; /** * Callback fired after receiving response from server. * * When connection error occured {error} object will be passed as first parameter. * * @callback S3Client~callback * @param {error} error * @param {response} response Response object */ /** * Callback fired after receiving response from server. * * When connection error occured {error} object will be passed as first parameter. * * @callback S3Client~callbackAcl * @param {error} error * @param {object} acl requested object's ACLs */ /** * Adds object to S3 * * @param {string} key key name of the object * @param {object} params object properties * @param {buffer|string} params.body object content * @param {object} params.headers headers * @param {string} params.acl ACL applied to object * @param {S3Client~callback} callback Called when response from the server is returned. */ S3Client.prototype.set = function (key, params, callback) { var headers = params.headers; var body = params.body; if (!body) { return callback(this._getError(S3Client.Error.ENOBODY), null); } if (!key) { return callback(this._getError(S3Client.Error.ENOKEY), null); } if (!headers) { return callback(this._getError(S3Client.Error.ENOHEADERS), null); } if (params.acl) { headers['x-amz-acl'] = params.acl; } this._spawnFunction(this._set.bind(this, key, headers, body), callback); }; /** * Retrieves object from S3. * * When retry count specified, callback is called once after all tries. * * @param {string} key * @param {S3Client~callback} callback Called when response from the server is returned. */ S3Client.prototype.get = function (key, params, callback) { params = params || {}; var headers = {}; if (params.range) { headers.range = params.range; } this._spawnFunction(this._get.bind(this, key, headers), callback); }; /** * Retrieves headers of object from S3. * * When retry count specified, callback is called once after all tries. * * @param {string} key * @param {S3Client~callback} callback Called when response from the server is returned. */ S3Client.prototype.head = function (key, headers, callback) { this._spawnFunction(this._head.bind(this, key, headers), callback); }; /** * Removes object from S3. * * When retry count specified, callback is called once after all tries. * * @param {string} key * @param {S3Client~callback} callback Called when response from the server is returned. */ S3Client.prototype.del = function (key, callback) { this._spawnFunction(this._del.bind(this, key), callback); }; /** * Copy object in S3 around the same bucket. * * When retry count specified, callback is called once after all tries. * * @param {string} keySrc * @param {string} keyDst * @param {string} headers * @param {S3Client~callback} callback Called when response from the server is returned. */ S3Client.prototype.copy = function (keySrc, keyDst, headers, callback) { this._spawnFunction(this._copy.bind(this, keySrc, keyDst, headers), callback); }; /** * List object in S3 bucket. * * When retry count specified, callback is called once after all tries. * * @param {string} params * @param {string} headers * @param {S3Client~callback} callback Called when response from the server is returned. */ S3Client.prototype.list = function (params, headers, callback) { this._client.list(params, headers, callback); }; /** * Retrieves object's ACL. * * When retry count specified, callback is called once after all tries. * * @param {type} key * @param {S3Client~callbackAcl} callback Called when response from the server is returned. */ S3Client.prototype.getAcl = function (key, callback) { key = key + '?acl'; this._spawnFunction(this._get.bind(this, key, null), function (err, response) { if (err) { return callback(err, response); } if (response.getStatusCode() != 200) { var error = new Error('Error fetching ACLs - status code: ' + response.getStatusCode()); return callback(error, null); } var xml = response.getBody().toBuffer().toString(); xml2js.parseString(xml, {explicitArray: false}, function (err, acl) { if (err) { var error = new Error('Error parsing XML response'); return callback(error, null); } callback(null, acl.AccessControlPolicy); }); }); }; /** * Wraps knox request and manages request events * * @private * @param {string} key * @param {Object} headers * @param {Buffer} body * @param {function} callback */ S3Client.prototype._set = function (key, headers, body, callback) { var request = this._client.put(key, headers, body); if (this.timeout) { request.setTimeout(this.timeout); } request.on('response', this._handleResponse.bind(this, callback)); request.on('error', function (error) { callback(error, null); }); request.once('timeout', function () { request.abort(); }); request.end(body); }; /** * Wraps knox request and manages request events * * @private * @param {string} keySrc * @param {Object} keyDst * @param {Buffer} headers * @param {function} callback */ S3Client.prototype._copy = function (keySrc, keyDst, headers, callback) { var request = this._client.copy(keySrc, keyDst, headers); if (this.timeout) { request.setTimeout(this.timeout); } request.on('response', this._handleResponse.bind(this, callback)); request.on('error', function (error) { callback(error, null); }); request.once('timeout', function () { request.abort(); }); request.end(); }; /** * Wraps knox request and manages request events * * @private * @param {string} key * @param {function} callback */ S3Client.prototype._get = function (key, headers, callback) { var request = this._client.get(key, headers); if (this.timeout) { request.setTimeout(this.timeout); } request.on('response', this._handleResponse.bind(this, callback)); request.on('error', function (error) { callback(error, null); }); request.once('timeout', function () { request.abort(); }); request.end(); }; /** * Wraps knox request and manages request events * * @private * @param {string} key * @param {function} callback */ S3Client.prototype._head = function (key, headers, callback) { var request = this._client.head(key, headers); if (this.timeout) { request.setTimeout(this.timeout); } request.on('response', this._handleResponse.bind(this, callback)); request.on('error', function (error) { callback(error, null); }); request.once('timeout', function () { request.abort(); }); request.end(); }; /** * Wraps knox request and manages request events * * @private * @param {string} key * @param {function} callback */ S3Client.prototype._del = function (key, callback) { var request = this._client.del(key); if (this.timeout) { request.setTimeout(this.timeout); } request.on('response', this._handleResponse.bind(this, callback)); request.on('error', function (error) { callback(error, null); }); request.once('timeout', function () { request.abort(); }); request.end(); }; /** * @private * @param {function} callback * @param {http.response} res */ S3Client.prototype._handleResponse = function (callback, res) { var dataLength = 0; var buffers = []; res.on('data', function (data) { buffers.push(data); dataLength += data.length; }); res.on('end', function () { var resultBuffer = new Buffer(dataLength); var bufferPosition = 0; for (var i = 0, l = buffers.length; i < l; i++) { buffers[i].copy(resultBuffer, bufferPosition); bufferPosition += buffers[i].length; } var response = new Response(); response.setStatusCode(res.statusCode); response.setHeaders(res.headers); response._setBody(resultBuffer); callback(null, response); }); }; /** * Function for handling retries * * @private * @param {function} fnc function to call * @param {function} callback callback fired when specified number * of retries reached or response recieved */ S3Client.prototype._spawnFunction = function (fnc, callback) { var maxRetry = this.retry; fnc(function (err, data) { if (err && maxRetry--) { console.log('S3Client retrying operation...', maxRetry); fnc(arguments.callee); } else { callback(err, data); } }); }; S3Client.prototype._getError = function (err) { var error = new Error(err.msg); error.code = err.code; return error; }; /** * Checks if passed ACL allows for public read. * * @param {Object} acl ACL * @returns {Boolean} True or false depending on ACL configuration */ S3Client.isPublicRead = function (acl) { try { var grants = acl.AccessControlList.Grant; var grant; for (var i = 0, len = grants.length; i < len; i++) { grant = grants[i]; if (grant.Grantee.URI == 'http://acs.amazonaws.com/groups/global/AllUsers') { if (['READ', 'FULL_CONTROL'].indexOf(grant.Permission) != -1) { return true; } } } return false; } catch (ex) { console.warn('S3Client error processing ACLs', ex); return false; } }; S3Client.Error = {}; S3Client.Error.ENOBODY = {code: 'ENOBODY', msg: 'No body given'}; S3Client.Error.ENOKEY = {code: 'ENOKEY', msg: 'No key give'}; S3Client.Error.ENOHEADERS = {code: 'ENOHEADERS', msg: 'No headers given'}; exports.S3Client = S3Client;