git-off

git off handles large files in git repos
git clone https://noulin.net/git/git-off.git
Log | Files | Refs | README

util.js (26894B)


      1 /* eslint guard-for-in:0 */
      2 var AWS;
      3 
      4 /**
      5  * A set of utility methods for use with the AWS SDK.
      6  *
      7  * @!attribute abort
      8  *   Return this value from an iterator function {each} or {arrayEach}
      9  *   to break out of the iteration.
     10  *   @example Breaking out of an iterator function
     11  *     AWS.util.each({a: 1, b: 2, c: 3}, function(key, value) {
     12  *       if (key == 'b') return AWS.util.abort;
     13  *     });
     14  *   @see each
     15  *   @see arrayEach
     16  * @api private
     17  */
     18 var util = {
     19   engine: function engine() {
     20     if (util.isBrowser() && typeof navigator !== 'undefined') {
     21       return navigator.userAgent;
     22     } else {
     23       return process.platform + '/' + process.version;
     24     }
     25   },
     26 
     27   userAgent: function userAgent() {
     28     var name = util.isBrowser() ? 'js' : 'nodejs';
     29     var agent = 'aws-sdk-' + name + '/' + require('./core').VERSION;
     30     if (name === 'nodejs') agent += ' ' + util.engine();
     31     return agent;
     32   },
     33 
     34   isBrowser: function isBrowser() { return process && process.browser; },
     35   isNode: function isNode() { return !util.isBrowser(); },
     36   uriEscape: function uriEscape(string) {
     37     var output = encodeURIComponent(string);
     38     output = output.replace(/[^A-Za-z0-9_.~\-%]+/g, escape);
     39 
     40     // AWS percent-encodes some extra non-standard characters in a URI
     41     output = output.replace(/[*]/g, function(ch) {
     42       return '%' + ch.charCodeAt(0).toString(16).toUpperCase();
     43     });
     44 
     45     return output;
     46   },
     47 
     48   uriEscapePath: function uriEscapePath(string) {
     49     var parts = [];
     50     util.arrayEach(string.split('/'), function (part) {
     51       parts.push(util.uriEscape(part));
     52     });
     53     return parts.join('/');
     54   },
     55 
     56   urlParse: function urlParse(url) {
     57     return util.url.parse(url);
     58   },
     59 
     60   urlFormat: function urlFormat(url) {
     61     return util.url.format(url);
     62   },
     63 
     64   queryStringParse: function queryStringParse(qs) {
     65     return util.querystring.parse(qs);
     66   },
     67 
     68   queryParamsToString: function queryParamsToString(params) {
     69     var items = [];
     70     var escape = util.uriEscape;
     71     var sortedKeys = Object.keys(params).sort();
     72 
     73     util.arrayEach(sortedKeys, function(name) {
     74       var value = params[name];
     75       var ename = escape(name);
     76       var result = ename + '=';
     77       if (Array.isArray(value)) {
     78         var vals = [];
     79         util.arrayEach(value, function(item) { vals.push(escape(item)); });
     80         result = ename + '=' + vals.sort().join('&' + ename + '=');
     81       } else if (value !== undefined && value !== null) {
     82         result = ename + '=' + escape(value);
     83       }
     84       items.push(result);
     85     });
     86 
     87     return items.join('&');
     88   },
     89 
     90   readFileSync: function readFileSync(path) {
     91     if (util.isBrowser()) return null;
     92     return require('fs').readFileSync(path, 'utf-8');
     93   },
     94 
     95   base64: {
     96 
     97     encode: function encode64(string) {
     98       return new util.Buffer(string).toString('base64');
     99     },
    100 
    101     decode: function decode64(string) {
    102       return new util.Buffer(string, 'base64');
    103     }
    104 
    105   },
    106 
    107   buffer: {
    108     toStream: function toStream(buffer) {
    109       if (!util.Buffer.isBuffer(buffer)) buffer = new util.Buffer(buffer);
    110 
    111       var readable = new (util.stream.Readable)();
    112       var pos = 0;
    113       readable._read = function(size) {
    114         if (pos >= buffer.length) return readable.push(null);
    115 
    116         var end = pos + size;
    117         if (end > buffer.length) end = buffer.length;
    118         readable.push(buffer.slice(pos, end));
    119         pos = end;
    120       };
    121 
    122       return readable;
    123     },
    124 
    125     /**
    126      * Concatenates a list of Buffer objects.
    127      */
    128     concat: function(buffers) {
    129       var length = 0,
    130           offset = 0,
    131           buffer = null, i;
    132 
    133       for (i = 0; i < buffers.length; i++) {
    134         length += buffers[i].length;
    135       }
    136 
    137       buffer = new util.Buffer(length);
    138 
    139       for (i = 0; i < buffers.length; i++) {
    140         buffers[i].copy(buffer, offset);
    141         offset += buffers[i].length;
    142       }
    143 
    144       return buffer;
    145     }
    146   },
    147 
    148   string: {
    149     byteLength: function byteLength(string) {
    150       if (string === null || string === undefined) return 0;
    151       if (typeof string === 'string') string = new util.Buffer(string);
    152 
    153       if (typeof string.byteLength === 'number') {
    154         return string.byteLength;
    155       } else if (typeof string.length === 'number') {
    156         return string.length;
    157       } else if (typeof string.size === 'number') {
    158         return string.size;
    159       } else if (typeof string.path === 'string') {
    160         return require('fs').lstatSync(string.path).size;
    161       } else {
    162         throw util.error(new Error('Cannot determine length of ' + string),
    163           { object: string });
    164       }
    165     },
    166 
    167     upperFirst: function upperFirst(string) {
    168       return string[0].toUpperCase() + string.substr(1);
    169     },
    170 
    171     lowerFirst: function lowerFirst(string) {
    172       return string[0].toLowerCase() + string.substr(1);
    173     }
    174   },
    175 
    176   ini: {
    177     parse: function string(ini) {
    178       var currentSection, map = {};
    179       util.arrayEach(ini.split(/\r?\n/), function(line) {
    180         line = line.split(/(^|\s)[;#]/)[0]; // remove comments
    181         var section = line.match(/^\s*\[([^\[\]]+)\]\s*$/);
    182         if (section) {
    183           currentSection = section[1];
    184         } else if (currentSection) {
    185           var item = line.match(/^\s*(.+?)\s*=\s*(.+?)\s*$/);
    186           if (item) {
    187             map[currentSection] = map[currentSection] || {};
    188             map[currentSection][item[1]] = item[2];
    189           }
    190         }
    191       });
    192 
    193       return map;
    194     }
    195   },
    196 
    197   fn: {
    198     noop: function() {},
    199 
    200     /**
    201      * Turn a synchronous function into as "async" function by making it call
    202      * a callback. The underlying function is called with all but the last argument,
    203      * which is treated as the callback. The callback is passed passed a first argument
    204      * of null on success to mimick standard node callbacks.
    205      */
    206     makeAsync: function makeAsync(fn, expectedArgs) {
    207       if (expectedArgs && expectedArgs <= fn.length) {
    208         return fn;
    209       }
    210 
    211       return function() {
    212         var args = Array.prototype.slice.call(arguments, 0);
    213         var callback = args.pop();
    214         var result = fn.apply(null, args);
    215         callback(result);
    216       };
    217     }
    218   },
    219 
    220   /**
    221    * Date and time utility functions.
    222    */
    223   date: {
    224 
    225     /**
    226      * @return [Date] the current JavaScript date object. Since all
    227      *   AWS services rely on this date object, you can override
    228      *   this function to provide a special time value to AWS service
    229      *   requests.
    230      */
    231     getDate: function getDate() {
    232       if (!AWS) AWS = require('./core');
    233       if (AWS.config.systemClockOffset) { // use offset when non-zero
    234         return new Date(new Date().getTime() + AWS.config.systemClockOffset);
    235       } else {
    236         return new Date();
    237       }
    238     },
    239 
    240     /**
    241      * @return [String] the date in ISO-8601 format
    242      */
    243     iso8601: function iso8601(date) {
    244       if (date === undefined) { date = util.date.getDate(); }
    245       return date.toISOString().replace(/\.\d{3}Z$/, 'Z');
    246     },
    247 
    248     /**
    249      * @return [String] the date in RFC 822 format
    250      */
    251     rfc822: function rfc822(date) {
    252       if (date === undefined) { date = util.date.getDate(); }
    253       return date.toUTCString();
    254     },
    255 
    256     /**
    257      * @return [Integer] the UNIX timestamp value for the current time
    258      */
    259     unixTimestamp: function unixTimestamp(date) {
    260       if (date === undefined) { date = util.date.getDate(); }
    261       return date.getTime() / 1000;
    262     },
    263 
    264     /**
    265      * @param [String,number,Date] date
    266      * @return [Date]
    267      */
    268     from: function format(date) {
    269       if (typeof date === 'number') {
    270         return new Date(date * 1000); // unix timestamp
    271       } else {
    272         return new Date(date);
    273       }
    274     },
    275 
    276     /**
    277      * Given a Date or date-like value, this function formats the
    278      * date into a string of the requested value.
    279      * @param [String,number,Date] date
    280      * @param [String] formatter Valid formats are:
    281      #   * 'iso8601'
    282      #   * 'rfc822'
    283      #   * 'unixTimestamp'
    284      * @return [String]
    285      */
    286     format: function format(date, formatter) {
    287       if (!formatter) formatter = 'iso8601';
    288       return util.date[formatter](util.date.from(date));
    289     },
    290 
    291     parseTimestamp: function parseTimestamp(value) {
    292       if (typeof value === 'number') { // unix timestamp (number)
    293         return new Date(value * 1000);
    294       } else if (value.match(/^\d+$/)) { // unix timestamp
    295         return new Date(value * 1000);
    296       } else if (value.match(/^\d{4}/)) { // iso8601
    297         return new Date(value);
    298       } else if (value.match(/^\w{3},/)) { // rfc822
    299         return new Date(value);
    300       } else {
    301         throw util.error(
    302           new Error('unhandled timestamp format: ' + value),
    303           {code: 'TimestampParserError'});
    304       }
    305     }
    306 
    307   },
    308 
    309   crypto: {
    310     crc32Table: [
    311      0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419,
    312      0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4,
    313      0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07,
    314      0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
    315      0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856,
    316      0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
    317      0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
    318      0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
    319      0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3,
    320      0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A,
    321      0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599,
    322      0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
    323      0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190,
    324      0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F,
    325      0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E,
    326      0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
    327      0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
    328      0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
    329      0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3,
    330      0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
    331      0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A,
    332      0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5,
    333      0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010,
    334      0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
    335      0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17,
    336      0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6,
    337      0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
    338      0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
    339      0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344,
    340      0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
    341      0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A,
    342      0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
    343      0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1,
    344      0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
    345      0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF,
    346      0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
    347      0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE,
    348      0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31,
    349      0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C,
    350      0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
    351      0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B,
    352      0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
    353      0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1,
    354      0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
    355      0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278,
    356      0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7,
    357      0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66,
    358      0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
    359      0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605,
    360      0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8,
    361      0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
    362      0x2D02EF8D],
    363 
    364     crc32: function crc32(data) {
    365       var tbl = util.crypto.crc32Table;
    366       var crc = 0 ^ -1;
    367 
    368       if (typeof data === 'string') {
    369         data = new util.Buffer(data);
    370       }
    371 
    372       for (var i = 0; i < data.length; i++) {
    373         var code = data.readUInt8(i);
    374         crc = (crc >>> 8) ^ tbl[(crc ^ code) & 0xFF];
    375       }
    376       return (crc ^ -1) >>> 0;
    377     },
    378 
    379     hmac: function hmac(key, string, digest, fn) {
    380       if (!digest) digest = 'binary';
    381       if (digest === 'buffer') { digest = undefined; }
    382       if (!fn) fn = 'sha256';
    383       if (typeof string === 'string') string = new util.Buffer(string);
    384       return util.crypto.lib.createHmac(fn, key).update(string).digest(digest);
    385     },
    386 
    387     md5: function md5(data, digest, callback) {
    388       return util.crypto.hash('md5', data, digest, callback);
    389     },
    390 
    391     sha256: function sha256(data, digest, callback) {
    392       return util.crypto.hash('sha256', data, digest, callback);
    393     },
    394 
    395     hash: function(algorithm, data, digest, callback) {
    396       var hash = util.crypto.createHash(algorithm);
    397       if (!digest) { digest = 'binary'; }
    398       if (digest === 'buffer') { digest = undefined; }
    399       if (typeof data === 'string') data = new util.Buffer(data);
    400       var sliceFn = util.arraySliceFn(data);
    401       var isBuffer = util.Buffer.isBuffer(data);
    402       //Identifying objects with an ArrayBuffer as buffers
    403       if (util.isBrowser() && typeof ArrayBuffer !== 'undefined' && data && data.buffer instanceof ArrayBuffer) isBuffer = true;
    404 
    405       if (callback && typeof data === 'object' &&
    406           typeof data.on === 'function' && !isBuffer) {
    407         data.on('data', function(chunk) { hash.update(chunk); });
    408         data.on('error', function(err) { callback(err); });
    409         data.on('end', function() { callback(null, hash.digest(digest)); });
    410       } else if (callback && sliceFn && !isBuffer &&
    411                  typeof FileReader !== 'undefined') {
    412         // this might be a File/Blob
    413         var index = 0, size = 1024 * 512;
    414         var reader = new FileReader();
    415         reader.onerror = function() {
    416           callback(new Error('Failed to read data.'));
    417         };
    418         reader.onload = function() {
    419           var buf = new util.Buffer(new Uint8Array(reader.result));
    420           hash.update(buf);
    421           index += buf.length;
    422           reader._continueReading();
    423         };
    424         reader._continueReading = function() {
    425           if (index >= data.size) {
    426             callback(null, hash.digest(digest));
    427             return;
    428           }
    429 
    430           var back = index + size;
    431           if (back > data.size) back = data.size;
    432           reader.readAsArrayBuffer(sliceFn.call(data, index, back));
    433         };
    434 
    435         reader._continueReading();
    436       } else {
    437         if (util.isBrowser() && typeof data === 'object' && !isBuffer) {
    438           data = new util.Buffer(new Uint8Array(data));
    439         }
    440         var out = hash.update(data).digest(digest);
    441         if (callback) callback(null, out);
    442         return out;
    443       }
    444     },
    445 
    446     toHex: function toHex(data) {
    447       var out = [];
    448       for (var i = 0; i < data.length; i++) {
    449         out.push(('0' + data.charCodeAt(i).toString(16)).substr(-2, 2));
    450       }
    451       return out.join('');
    452     },
    453 
    454     createHash: function createHash(algorithm) {
    455       return util.crypto.lib.createHash(algorithm);
    456     }
    457 
    458   },
    459 
    460   /** @!ignore */
    461 
    462   /* Abort constant */
    463   abort: {},
    464 
    465   each: function each(object, iterFunction) {
    466     for (var key in object) {
    467       if (Object.prototype.hasOwnProperty.call(object, key)) {
    468         var ret = iterFunction.call(this, key, object[key]);
    469         if (ret === util.abort) break;
    470       }
    471     }
    472   },
    473 
    474   arrayEach: function arrayEach(array, iterFunction) {
    475     for (var idx in array) {
    476       if (Object.prototype.hasOwnProperty.call(array, idx)) {
    477         var ret = iterFunction.call(this, array[idx], parseInt(idx, 10));
    478         if (ret === util.abort) break;
    479       }
    480     }
    481   },
    482 
    483   update: function update(obj1, obj2) {
    484     util.each(obj2, function iterator(key, item) {
    485       obj1[key] = item;
    486     });
    487     return obj1;
    488   },
    489 
    490   merge: function merge(obj1, obj2) {
    491     return util.update(util.copy(obj1), obj2);
    492   },
    493 
    494   copy: function copy(object) {
    495     if (object === null || object === undefined) return object;
    496     var dupe = {};
    497     // jshint forin:false
    498     for (var key in object) {
    499       dupe[key] = object[key];
    500     }
    501     return dupe;
    502   },
    503 
    504   isEmpty: function isEmpty(obj) {
    505     for (var prop in obj) {
    506       if (Object.prototype.hasOwnProperty.call(obj, prop)) {
    507         return false;
    508       }
    509     }
    510     return true;
    511   },
    512 
    513   arraySliceFn: function arraySliceFn(obj) {
    514     var fn = obj.slice || obj.webkitSlice || obj.mozSlice;
    515     return typeof fn === 'function' ? fn : null;
    516   },
    517 
    518   isType: function isType(obj, type) {
    519     // handle cross-"frame" objects
    520     if (typeof type === 'function') type = util.typeName(type);
    521     return Object.prototype.toString.call(obj) === '[object ' + type + ']';
    522   },
    523 
    524   typeName: function typeName(type) {
    525     if (Object.prototype.hasOwnProperty.call(type, 'name')) return type.name;
    526     var str = type.toString();
    527     var match = str.match(/^\s*function (.+)\(/);
    528     return match ? match[1] : str;
    529   },
    530 
    531   error: function error(err, options) {
    532     var originalError = null;
    533     if (typeof err.message === 'string' && err.message !== '') {
    534       if (typeof options === 'string' || (options && options.message)) {
    535         originalError = util.copy(err);
    536         originalError.message = err.message;
    537       }
    538     }
    539     err.message = err.message || null;
    540 
    541     if (typeof options === 'string') {
    542       err.message = options;
    543     } else if (typeof options === 'object' && options !== null) {
    544       util.update(err, options);
    545       if (options.message)
    546         err.message = options.message;
    547       if (options.code || options.name)
    548         err.code = options.code || options.name;
    549       if (options.stack)
    550         err.stack = options.stack;
    551     }
    552 
    553     if (typeof Object.defineProperty === 'function') {
    554       Object.defineProperty(err, 'name', {writable: true, enumerable: false});
    555       Object.defineProperty(err, 'message', {enumerable: true});
    556     }
    557 
    558     err.name = options && options.name || err.name || err.code || 'Error';
    559     err.time = new Date();
    560 
    561     if (originalError) err.originalError = originalError;
    562 
    563     return err;
    564   },
    565 
    566   /**
    567    * @api private
    568    */
    569   inherit: function inherit(klass, features) {
    570     var newObject = null;
    571     if (features === undefined) {
    572       features = klass;
    573       klass = Object;
    574       newObject = {};
    575     } else {
    576       var ctor = function ConstructorWrapper() {};
    577       ctor.prototype = klass.prototype;
    578       newObject = new ctor();
    579     }
    580 
    581     // constructor not supplied, create pass-through ctor
    582     if (features.constructor === Object) {
    583       features.constructor = function() {
    584         if (klass !== Object) {
    585           return klass.apply(this, arguments);
    586         }
    587       };
    588     }
    589 
    590     features.constructor.prototype = newObject;
    591     util.update(features.constructor.prototype, features);
    592     features.constructor.__super__ = klass;
    593     return features.constructor;
    594   },
    595 
    596   /**
    597    * @api private
    598    */
    599   mixin: function mixin() {
    600     var klass = arguments[0];
    601     for (var i = 1; i < arguments.length; i++) {
    602       // jshint forin:false
    603       for (var prop in arguments[i].prototype) {
    604         var fn = arguments[i].prototype[prop];
    605         if (prop !== 'constructor') {
    606           klass.prototype[prop] = fn;
    607         }
    608       }
    609     }
    610     return klass;
    611   },
    612 
    613   /**
    614    * @api private
    615    */
    616   hideProperties: function hideProperties(obj, props) {
    617     if (typeof Object.defineProperty !== 'function') return;
    618 
    619     util.arrayEach(props, function (key) {
    620       Object.defineProperty(obj, key, {
    621         enumerable: false, writable: true, configurable: true });
    622     });
    623   },
    624 
    625   /**
    626    * @api private
    627    */
    628   property: function property(obj, name, value, enumerable, isValue) {
    629     var opts = {
    630       configurable: true,
    631       enumerable: enumerable !== undefined ? enumerable : true
    632     };
    633     if (typeof value === 'function' && !isValue) {
    634       opts.get = value;
    635     }
    636     else {
    637       opts.value = value; opts.writable = true;
    638     }
    639 
    640     Object.defineProperty(obj, name, opts);
    641   },
    642 
    643   /**
    644    * @api private
    645    */
    646   memoizedProperty: function memoizedProperty(obj, name, get, enumerable) {
    647     var cachedValue = null;
    648 
    649     // build enumerable attribute for each value with lazy accessor.
    650     util.property(obj, name, function() {
    651       if (cachedValue === null) {
    652         cachedValue = get();
    653       }
    654       return cachedValue;
    655     }, enumerable);
    656   },
    657 
    658   /**
    659    * TODO Remove in major version revision
    660    * This backfill populates response data without the
    661    * top-level payload name.
    662    *
    663    * @api private
    664    */
    665   hoistPayloadMember: function hoistPayloadMember(resp) {
    666     var req = resp.request;
    667     var operation = req.operation;
    668     var output = req.service.api.operations[operation].output;
    669     if (output.payload) {
    670       var payloadMember = output.members[output.payload];
    671       var responsePayload = resp.data[output.payload];
    672       if (payloadMember.type === 'structure') {
    673         util.each(responsePayload, function(key, value) {
    674           util.property(resp.data, key, value, false);
    675         });
    676       }
    677     }
    678   },
    679 
    680   /**
    681    * Compute SHA-256 checksums of streams
    682    *
    683    * @api private
    684    */
    685   computeSha256: function computeSha256(body, done) {
    686     if (util.isNode()) {
    687       var Stream = util.stream.Stream;
    688       var fs = require('fs');
    689       if (body instanceof Stream) {
    690         if (typeof body.path === 'string') { // assume file object
    691           var settings = {};
    692           if (typeof body.start === 'number') {
    693             settings.start = body.start;
    694           }
    695           if (typeof body.end === 'number') {
    696             settings.end = body.end;
    697           }
    698           body = fs.createReadStream(body.path, settings);
    699         } else { // TODO support other stream types
    700           return done(new Error('Non-file stream objects are ' +
    701                                 'not supported with SigV4'));
    702         }
    703       }
    704     }
    705 
    706     util.crypto.sha256(body, 'hex', function(err, sha) {
    707       if (err) done(err);
    708       else done(null, sha);
    709     });
    710   },
    711 
    712   /**
    713    * @api private
    714    */
    715   isClockSkewed: function isClockSkewed(serverTime) {
    716     if (serverTime) {
    717       util.property(AWS.config, 'isClockSkewed',
    718         Math.abs(new Date().getTime() - serverTime) >= 300000, false);
    719       return AWS.config.isClockSkewed;
    720     }
    721   },
    722 
    723   applyClockOffset: function applyClockOffset(serverTime) {
    724     if (serverTime)
    725       AWS.config.systemClockOffset = serverTime - new Date().getTime();
    726   },
    727 
    728   /**
    729    * @api private
    730    */
    731   extractRequestId: function extractRequestId(resp) {
    732     var requestId = resp.httpResponse.headers['x-amz-request-id'] ||
    733                      resp.httpResponse.headers['x-amzn-requestid'];
    734 
    735     if (!requestId && resp.data && resp.data.ResponseMetadata) {
    736       requestId = resp.data.ResponseMetadata.RequestId;
    737     }
    738 
    739     if (requestId) {
    740       resp.requestId = requestId;
    741     }
    742 
    743     if (resp.error) {
    744       resp.error.requestId = requestId;
    745     }
    746   },
    747 
    748   /**
    749    * @api private
    750    */
    751   addPromises: function addPromises(constructors, PromiseDependency) {
    752     if (PromiseDependency === undefined && AWS && AWS.config) {
    753       PromiseDependency = AWS.config.getPromisesDependency();
    754     }
    755     if (PromiseDependency === undefined && typeof Promise !== 'undefined') {
    756       PromiseDependency = Promise;
    757     }
    758     if (typeof PromiseDependency !== 'function') var deletePromises = true;
    759     if (!Array.isArray(constructors)) constructors = [constructors];
    760 
    761     for (var ind = 0; ind < constructors.length; ind++) {
    762       var constructor = constructors[ind];
    763       if (deletePromises) {
    764         if (constructor.deletePromisesFromClass) {
    765           constructor.deletePromisesFromClass();
    766         }
    767       } else if (constructor.addPromisesToClass) {
    768         constructor.addPromisesToClass(PromiseDependency);
    769       }
    770     }
    771   },
    772 
    773   /**
    774    * @api private
    775    */
    776   promisifyMethod: function promisifyMethod(methodName, PromiseDependency) {
    777     return function promise() {
    778       var self = this;
    779       return new PromiseDependency(function(resolve, reject) {
    780         self[methodName](function(err, data) {
    781           if (err) {
    782             reject(err);
    783           } else {
    784             resolve(data);
    785           }
    786         });
    787       });
    788     };
    789   },
    790 
    791   /**
    792    * @api private
    793    */
    794   isDualstackAvailable: function isDualstackAvailable(service) {
    795     if (!service) return false;
    796     var metadata = require('../apis/metadata.json');
    797     if (typeof service !== 'string') service = service.serviceIdentifier;
    798     if (typeof service !== 'string' || !metadata.hasOwnProperty(service)) return false;
    799     return !!metadata[service].dualstackAvailable;
    800   },
    801 
    802   /**
    803    * @api private
    804    */
    805   calculateRetryDelay: function calculateRetryDelay(retryCount, retryDelayOptions) {
    806     if (!retryDelayOptions) retryDelayOptions = {};
    807     var customBackoff = retryDelayOptions.customBackoff || null;
    808     if (typeof customBackoff === 'function') {
    809       return customBackoff(retryCount);
    810     }
    811     var base = retryDelayOptions.base || 100;
    812     var delay = Math.random() * (Math.pow(2, retryCount) * base);
    813     return delay;
    814   },
    815 
    816   /**
    817    * @api private
    818    */
    819   handleRequestWithRetries: function handleRequestWithRetries(httpRequest, options, cb) {
    820     if (!options) options = {};
    821     var http = AWS.HttpClient.getInstance();
    822     var httpOptions = options.httpOptions || {};
    823     var retryCount = 0;
    824 
    825     var errCallback = function(err) {
    826       var maxRetries = options.maxRetries || 0;
    827       if (err && err.code === 'TimeoutError') err.retryable = true;
    828       if (err && err.retryable && retryCount < maxRetries) {
    829         retryCount++;
    830         var delay = util.calculateRetryDelay(retryCount, options.retryDelayOptions);
    831         setTimeout(sendRequest, delay + (err.retryAfter || 0));
    832       } else {
    833         cb(err);
    834       }
    835     };
    836 
    837     var sendRequest = function() {
    838       var data = '';
    839       http.handleRequest(httpRequest, httpOptions, function(httpResponse) {
    840         httpResponse.on('data', function(chunk) { data += chunk.toString(); });
    841         httpResponse.on('end', function() {
    842           var statusCode = httpResponse.statusCode;
    843           if (statusCode < 300) {
    844             cb(null, data);
    845           } else {
    846             var retryAfter = parseInt(httpResponse.headers['retry-after'], 10) * 1000 || 0;
    847             var err = util.error(new Error(),
    848               { retryable: statusCode >= 500 || statusCode === 429 }
    849             );
    850             if (retryAfter && err.retryable) err.retryAfter = retryAfter;
    851             errCallback(err);
    852           }
    853         });
    854       }, errCallback);
    855     };
    856 
    857     process.nextTick(sendRequest);
    858   }
    859 
    860 };
    861 
    862 module.exports = util;