UNPKG

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