feat: Add Be and tbd skill, also added Roadmap file

This commit is contained in:
2026-05-10 16:32:12 -04:00
parent 3500ade13f
commit 0bb8885802
29587 changed files with 10611695 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
var AWS = require('./core');
AWS.apiLoader = function(svc, version) {
if (!AWS.apiLoader.services.hasOwnProperty(svc)) {
throw new Error('InvalidService: Failed to load api for ' + svc);
}
return AWS.apiLoader.services[svc][version];
};
AWS.apiLoader.services = {};
module.exports = AWS.apiLoader;

View File

@@ -0,0 +1,7 @@
require('./node_loader');
var AWS = require('./core');
// Load all service classes
require('../clients/all');
module.exports = AWS;

View File

@@ -0,0 +1,14 @@
require('./browser_loader');
var AWS = require('./core');
if (typeof window !== 'undefined') window.Jibo = AWS;
if (typeof module !== 'undefined') module.exports = AWS;
if (typeof self !== 'undefined') self.Jibo = AWS;
/**
* @private
* DO NOT REMOVE
* browser builder will strip out this line if services are supplied on the command line.
*/
require('../clients/browser_default');

View File

@@ -0,0 +1,24 @@
var util = require('./util');
// browser specific modules
util.crypto.lib = require('crypto-browserify');
util.Buffer = require('buffer/').Buffer;
util.url = require('url/');
util.querystring = require('querystring/');
var AWS = require('./core');
// Use default API loader function
require('./api_loader');
// Load the DOMParser XML parser
AWS.XML.Parser = require('./xml/browser_parser');
// Load the XHR HttpClient
require('./http/xhr');
if (typeof process === 'undefined') {
process = {
browser: true
};
}

View File

@@ -0,0 +1,238 @@
import {Agent as httpAgent} from 'http';
import {Agent as httpsAgent} from 'https';
import {AWSError} from './error';
import {Credentials, CredentialsOptions} from './credentials';
import {ConfigurationServicePlaceholders, ConfigurationServiceApiVersions} from './config_service_placeholders';
export class ConfigBase extends ConfigurationOptions{
constructor(options?: ConfigurationOptions);
/**
* Loads credentials from the configuration object.
*/
getCredentials(callback: (err: AWSError) => void): void;
/**
* Loads configuration data from a JSON file into this config object.
* Loading configuration willr eset all existing configuration on the object.
* This feature is not supported in the browser environment of the SDK.
*
* @param {string} path - the path relative to your process's current working directory to load configuration from.
*/
loadFromPath(path: string): ConfigBase;
/**
* Updates the current configuration object with new options.
*
* @param {ConfigurationOptions} options - a map of option keys and values.
* @param {boolean} allowUnknownKeys - Whether unknown keys can be set on the configuration object.
*/
update(options: ConfigurationOptions & {[key: string]: any}, allowUnknownKeys: true): void;
/**
* Updates the current configuration object with new options.
*
* @param {ConfigurationOptions} options - a map of option keys and values.
* @param {boolean} allowUnknownKeys - Defaults to false. Whether unknown keys can be set on the configuration object.
*/
update(options: ConfigurationOptions, allowUnknownKeys?: false): void;
/**
* Gets the promise dependency the SDK will use wherever Promises are returned.
*/
getPromisesDependency(): typeof Promise | void;
/**
* Sets the promise dependency the SDK will use wherever Promises are returned.
* @param {function} dep - a reference to a Promise constructor
*/
setPromisesDependency(dep: any): void;
}
export class Config extends ConfigBase {
/**
* Creates a new configuration object.
* This is the object that passes option data along to service requests, including credentials, security, region information, and some service specific settings.
*/
constructor(options?: ConfigurationOptions & ConfigurationServicePlaceholders & APIVersions);
/**
* Loads configuration data from a JSON file into this config object.
* Loading configuration willr eset all existing configuration on the object.
* This feature is not supported in the browser environment of the SDK.
*
* @param {string} path - the path relative to your process's current working directory to load configuration from.
*/
loadFromPath(path: string): Config & ConfigurationServicePlaceholders & APIVersions;
/**
* Updates the current configuration object with new options.
*
* @param {ConfigurationOptions} options - a map of option keys and values.
* @param {boolean} allowUnknownKeys - Whether unknown keys can be set on the configuration object.
*/
update(options: ConfigurationOptions & ConfigurationServicePlaceholders & APIVersions & {[key: string]: any}, allowUnknownKeys: true): void;
/**
* Updates the current configuration object with new options.
*
* @param {ConfigurationOptions} options - a map of option keys and values.
* @param {boolean} allowUnknownKeys - Defaults to false. Whether unknown keys can be set on the configuration object.
*/
update(options: ConfigurationOptions & ConfigurationServicePlaceholders & APIVersions, allowUnknownKeys?: false): void;
}
export type GlobalConfigInstance = Config & ConfigurationServicePlaceholders & APIVersions;
export interface HTTPOptions {
/**
* the URL to proxy requests through.
*/
proxy?: string
/**
* the Agent object to perform HTTP requests with.
* Used for connection pooling.
* Defaults to the global agent (http.globalAgent) for non-SSL connections.
*/
agent?: httpAgent | httpsAgent
/**
* The number of milliseconds to wait before giving up on a connection attempt.
* Defaults to two minutes (120000).
*/
timeout?: number
/**
* Whether the SDK will send asynchronous HTTP requests.
* Used in the browser environment only.
* Set to false to send requests synchronously.
* Defaults to true (async on).
*/
xhrAsync?: boolean
/**
* Sets the 'withCredentials' property of an XMLHttpRequest object.
* Used in the browser environment only.
* Defaults to false.
*/
xhrWithCredentials?: boolean
}
export interface Logger {
write?: (chunk: any, encoding?: string, callback?: () => void) => void
log?: (...messages: any[]) => void;
}
export interface ParamValidation {
/**
* Validates that a value meets the min constraint.
* This is enabled by default when paramValidation is set to true.
*/
min?: boolean
/**
* Validates that a value meets the max constraint.
*/
max?: boolean
/**
* Validates that a string value matches a regular expression.
*/
pattern?: boolean
/**
* Validates that a string value matches one of the allowable enum values.
*/
enum?: boolean
}
export interface RetryDelayOptions {
/**
* The base number of milliseconds to use in the exponential backoff for operation retries.
* Defaults to 100 ms.
*/
base?: number
/**
* A custom function that accepts a retry count and returns the amount of time to delay in milliseconds.
* The base option will be ignored if this option is supplied.
*/
customBackoff?: (retryCount: number) => number
}
export interface APIVersions {
/**
* A string in YYYY-MM-DD format that represents the latest possible API version that can be used in all services (unless overridden by apiVersions). Specify \'latest\' to use the latest possible version.
*/
apiVersion?: "latest"|string;
/**
* A map of service identifiers (the lowercase service class name) with the API version to use when instantiating a service. Specify 'latest' for each individual that can use the latest available version.
*/
apiVersions?: ConfigurationServiceApiVersions;
}
export abstract class ConfigurationOptions {
/**
* Whether to compute checksums for payload bodies when the service accepts it.
* Currently supported in S3 only.
*/
computeChecksums?: boolean
/**
* Whether types are converted when parsing response data.
*/
convertResponseTypes?: boolean
/**
* Whether to apply a clock skew correction and retry requests that fail because of an skewed client clock.
*/
correctClockSkew?: boolean
/**
* The AWS credentials to sign requests with.
*/
credentials?: Credentials|CredentialsOptions
/**
* A set of options to pass to the low-level HTTP request.
*/
httpOptions?: HTTPOptions
/**
* An object that responds to .write() (like a stream) or .log() (like the console object) in order to log information about requests.
*/
logger?: Logger
/**
* The maximum amount of redirects to follow for a service request.
*/
maxRedirects?: number
/**
* The maximum amount of retries to perform for a service request.
*/
maxRetries?: number
/**
* Returns whether input parameters should be validated against the operation description before sending the request.
* Defaults to true.
* Pass a map to enable any of the following specific validation features: min|max|pattern|enum
*/
paramValidation?: ParamValidation|boolean
/**
* The region to send service requests to.
*/
region?: string
/**
* Returns A set of options to configure the retry delay on retryable errors.
*/
retryDelayOptions?: RetryDelayOptions
/**
* Whether the provided endpoint addresses an individual bucket.
* false if it addresses the root API endpoint.
*/
s3BucketEndpoint?: boolean
/**
* Whether to disable S3 body signing when using signature version v4.
*/
s3DisableBodySigning?: boolean
/**
* Whether to force path style URLs for S3 objects.
*/
s3ForcePathStyle?: boolean
/**
* Whether the signature to sign requests with (overriding the API configuration) is cached.
*/
signatureCache?: boolean
/**
* The signature version to sign requests with (overriding the API configuration).
* Possible values: 'v2'|'v3'|'v4'
*/
signatureVersion?: "v2"|"v3"|"v4"|string
/**
* Whether SSL is enabled for requests.
*/
sslEnabled?: boolean
/**
* An offset value in milliseconds to apply to all signing times.
*/
systemClockOffset?: number
/**
* Whether to use the Accelerate endpoint with the S3 service.
*/
useAccelerateEndpoint?: boolean
}

View File

@@ -0,0 +1,528 @@
var AWS = require('./core');
require('./credentials');
require('./credentials/credential_provider_chain');
var PromisesDependency;
/**
* The main configuration class used by all service objects to set
* the region, credentials, and other options for requests.
*
* By default, credentials and region settings are left unconfigured.
* This should be configured by the application before using any
* AWS service APIs.
*
* In order to set global configuration options, properties should
* be assigned to the global {AWS.config} object.
*
* @see AWS.config
*
* @!group General Configuration Options
*
* @!attribute credentials
* @return [AWS.Credentials] the AWS credentials to sign requests with.
*
* @!attribute region
* @example Set the global region setting to us-west-2
* AWS.config.update({region: 'us-west-2'});
* @return [AWS.Credentials] The region to send service requests to.
* @see http://docs.amazonwebservices.com/general/latest/gr/rande.html
* A list of available endpoints for each AWS service
*
* @!attribute maxRetries
* @return [Integer] the maximum amount of retries to perform for a
* service request. By default this value is calculated by the specific
* service object that the request is being made to.
*
* @!attribute maxRedirects
* @return [Integer] the maximum amount of redirects to follow for a
* service request. Defaults to 10.
*
* @!attribute paramValidation
* @return [Boolean|map] whether input parameters should be validated against
* the operation description before sending the request. Defaults to true.
* Pass a map to enable any of the following specific validation features:
*
* * **min** [Boolean] — Validates that a value meets the min
* constraint. This is enabled by default when paramValidation is set
* to `true`.
* * **max** [Boolean] — Validates that a value meets the max
* constraint.
* * **pattern** [Boolean] — Validates that a string value matches a
* regular expression.
* * **enum** [Boolean] — Validates that a string value matches one
* of the allowable enum values.
*
* @!attribute computeChecksums
* @return [Boolean] whether to compute checksums for payload bodies when
* the service accepts it (currently supported in S3 only).
*
* @!attribute convertResponseTypes
* @return [Boolean] whether types are converted when parsing response data.
* Currently only supported for JSON based services. Turning this off may
* improve performance on large response payloads. Defaults to `true`.
*
* @!attribute correctClockSkew
* @return [Boolean] whether to apply a clock skew correction and retry
* requests that fail because of an skewed client clock. Defaults to
* `false`.
*
* @!attribute sslEnabled
* @return [Boolean] whether SSL is enabled for requests
*
* @!attribute s3ForcePathStyle
* @return [Boolean] whether to force path style URLs for S3 objects
*
* @!attribute s3BucketEndpoint
* @note Setting this configuration option requires an `endpoint` to be
* provided explicitly to the service constructor.
* @return [Boolean] whether the provided endpoint addresses an individual
* bucket (false if it addresses the root API endpoint).
*
* @!attribute s3DisableBodySigning
* @return [Boolean] whether to disable S3 body signing when using signature version `v4`.
* Body signing can only be disabled when using https. Defaults to `true`.
*
* @!attribute useAccelerateEndpoint
* @note This configuration option is only compatible with S3 while accessing
* dns-compatible buckets.
* @return [Boolean] Whether to use the Accelerate endpoint with the S3 service.
* Defaults to `false`.
*
* @!attribute retryDelayOptions
* @example Set the base retry delay for all services to 300 ms
* AWS.config.update({retryDelayOptions: {base: 300}});
* // Delays with maxRetries = 3: 300, 600, 1200
* @example Set a custom backoff function to provide delay values on retries
* AWS.config.update({retryDelayOptions: {customBackoff: function(retryCount) {
* // returns delay in ms
* }}});
* @note This works with all services except DynamoDB.
* @return [map] A set of options to configure the retry delay on retryable errors.
* Currently supported options are:
*
* * **base** [Integer] — The base number of milliseconds to use in the
* exponential backoff for operation retries. Defaults to 100 ms.
* * **customBackoff ** [function] — A custom function that accepts a retry count
* and returns the amount of time to delay in milliseconds. The `base` option will be
* ignored if this option is supplied.
*
* @!attribute httpOptions
* @return [map] A set of options to pass to the low-level HTTP request.
* Currently supported options are:
*
* * **proxy** [String] — the URL to proxy requests through
* * **agent** [http.Agent, https.Agent] — the Agent object to perform
* HTTP requests with. Used for connection pooling. Defaults to the global
* agent (`http.globalAgent`) for non-SSL connections. Note that for
* SSL connections, a special Agent object is used in order to enable
* peer certificate verification. This feature is only supported in the
* Node.js environment.
* * **timeout** [Integer] — The number of milliseconds to wait before
* giving up on a connection attempt. Defaults to two minutes (120000).
* * **xhrAsync** [Boolean] — Whether the SDK will send asynchronous
* HTTP requests. Used in the browser environment only. Set to false to
* send requests synchronously. Defaults to true (async on).
* * **xhrWithCredentials** [Boolean] — Sets the "withCredentials"
* property of an XMLHttpRequest object. Used in the browser environment
* only. Defaults to false.
* @!attribute logger
* @return [#write,#log] an object that responds to .write() (like a stream)
* or .log() (like the console object) in order to log information about
* requests
*
* @!attribute systemClockOffset
* @return [Number] an offset value in milliseconds to apply to all signing
* times. Use this to compensate for clock skew when your system may be
* out of sync with the service time. Note that this configuration option
* can only be applied to the global `AWS.config` object and cannot be
* overridden in service-specific configuration. Defaults to 0 milliseconds.
*
* @!attribute signatureVersion
* @return [String] the signature version to sign requests with (overriding
* the API configuration). Possible values are: 'v2', 'v3', 'v4'.
*
* @!attribute signatureCache
* @return [Boolean] whether the signature to sign requests with (overriding
* the API configuration) is cached. Only applies to the signature version 'v4'.
* Defaults to `true`.
*/
AWS.Config = AWS.util.inherit({
/**
* @!endgroup
*/
/**
* Creates a new configuration object. This is the object that passes
* option data along to service requests, including credentials, security,
* region information, and some service specific settings.
*
* @example Creating a new configuration object with credentials and region
* var config = new AWS.Config({
* accessKeyId: 'AKID', secretAccessKey: 'SECRET', region: 'us-west-2'
* });
* @option options accessKeyId [String] your AWS access key ID.
* @option options secretAccessKey [String] your AWS secret access key.
* @option options sessionToken [AWS.Credentials] the optional AWS
* session token to sign requests with.
* @option options credentials [AWS.Credentials] the AWS credentials
* to sign requests with. You can either specify this object, or
* specify the accessKeyId and secretAccessKey options directly.
* @option options credentialProvider [AWS.CredentialProviderChain] the
* provider chain used to resolve credentials if no static `credentials`
* property is set.
* @option options region [String] the region to send service requests to.
* See {region} for more information.
* @option options maxRetries [Integer] the maximum amount of retries to
* attempt with a request. See {maxRetries} for more information.
* @option options maxRedirects [Integer] the maximum amount of redirects to
* follow with a request. See {maxRedirects} for more information.
* @option options sslEnabled [Boolean] whether to enable SSL for
* requests.
* @option options paramValidation [Boolean|map] whether input parameters
* should be validated against the operation description before sending
* the request. Defaults to true. Pass a map to enable any of the
* following specific validation features:
*
* * **min** [Boolean] — Validates that a value meets the min
* constraint. This is enabled by default when paramValidation is set
* to `true`.
* * **max** [Boolean] — Validates that a value meets the max
* constraint.
* * **pattern** [Boolean] — Validates that a string value matches a
* regular expression.
* * **enum** [Boolean] — Validates that a string value matches one
* of the allowable enum values.
* @option options computeChecksums [Boolean] whether to compute checksums
* for payload bodies when the service accepts it (currently supported
* in S3 only)
* @option options convertResponseTypes [Boolean] whether types are converted
* when parsing response data. Currently only supported for JSON based
* services. Turning this off may improve performance on large response
* payloads. Defaults to `true`.
* @option options correctClockSkew [Boolean] whether to apply a clock skew
* correction and retry requests that fail because of an skewed client
* clock. Defaults to `false`.
* @option options s3ForcePathStyle [Boolean] whether to force path
* style URLs for S3 objects.
* @option options s3BucketEndpoint [Boolean] whether the provided endpoint
* addresses an individual bucket (false if it addresses the root API
* endpoint). Note that setting this configuration option requires an
* `endpoint` to be provided explicitly to the service constructor.
* @option options s3DisableBodySigning [Boolean] whether S3 body signing
* should be disabled when using signature version `v4`. Body signing
* can only be disabled when using https. Defaults to `true`.
*
* @option options retryDelayOptions [map] A set of options to configure
* the retry delay on retryable errors. Currently supported options are:
*
* * **base** [Integer] — The base number of milliseconds to use in the
* exponential backoff for operation retries. Defaults to 100 ms.
* * **customBackoff ** [function] — A custom function that accepts a retry count
* and returns the amount of time to delay in milliseconds. The `base` option will be
* ignored if this option is supplied.
* @option options httpOptions [map] A set of options to pass to the low-level
* HTTP request. Currently supported options are:
*
* * **proxy** [String] — the URL to proxy requests through
* * **agent** [http.Agent, https.Agent] — the Agent object to perform
* HTTP requests with. Used for connection pooling. Defaults to the global
* agent (`http.globalAgent`) for non-SSL connections. Note that for
* SSL connections, a special Agent object is used in order to enable
* peer certificate verification. This feature is only available in the
* Node.js environment.
* * **timeout** [Integer] — Sets the socket to timeout after timeout
* milliseconds of inactivity on the socket. Defaults to two minutes
* (120000).
* * **xhrAsync** [Boolean] — Whether the SDK will send asynchronous
* HTTP requests. Used in the browser environment only. Set to false to
* send requests synchronously. Defaults to true (async on).
* * **xhrWithCredentials** [Boolean] — Sets the "withCredentials"
* property of an XMLHttpRequest object. Used in the browser environment
* only. Defaults to false.
* @option options apiVersion [String, Date] a String in YYYY-MM-DD format
* (or a date) that represents the latest possible API version that can be
* used in all services (unless overridden by `apiVersions`). Specify
* 'latest' to use the latest possible version.
* @option options apiVersions [map<String, String|Date>] a map of service
* identifiers (the lowercase service class name) with the API version to
* use when instantiating a service. Specify 'latest' for each individual
* that can use the latest available version.
* @option options logger [#write,#log] an object that responds to .write()
* (like a stream) or .log() (like the console object) in order to log
* information about requests
* @option options systemClockOffset [Number] an offset value in milliseconds
* to apply to all signing times. Use this to compensate for clock skew
* when your system may be out of sync with the service time. Note that
* this configuration option can only be applied to the global `AWS.config`
* object and cannot be overridden in service-specific configuration.
* Defaults to 0 milliseconds.
* @option options signatureVersion [String] the signature version to sign
* requests with (overriding the API configuration). Possible values are:
* 'v2', 'v3', 'v4'.
* @option options signatureCache [Boolean] whether the signature to sign
* requests with (overriding the API configuration) is cached. Only applies
* to the signature version 'v4'. Defaults to `true`.
*/
constructor: function Config(options) {
if (options === undefined) options = {};
options = this.extractCredentials(options);
AWS.util.each.call(this, this.keys, function (key, value) {
this.set(key, options[key], value);
});
},
/**
* @!group Managing Credentials
*/
/**
* Loads credentials from the configuration object. This is used internally
* by the SDK to ensure that refreshable {Credentials} objects are properly
* refreshed and loaded when sending a request. If you want to ensure that
* your credentials are loaded prior to a request, you can use this method
* directly to provide accurate credential data stored in the object.
*
* @note If you configure the SDK with static or environment credentials,
* the credential data should already be present in {credentials} attribute.
* This method is primarily necessary to load credentials from asynchronous
* sources, or sources that can refresh credentials periodically.
* @example Getting your access key
* AWS.config.getCredentials(function(err) {
* if (err) console.log(err.stack); // credentials not loaded
* else console.log("Access Key:", AWS.config.credentials.accessKeyId);
* })
* @callback callback function(err)
* Called when the {credentials} have been properly set on the configuration
* object.
*
* @param err [Error] if this is set, credentials were not successfuly
* loaded and this error provides information why.
* @see credentials
* @see Credentials
*/
getCredentials: function getCredentials(callback) {
var self = this;
function finish(err) {
callback(err, err ? null : self.credentials);
}
function credError(msg, err) {
return new AWS.util.error(err || new Error(), {
code: 'CredentialsError', message: msg
});
}
function getAsyncCredentials() {
self.credentials.get(function(err) {
if (err) {
var msg = 'Could not load credentials from ' +
self.credentials.constructor.name;
err = credError(msg, err);
}
finish(err);
});
}
function getStaticCredentials() {
var err = null;
if (!self.credentials.accessKeyId || !self.credentials.secretAccessKey) {
err = credError('Missing credentials');
}
finish(err);
}
if (self.credentials) {
if (typeof self.credentials.get === 'function') {
getAsyncCredentials();
} else { // static credentials
getStaticCredentials();
}
} else if (self.credentialProvider) {
self.credentialProvider.resolve(function(err, creds) {
if (err) {
err = credError('Could not load credentials from any providers', err);
}
self.credentials = creds;
finish(err);
});
} else {
finish(credError('No credentials to load'));
}
},
/**
* @!group Loading and Setting Configuration Options
*/
/**
* @overload update(options, allowUnknownKeys = false)
* Updates the current configuration object with new options.
*
* @example Update maxRetries property of a configuration object
* config.update({maxRetries: 10});
* @param [Object] options a map of option keys and values.
* @param [Boolean] allowUnknownKeys whether unknown keys can be set on
* the configuration object. Defaults to `false`.
* @see constructor
*/
update: function update(options, allowUnknownKeys) {
allowUnknownKeys = allowUnknownKeys || false;
options = this.extractCredentials(options);
AWS.util.each.call(this, options, function (key, value) {
if (allowUnknownKeys || Object.prototype.hasOwnProperty.call(this.keys, key) ||
AWS.Service.hasService(key)) {
this.set(key, value);
}
});
},
/**
* Loads configuration data from a JSON file into this config object.
* @note Loading configuration will reset all existing configuration
* on the object.
* @!macro nobrowser
* @param path [String] the path relative to your process's current
* working directory to load configuration from.
* @return [AWS.Config] the same configuration object
*/
loadFromPath: function loadFromPath(path) {
this.clear();
var options = JSON.parse(AWS.util.readFileSync(path));
var fileSystemCreds = new AWS.FileSystemCredentials(path);
var chain = new AWS.CredentialProviderChain();
chain.providers.unshift(fileSystemCreds);
chain.resolve(function (err, creds) {
if (err) throw err;
else options.credentials = creds;
});
this.constructor(options);
return this;
},
/**
* Clears configuration data on this object
*
* @api private
*/
clear: function clear() {
/*jshint forin:false */
AWS.util.each.call(this, this.keys, function (key) {
delete this[key];
});
// reset credential provider
this.set('credentials', undefined);
this.set('credentialProvider', undefined);
},
/**
* Sets a property on the configuration object, allowing for a
* default value
* @api private
*/
set: function set(property, value, defaultValue) {
if (value === undefined) {
if (defaultValue === undefined) {
defaultValue = this.keys[property];
}
if (typeof defaultValue === 'function') {
this[property] = defaultValue.call(this);
} else {
this[property] = defaultValue;
}
} else if (property === 'httpOptions' && this[property]) {
// deep merge httpOptions
this[property] = AWS.util.merge(this[property], value);
} else {
this[property] = value;
}
},
/**
* All of the keys with their default values.
*
* @constant
* @api private
*/
keys: {
credentials: null,
credentialProvider: null,
region: null,
logger: null,
apiVersions: {},
apiVersion: null,
endpoint: undefined,
httpOptions: {
timeout: 120000
},
maxRetries: undefined,
maxRedirects: 10,
paramValidation: true,
sslEnabled: true,
s3ForcePathStyle: false,
s3BucketEndpoint: false,
s3DisableBodySigning: true,
computeChecksums: true,
convertResponseTypes: true,
correctClockSkew: false,
customUserAgent: null,
dynamoDbCrc32: true,
systemClockOffset: 0,
signatureVersion: null,
signatureCache: true,
retryDelayOptions: {
base: 100
},
useAccelerateEndpoint: false
},
/**
* Extracts accessKeyId, secretAccessKey and sessionToken
* from a configuration hash.
*
* @api private
*/
extractCredentials: function extractCredentials(options) {
if (options.accessKeyId && options.secretAccessKey) {
options = AWS.util.copy(options);
options.credentials = new AWS.Credentials(options);
}
return options;
},
/**
* Sets the promise dependency the SDK will use wherever Promises are returned.
* Passing `null` will force the SDK to use native Promises if they are available.
* If native Promises are not available, passing `null` will have no effect.
* @param [Constructor] dep A reference to a Promise constructor
*/
setPromisesDependency: function setPromisesDependency(dep) {
PromisesDependency = dep;
// if null was passed in, we should try to use native promises
if (dep === null && typeof Promise === 'function') {
PromisesDependency = Promise;
}
var constructors = [AWS.Request, AWS.Credentials, AWS.CredentialProviderChain];
if (AWS.S3 && AWS.S3.ManagedUpload) constructors.push(AWS.S3.ManagedUpload);
AWS.util.addPromises(constructors, PromisesDependency);
},
/**
* Gets the promise dependency set by `AWS.config.setPromisesDependency`.
*/
getPromisesDependency: function getPromisesDependency() {
return PromisesDependency;
}
});
/**
* @return [AWS.Config] The global configuration object singleton instance
* @readonly
* @see AWS.Config
*/
AWS.config = new AWS.Config();

View File

@@ -0,0 +1,53 @@
import * as AWS from '../clients/all';
export abstract class ConfigurationServicePlaceholders {
log?: AWS.Log.Types.ClientConfiguration;
logadmin?: AWS.LogAdmin.Types.ClientConfiguration;
push?: AWS.Push.Types.ClientConfiguration;
account?: AWS.Account.Types.ClientConfiguration;
accountadmin?: AWS.AccountAdmin.Types.ClientConfiguration;
backup?: AWS.Backup.Types.ClientConfiguration;
notification?: AWS.Notification.Types.ClientConfiguration;
update?: AWS.Update.Types.ClientConfiguration;
updateadmin?: AWS.UpdateAdmin.Types.ClientConfiguration;
robot?: AWS.Robot.Types.ClientConfiguration;
robotadmin?: AWS.RobotAdmin.Types.ClientConfiguration;
key?: AWS.Key.Types.ClientConfiguration;
loop?: AWS.Loop.Types.ClientConfiguration;
media?: AWS.Media.Types.ClientConfiguration;
mediaadmin?: AWS.MediaAdmin.Types.ClientConfiguration;
person?: AWS.Person.Types.ClientConfiguration;
gqa?: AWS.GQA.Types.ClientConfiguration;
oobe?: AWS.OOBE.Types.ClientConfiguration;
nlp?: AWS.NLP.Types.ClientConfiguration;
collision?: AWS.Collision.Types.ClientConfiguration;
ifttt?: AWS.IFTTT.Types.ClientConfiguration;
oobeadmin?: AWS.OOBEAdmin.Types.ClientConfiguration;
rom?: AWS.ROM.Types.ClientConfiguration;
oauthclientsadmin?: AWS.OauthClientsAdmin.Types.ClientConfiguration;
}
export interface ConfigurationServiceApiVersions {
log?: AWS.Log.Types.apiVersion;
logadmin?: AWS.LogAdmin.Types.apiVersion;
push?: AWS.Push.Types.apiVersion;
account?: AWS.Account.Types.apiVersion;
accountadmin?: AWS.AccountAdmin.Types.apiVersion;
backup?: AWS.Backup.Types.apiVersion;
notification?: AWS.Notification.Types.apiVersion;
update?: AWS.Update.Types.apiVersion;
updateadmin?: AWS.UpdateAdmin.Types.apiVersion;
robot?: AWS.Robot.Types.apiVersion;
robotadmin?: AWS.RobotAdmin.Types.apiVersion;
key?: AWS.Key.Types.apiVersion;
loop?: AWS.Loop.Types.apiVersion;
media?: AWS.Media.Types.apiVersion;
mediaadmin?: AWS.MediaAdmin.Types.apiVersion;
person?: AWS.Person.Types.apiVersion;
gqa?: AWS.GQA.Types.apiVersion;
oobe?: AWS.OOBE.Types.apiVersion;
nlp?: AWS.NLP.Types.apiVersion;
collision?: AWS.Collision.Types.apiVersion;
ifttt?: AWS.IFTTT.Types.apiVersion;
oobeadmin?: AWS.OOBEAdmin.Types.apiVersion;
rom?: AWS.ROM.Types.apiVersion;
oauthclientsadmin?: AWS.OauthClientsAdmin.Types.apiVersion;
}

View File

@@ -0,0 +1,8 @@
export interface UseDualstackConfigOptions {
/**
* Enables IPv6/IPv4 dualstack endpoint. When a DNS lookup is performed on an endpoint of this type, it returns an “A” record with an IPv4 address and an “AAAA” record with an IPv6 address.
* In most cases the network stack in the client environment will automatically prefer the AAAA record and make a connection using the IPv6 address.
* Note, however, that currently on Windows, the IPv4 address will be preferred.
*/
useDualStack?: boolean;
}

View File

@@ -0,0 +1,14 @@
export {Config} from './config';
export {Credentials} from './credentials';
export {CredentialProviderChain} from './credentials/credential_provider_chain';
export {EnvironmentCredentials} from './credentials/environment_credentials';
export {FileSystemCredentials} from './credentials/file_system_credentials';
export {SharedIniFileCredentials} from './credentials/shared_ini_file_credentials';
export {Endpoint} from './endpoint';
export {EventListeners} from './event_listeners';
export {HttpRequest} from './http_request';
export {HttpResponse} from './http_response';
export {MetadataService} from './metadata_service';
export {Request} from './request';
export {Response} from './response';
export {Service} from './service';

View File

@@ -0,0 +1,103 @@
/**
* The main AWS namespace
*/
var AWS = { util: require('./util') };
/**
* @api private
* @!macro [new] nobrowser
* @note This feature is not supported in the browser environment of the SDK.
*/
var _hidden = {}; _hidden.toString(); // hack to parse macro
module.exports = AWS;
AWS.util.update(AWS, {
/**
* @constant
*/
VERSION: require('../package.json').version,
/**
* @api private
*/
Signers: {},
/**
* @api private
*/
Protocol: {
Json: require('./protocol/json'),
Query: require('./protocol/query'),
Rest: require('./protocol/rest'),
RestJson: require('./protocol/rest_json'),
RestXml: require('./protocol/rest_xml')
},
/**
* @api private
*/
XML: {
Builder: require('./xml/builder'),
Parser: null // conditionally set based on environment
},
/**
* @api private
*/
JSON: {
Builder: require('./json/builder'),
Parser: require('./json/parser')
},
/**
* @api private
*/
Model: {
Api: require('./model/api'),
Operation: require('./model/operation'),
Shape: require('./model/shape'),
Paginator: require('./model/paginator'),
ResourceWaiter: require('./model/resource_waiter')
},
util: require('./util'),
/**
* @api private
*/
apiLoader: function() { throw new Error('No API loader set'); }
});
require('./service');
require('./config');
require('./credentials');
require('./credentials/credential_provider_chain');
require('./http');
require('./sequential_executor');
require('./event_listeners');
require('./request');
require('./response');
require('./resource_waiter');
require('./signers/request_signer');
require('./param_validator');
/**
* @readonly
* @return [AWS.SequentialExecutor] a collection of global event listeners that
* are attached to every sent request.
* @see AWS.Request AWS.Request for a list of events to listen for
* @example Logging the time taken to send a request
* AWS.events.on('send', function startSend(resp) {
* resp.startTime = new Date().getTime();
* }).on('complete', function calculateTime(resp) {
* var time = (new Date().getTime() - resp.startTime) / 1000;
* console.log('Request took ' + time + ' seconds');
* });
*
* new AWS.S3().listBuckets(); // prints 'Request took 0.285 seconds'
*/
AWS.events = new AWS.SequentialExecutor();

View File

@@ -0,0 +1,74 @@
import {AWSError} from './error';
export class Credentials {
/**
* Creates a Credentials object with a given set of credential information as an options hash.
*
* @param {object} options - An option hash containing a set of credential information.
*/
constructor(options: CredentialsOptions);
/**
* Creates a Credentials object with a given set of credential information as positional arguments.
* *
* @param {string} accessKeyId - The AWS access key ID.
* @param {string} secretAccessKey - The AWS secret access key.
* @param {string} sessionToken - The optional AWS session token.
*/
constructor(accessKeyId: string, secretAccessKey: string, sessionToken?: string);
/**
* Gets the existing credentials, refreshing them if they are not yet loaded or have expired.
* Users should call this method before using refresh(), as this will not attempt to reload
* credentials when they are already loaded into the object.
*
* @param {get} callback - Called when the instance metadata service responds. When called with no error, the credentials information has been loaded into the object.
*/
get(callback: (err: AWSError) => void): void;
/**
* Returns whether the credentials object should call refresh()
*/
needsRefresh(): boolean;
/**
* Refreshes the credentials.
* Users should call get() before attempting to forcibly refresh credentials.
*
* @param {function} callback - Called when the instance metadata service responds. When called with no error, the credentials information has been loaded into the object.
*/
refresh(callback: (err: AWSError) => void): void;
/**
* AWS access key ID.
*/
accessKeyId: string
/**
* Whether the credentials have been expired and require a refresh.
* Used in conjunction with expireTime.
*/
expired: boolean
/**
* Time when credentials should be considered expired.
* Used in conjunction with expired.
*/
expireTime: Date
static expiryWindow: number
/**
* AWS secret access key.
*/
secretAccessKey: string
/**
* AWS session token.
*/
sessionToken: string
}
interface CredentialsOptions {
/**
* AWS access key ID.
*/
accessKeyId: string
/**
* AWS secret access key.
*/
secretAccessKey: string
/**
* AWS session token.
*/
sessionToken?: string
}

View File

@@ -0,0 +1,214 @@
var AWS = require('./core');
/**
* Represents your AWS security credentials, specifically the
* {accessKeyId}, {secretAccessKey}, and optional {sessionToken}.
* Creating a `Credentials` object allows you to pass around your
* security information to configuration and service objects.
*
* Note that this class typically does not need to be constructed manually,
* as the {AWS.Config} and {AWS.Service} classes both accept simple
* options hashes with the three keys. These structures will be converted
* into Credentials objects automatically.
*
* ## Expiring and Refreshing Credentials
*
* Occasionally credentials can expire in the middle of a long-running
* application. In this case, the SDK will automatically attempt to
* refresh the credentials from the storage location if the Credentials
* class implements the {refresh} method.
*
* If you are implementing a credential storage location, you
* will want to create a subclass of the `Credentials` class and
* override the {refresh} method. This method allows credentials to be
* retrieved from the backing store, be it a file system, database, or
* some network storage. The method should reset the credential attributes
* on the object.
*
* @!attribute expired
* @return [Boolean] whether the credentials have been expired and
* require a refresh. Used in conjunction with {expireTime}.
* @!attribute expireTime
* @return [Date] a time when credentials should be considered expired. Used
* in conjunction with {expired}.
* @!attribute accessKeyId
* @return [String] the AWS access key ID
* @!attribute secretAccessKey
* @return [String] the AWS secret access key
* @!attribute sessionToken
* @return [String] an optional AWS session token
*/
AWS.Credentials = AWS.util.inherit({
/**
* A credentials object can be created using positional arguments or an options
* hash.
*
* @overload AWS.Credentials(accessKeyId, secretAccessKey, sessionToken=null)
* Creates a Credentials object with a given set of credential information
* as positional arguments.
* @param accessKeyId [String] the AWS access key ID
* @param secretAccessKey [String] the AWS secret access key
* @param sessionToken [String] the optional AWS session token
* @example Create a credentials object with AWS credentials
* var creds = new AWS.Credentials('akid', 'secret', 'session');
* @overload AWS.Credentials(options)
* Creates a Credentials object with a given set of credential information
* as an options hash.
* @option options accessKeyId [String] the AWS access key ID
* @option options secretAccessKey [String] the AWS secret access key
* @option options sessionToken [String] the optional AWS session token
* @example Create a credentials object with AWS credentials
* var creds = new AWS.Credentials({
* accessKeyId: 'akid', secretAccessKey: 'secret', sessionToken: 'session'
* });
*/
constructor: function Credentials() {
// hide secretAccessKey from being displayed with util.inspect
AWS.util.hideProperties(this, ['secretAccessKey']);
this.expired = false;
this.expireTime = null;
if (arguments.length === 1 && typeof arguments[0] === 'object') {
var creds = arguments[0].credentials || arguments[0];
this.accessKeyId = creds.accessKeyId;
this.secretAccessKey = creds.secretAccessKey;
this.sessionToken = creds.sessionToken;
} else {
this.accessKeyId = arguments[0];
this.secretAccessKey = arguments[1];
this.sessionToken = arguments[2];
}
},
/**
* @return [Integer] the window size in seconds to attempt refreshing of
* credentials before the expireTime occurs.
*/
expiryWindow: 15,
/**
* @return [Boolean] whether the credentials object should call {refresh}
* @note Subclasses should override this method to provide custom refresh
* logic.
*/
needsRefresh: function needsRefresh() {
var currentTime = AWS.util.date.getDate().getTime();
var adjustedTime = new Date(currentTime + this.expiryWindow * 1000);
if (this.expireTime && adjustedTime > this.expireTime) {
return true;
} else {
return this.expired || !this.accessKeyId || !this.secretAccessKey;
}
},
/**
* Gets the existing credentials, refreshing them if they are not yet loaded
* or have expired. Users should call this method before using {refresh},
* as this will not attempt to reload credentials when they are already
* loaded into the object.
*
* @callback callback function(err)
* When this callback is called with no error, it means either credentials
* do not need to be refreshed or refreshed credentials information has
* been loaded into the object (as the `accessKeyId`, `secretAccessKey`,
* and `sessionToken` properties).
* @param err [Error] if an error occurred, this value will be filled
*/
get: function get(callback) {
var self = this;
if (this.needsRefresh()) {
this.refresh(function(err) {
if (!err) self.expired = false; // reset expired flag
if (callback) callback(err);
});
} else if (callback) {
callback();
}
},
/**
* @!method getPromise()
* Returns a 'thenable' promise.
* Gets the existing credentials, refreshing them if they are not yet loaded
* or have expired. Users should call this method before using {refresh},
* as this will not attempt to reload credentials when they are already
* loaded into the object.
*
* Two callbacks can be provided to the `then` method on the returned promise.
* The first callback will be called if the promise is fulfilled, and the second
* callback will be called if the promise is rejected.
* @callback fulfilledCallback function()
* Called if the promise is fulfilled. When this callback is called, it
* means either credentials do not need to be refreshed or refreshed
* credentials information has been loaded into the object (as the
* `accessKeyId`, `secretAccessKey`, and `sessionToken` properties).
* @callback rejectedCallback function(err)
* Called if the promise is rejected.
* @param err [Error] if an error occurred, this value will be filled
* @return [Promise] A promise that represents the state of the `get` call.
* @example Calling the `getPromise` method.
* var promise = credProvider.getPromise();
* promise.then(function() { ... }, function(err) { ... });
*/
/**
* @!method refreshPromise()
* Returns a 'thenable' promise.
* Refreshes the credentials. Users should call {get} before attempting
* to forcibly refresh credentials.
*
* Two callbacks can be provided to the `then` method on the returned promise.
* The first callback will be called if the promise is fulfilled, and the second
* callback will be called if the promise is rejected.
* @callback fulfilledCallback function()
* Called if the promise is fulfilled. When this callback is called, it
* means refreshed credentials information has been loaded into the object
* (as the `accessKeyId`, `secretAccessKey`, and `sessionToken` properties).
* @callback rejectedCallback function(err)
* Called if the promise is rejected.
* @param err [Error] if an error occurred, this value will be filled
* @return [Promise] A promise that represents the state of the `refresh` call.
* @example Calling the `refreshPromise` method.
* var promise = credProvider.refreshPromise();
* promise.then(function() { ... }, function(err) { ... });
*/
/**
* Refreshes the credentials. Users should call {get} before attempting
* to forcibly refresh credentials.
*
* @callback callback function(err)
* When this callback is called with no error, it means refreshed
* credentials information has been loaded into the object (as the
* `accessKeyId`, `secretAccessKey`, and `sessionToken` properties).
* @param err [Error] if an error occurred, this value will be filled
* @note Subclasses should override this class to reset the
* {accessKeyId}, {secretAccessKey} and optional {sessionToken}
* on the credentials object and then call the callback with
* any error information.
* @see get
*/
refresh: function refresh(callback) {
this.expired = false;
callback();
}
});
/**
* @api private
*/
AWS.Credentials.addPromisesToClass = function addPromisesToClass(PromiseDependency) {
this.prototype.getPromise = AWS.util.promisifyMethod('get', PromiseDependency);
this.prototype.refreshPromise = AWS.util.promisifyMethod('refresh', PromiseDependency);
};
/**
* @api private
*/
AWS.Credentials.deletePromisesFromClass = function deletePromisesFromClass() {
delete this.prototype.getPromise;
delete this.prototype.refreshPromise;
};
AWS.util.addPromises(AWS.Credentials);

View File

@@ -0,0 +1,27 @@
export class Endpoint {
/**
* Constructs a new endpoint given an endpoint URL.
*/
constructor(url: string);
/**
* The host portion of the endpoint including the port, e.g., example.com:80.
*/
host: string;
/**
* The host portion of the endpoint, e.g., example.com.
*/
hostname: string;
/**
* The full URL of the endpoint.
*/
href: string;
/**
* The port of the endpoint.
*/
port: number;
/**
* The protocol (http or https) of the endpoint URL.
*/
protocol: string;
}

View File

@@ -0,0 +1,49 @@
/**
* A structure containing information about a service or networking error.
*/
export class AWSError extends Error {
/**
* A unique short code representing the error that was emitted.
*/
code: string;
/**
* A longer human readable error message.
*/
message: string;
/**
* Whether the error message is retryable.
*/
retryable: boolean;
/**
* In the case of a request that reached the service, this value contains the response status code.
*/
statusCode: number;
/**
* The date time object when the error occurred.
*/
time: Date;
/**
* Set when a networking error occurs to easily identify the endpoint of the request.
*/
hostname: string;
/**
* Set when a networking error occurs to easily identify the region of the request.
*/
region: string;
/**
* Amount of time (in seconds) that the request waited before being resent.
*/
retryDelay: number;
/**
* The unique request ID associated with the response.
*/
requestId: string;
/**
* Second request ID associated with the response from S3.
*/
extendedRequstId: string;
/**
* CloudFront request ID associated with the response.
*/
cfId: string;
}

View File

@@ -0,0 +1,35 @@
/**
* The namespace used to register global event listeners for request building and sending.
*/
export namespace EventListeners {
/**
* The namespace used to register global event listeners for request building and sending.
*/
export namespace CORE {
/**
* Removes an event listener from all requests.
*/
export function removeListener(eventName: string, eventListener: Function): void;
/**
* A request listener that reads data from the HTTP connection in order to build the response data. Handles the 'httpData' Request event.
* Remove this handler if you are overriding the 'httpData' event and do not want extra data processing and buffering overhead.
*/
export function HTTP_DATA(): void;
/**
* A request listener that initiates the HTTP connection for a request being sent. Handles the 'send' Request event.
*/
export function SEND(): void;
/**
* A request listener that validates whether the request is being sent with credentials. Handles the 'validate' Request event
*/
export function VALIDATE_CREDENTIALS(): void;
/**
* A request listener that validates input parameters in a request. Handles the 'validate' Request event.
*/
export function VALIDATE_PARAMETERS(): void;
/**
* A request listener that validates whether the region is set for a request. Handles the 'validate' Request event.
*/
export function VALIDATE_REGION():void;
}
}

View File

@@ -0,0 +1,498 @@
var AWS = require('./core');
var SequentialExecutor = require('./sequential_executor');
var uuid = require('uuid');
/**
* The namespace used to register global event listeners for request building
* and sending.
*/
AWS.EventListeners = {
/**
* @!attribute VALIDATE_CREDENTIALS
* A request listener that validates whether the request is being
* sent with credentials.
* Handles the {AWS.Request~validate 'validate' Request event}
* @example Sending a request without validating credentials
* var listener = AWS.EventListeners.Core.VALIDATE_CREDENTIALS;
* request.removeListener('validate', listener);
* @readonly
* @return [Function]
* @!attribute VALIDATE_REGION
* A request listener that validates whether the region is set
* for a request.
* Handles the {AWS.Request~validate 'validate' Request event}
* @example Sending a request without validating region configuration
* var listener = AWS.EventListeners.Core.VALIDATE_REGION;
* request.removeListener('validate', listener);
* @readonly
* @return [Function]
* @!attribute VALIDATE_PARAMETERS
* A request listener that validates input parameters in a request.
* Handles the {AWS.Request~validate 'validate' Request event}
* @example Sending a request without validating parameters
* var listener = AWS.EventListeners.Core.VALIDATE_PARAMETERS;
* request.removeListener('validate', listener);
* @example Disable parameter validation globally
* AWS.EventListeners.Core.removeListener('validate',
* AWS.EventListeners.Core.VALIDATE_REGION);
* @readonly
* @return [Function]
* @!attribute SEND
* A request listener that initiates the HTTP connection for a
* request being sent. Handles the {AWS.Request~send 'send' Request event}
* @example Replacing the HTTP handler
* var listener = AWS.EventListeners.Core.SEND;
* request.removeListener('send', listener);
* request.on('send', function(response) {
* customHandler.send(response);
* });
* @return [Function]
* @readonly
* @!attribute HTTP_DATA
* A request listener that reads data from the HTTP connection in order
* to build the response data.
* Handles the {AWS.Request~httpData 'httpData' Request event}.
* Remove this handler if you are overriding the 'httpData' event and
* do not want extra data processing and buffering overhead.
* @example Disabling default data processing
* var listener = AWS.EventListeners.Core.HTTP_DATA;
* request.removeListener('httpData', listener);
* @return [Function]
* @readonly
*/
Core: {} /* doc hack */
};
AWS.EventListeners = {
Core: new SequentialExecutor().addNamedListeners(function(add, addAsync) {
addAsync('VALIDATE_CREDENTIALS', 'validate',
function VALIDATE_CREDENTIALS(req, done) {
if (!req.service.api.signatureVersion) return done(); // none
req.service.config.getCredentials(function(err) {
if (err) {
req.response.error = AWS.util.error(err,
{code: 'CredentialsError', message: 'Missing credentials in config'});
}
done();
});
});
add('VALIDATE_REGION', 'validate', function VALIDATE_REGION(req) {
if (!req.service.config.region && !req.service.isGlobalEndpoint) {
req.response.error = AWS.util.error(new Error(),
{code: 'ConfigError', message: 'Missing region in config'});
}
});
add('BUILD_IDEMPOTENCY_TOKENS', 'validate', function BUILD_IDEMPOTENCY_TOKENS(req) {
var operation = req.service.api.operations[req.operation];
if (!operation) {
return;
}
var idempotentMembers = operation.idempotentMembers;
if (!idempotentMembers.length) {
return;
}
// creates a copy of params so user's param object isn't mutated
var params = AWS.util.copy(req.params);
for (var i = 0, iLen = idempotentMembers.length; i < iLen; i++) {
if (!params[idempotentMembers[i]]) {
// add the member
params[idempotentMembers[i]] = uuid.v4();
}
}
req.params = params;
});
add('VALIDATE_PARAMETERS', 'validate', function VALIDATE_PARAMETERS(req) {
var rules = req.service.api.operations[req.operation].input;
var validation = req.service.config.paramValidation;
new AWS.ParamValidator(validation).validate(rules, req.params);
});
addAsync('COMPUTE_SHA256', 'afterBuild', function COMPUTE_SHA256(req, done) {
req.haltHandlersOnError();
if (!req.service.api.signatureVersion) return done(); // none
if (req.service.getSignerClass(req) === AWS.Signers.V4) {
var body = req.httpRequest.body || '';
AWS.util.computeSha256(body, function(err, sha) {
if (err) {
done(err);
}
else {
req.httpRequest.headers['X-Amz-Content-Sha256'] = sha;
done();
}
});
} else {
done();
}
});
add('SET_CONTENT_LENGTH', 'afterBuild', function SET_CONTENT_LENGTH(req) {
if (req.httpRequest.headers['Content-Length'] === undefined) {
var length;
try {
length = AWS.util.string.byteLength(req.httpRequest.body);
} catch (e) {
// We just won't add Content-Length to request if cannot be determined
}
if (length) {
req.httpRequest.headers['Content-Length'] = length;
}
}
});
add('SET_HTTP_HOST', 'afterBuild', function SET_HTTP_HOST(req) {
req.httpRequest.headers['Host'] = req.httpRequest.endpoint.host;
});
add('RESTART', 'restart', function RESTART() {
var err = this.response.error;
if (!err || !err.retryable) return;
this.httpRequest = new AWS.HttpRequest(
this.service.endpoint,
this.service.region
);
if (this.response.retryCount < this.service.config.maxRetries) {
this.response.retryCount++;
} else {
this.response.error = null;
}
});
addAsync('SIGN', 'sign', function SIGN(req, done) {
var service = req.service;
if (!service.api.signatureVersion) return done(); // none
service.config.getCredentials(function (err, credentials) {
if (err) {
req.response.error = err;
return done();
}
try {
var date = AWS.util.date.getDate();
var SignerClass = service.getSignerClass(req);
var signer = new SignerClass(req.httpRequest,
service.api.signingName || service.api.endpointPrefix,
service.config.signatureCache);
signer.setServiceClientId(service._clientId);
// clear old authorization headers
delete req.httpRequest.headers['Authorization'];
delete req.httpRequest.headers['Date'];
delete req.httpRequest.headers['X-Amz-Date'];
// add new authorization
signer.addAuthorization(credentials, date);
req.signedAt = date;
} catch (e) {
req.response.error = e;
}
done();
});
});
add('VALIDATE_RESPONSE', 'validateResponse', function VALIDATE_RESPONSE(resp) {
if (this.service.successfulResponse(resp, this)) {
resp.data = {};
resp.error = null;
} else {
resp.data = null;
resp.error = AWS.util.error(new Error(),
{code: 'UnknownError', message: 'An unknown error occurred.'});
}
});
addAsync('SEND', 'send', function SEND(resp, done) {
resp.httpResponse._abortCallback = done;
resp.error = null;
resp.data = null;
function callback(httpResp) {
resp.httpResponse.stream = httpResp;
httpResp.on('headers', function onHeaders(statusCode, headers) {
resp.request.emit('httpHeaders', [statusCode, headers, resp]);
if (!resp.httpResponse.streaming) {
if (AWS.HttpClient.streamsApiVersion === 2) { // streams2 API check
httpResp.on('readable', function onReadable() {
var data = httpResp.read();
if (data !== null) {
resp.request.emit('httpData', [data, resp]);
}
});
} else { // legacy streams API
httpResp.on('data', function onData(data) {
resp.request.emit('httpData', [data, resp]);
});
}
}
});
httpResp.on('end', function onEnd() {
resp.request.emit('httpDone');
done();
});
}
function progress(httpResp) {
httpResp.on('sendProgress', function onSendProgress(value) {
resp.request.emit('httpUploadProgress', [value, resp]);
});
httpResp.on('receiveProgress', function onReceiveProgress(value) {
resp.request.emit('httpDownloadProgress', [value, resp]);
});
}
function error(err) {
resp.error = AWS.util.error(err, {
code: 'NetworkingError',
region: resp.request.httpRequest.region,
hostname: resp.request.httpRequest.endpoint.hostname,
retryable: true
});
resp.request.emit('httpError', [resp.error, resp], function() {
done();
});
}
function executeSend() {
var http = AWS.HttpClient.getInstance();
var httpOptions = resp.request.service.config.httpOptions || {};
try {
var stream = http.handleRequest(resp.request.httpRequest, httpOptions,
callback, error);
progress(stream);
} catch (err) {
error(err);
}
}
var timeDiff = (AWS.util.date.getDate() - this.signedAt) / 1000;
if (timeDiff >= 60 * 10) { // if we signed 10min ago, re-sign
this.emit('sign', [this], function(err) {
if (err) done(err);
else executeSend();
});
} else {
executeSend();
}
});
add('HTTP_HEADERS', 'httpHeaders',
function HTTP_HEADERS(statusCode, headers, resp) {
resp.httpResponse.statusCode = statusCode;
resp.httpResponse.headers = headers;
resp.httpResponse.body = new AWS.util.Buffer('');
resp.httpResponse.buffers = [];
resp.httpResponse.numBytes = 0;
var dateHeader = headers.date || headers.Date;
if (dateHeader) {
var serverTime = Date.parse(dateHeader);
if (resp.request.service.config.correctClockSkew
&& AWS.util.isClockSkewed(serverTime)) {
AWS.util.applyClockOffset(serverTime);
}
}
});
add('HTTP_DATA', 'httpData', function HTTP_DATA(chunk, resp) {
if (chunk) {
if (AWS.util.isNode()) {
resp.httpResponse.numBytes += chunk.length;
var total = resp.httpResponse.headers['content-length'];
var progress = { loaded: resp.httpResponse.numBytes, total: total };
resp.request.emit('httpDownloadProgress', [progress, resp]);
}
resp.httpResponse.buffers.push(new AWS.util.Buffer(chunk));
}
});
add('HTTP_DONE', 'httpDone', function HTTP_DONE(resp) {
// convert buffers array into single buffer
if (resp.httpResponse.buffers && resp.httpResponse.buffers.length > 0) {
var body = AWS.util.buffer.concat(resp.httpResponse.buffers);
resp.httpResponse.body = body;
}
delete resp.httpResponse.numBytes;
delete resp.httpResponse.buffers;
});
add('FINALIZE_ERROR', 'retry', function FINALIZE_ERROR(resp) {
if (resp.httpResponse.statusCode) {
resp.error.statusCode = resp.httpResponse.statusCode;
if (resp.error.retryable === undefined) {
resp.error.retryable = this.service.retryableError(resp.error, this);
}
}
});
add('INVALIDATE_CREDENTIALS', 'retry', function INVALIDATE_CREDENTIALS(resp) {
if (!resp.error) return;
switch (resp.error.code) {
case 'RequestExpired': // EC2 only
case 'ExpiredTokenException':
case 'ExpiredToken':
resp.error.retryable = true;
resp.request.service.config.credentials.expired = true;
}
});
add('EXPIRED_SIGNATURE', 'retry', function EXPIRED_SIGNATURE(resp) {
var err = resp.error;
if (!err) return;
if (typeof err.code === 'string' && typeof err.message === 'string') {
if (err.code.match(/Signature/) && err.message.match(/expired/)) {
resp.error.retryable = true;
}
}
});
add('CLOCK_SKEWED', 'retry', function CLOCK_SKEWED(resp) {
if (!resp.error) return;
if (this.service.clockSkewError(resp.error)
&& this.service.config.correctClockSkew
&& AWS.config.isClockSkewed) {
resp.error.retryable = true;
}
});
add('REDIRECT', 'retry', function REDIRECT(resp) {
if (resp.error && resp.error.statusCode >= 300 &&
resp.error.statusCode < 400 && resp.httpResponse.headers['location']) {
this.httpRequest.endpoint =
new AWS.Endpoint(resp.httpResponse.headers['location']);
this.httpRequest.headers['Host'] = this.httpRequest.endpoint.host;
resp.error.redirect = true;
resp.error.retryable = true;
}
});
add('RETRY_CHECK', 'retry', function RETRY_CHECK(resp) {
if (resp.error) {
if (resp.error.redirect && resp.redirectCount < resp.maxRedirects) {
resp.error.retryDelay = 0;
} else if (resp.retryCount < resp.maxRetries) {
resp.error.retryDelay = this.service.retryDelays(resp.retryCount) || 0;
}
}
});
addAsync('RESET_RETRY_STATE', 'afterRetry', function RESET_RETRY_STATE(resp, done) {
var delay, willRetry = false;
if (resp.error) {
delay = resp.error.retryDelay || 0;
if (resp.error.retryable && resp.retryCount < resp.maxRetries) {
resp.retryCount++;
willRetry = true;
} else if (resp.error.redirect && resp.redirectCount < resp.maxRedirects) {
resp.redirectCount++;
willRetry = true;
}
}
if (willRetry) {
resp.error = null;
setTimeout(done, delay);
} else {
done();
}
});
}),
CorePost: new SequentialExecutor().addNamedListeners(function(add) {
add('EXTRACT_REQUEST_ID', 'extractData', AWS.util.extractRequestId);
add('EXTRACT_REQUEST_ID', 'extractError', AWS.util.extractRequestId);
add('ENOTFOUND_ERROR', 'httpError', function ENOTFOUND_ERROR(err) {
if (err.code === 'NetworkingError' && err.errno === 'ENOTFOUND') {
var message = 'Inaccessible host: `' + err.hostname +
'\'. This service may not be available in the `' + err.region +
'\' region.';
this.response.error = AWS.util.error(new Error(message), {
code: 'UnknownEndpoint',
region: err.region,
hostname: err.hostname,
retryable: true,
originalError: err
});
}
});
}),
Logger: new SequentialExecutor().addNamedListeners(function(add) {
add('LOG_REQUEST', 'complete', function LOG_REQUEST(resp) {
var req = resp.request;
var logger = req.service.config.logger;
if (!logger) return;
function buildMessage() {
var time = AWS.util.date.getDate().getTime();
var delta = (time - req.startTime.getTime()) / 1000;
var ansi = logger.isTTY ? true : false;
var status = resp.httpResponse.statusCode;
var params = require('util').inspect(req.params, true, null);
var message = '';
if (ansi) message += '\x1B[33m';
message += '[AWS ' + req.service.serviceIdentifier + ' ' + status;
message += ' ' + delta.toString() + 's ' + resp.retryCount + ' retries]';
if (ansi) message += '\x1B[0;1m';
message += ' ' + AWS.util.string.lowerFirst(req.operation);
message += '(' + params + ')';
if (ansi) message += '\x1B[0m';
return message;
}
var line = buildMessage();
if (typeof logger.log === 'function') {
logger.log(line);
} else if (typeof logger.write === 'function') {
logger.write(line + '\n');
}
});
}),
Json: new SequentialExecutor().addNamedListeners(function(add) {
var svc = require('./protocol/json');
add('BUILD', 'build', svc.buildRequest);
add('EXTRACT_DATA', 'extractData', svc.extractData);
add('EXTRACT_ERROR', 'extractError', svc.extractError);
}),
Rest: new SequentialExecutor().addNamedListeners(function(add) {
var svc = require('./protocol/rest');
add('BUILD', 'build', svc.buildRequest);
add('EXTRACT_DATA', 'extractData', svc.extractData);
add('EXTRACT_ERROR', 'extractError', svc.extractError);
}),
RestJson: new SequentialExecutor().addNamedListeners(function(add) {
var svc = require('./protocol/rest_json');
add('BUILD', 'build', svc.buildRequest);
add('EXTRACT_DATA', 'extractData', svc.extractData);
add('EXTRACT_ERROR', 'extractError', svc.extractError);
}),
RestXml: new SequentialExecutor().addNamedListeners(function(add) {
var svc = require('./protocol/rest_xml');
add('BUILD', 'build', svc.buildRequest);
add('EXTRACT_DATA', 'extractData', svc.extractData);
add('EXTRACT_ERROR', 'extractError', svc.extractError);
}),
Query: new SequentialExecutor().addNamedListeners(function(add) {
var svc = require('./protocol/query');
add('BUILD', 'build', svc.buildRequest);
add('EXTRACT_DATA', 'extractData', svc.extractData);
add('EXTRACT_ERROR', 'extractError', svc.extractError);
})
};

View File

@@ -0,0 +1,208 @@
var AWS = require('./core');
var inherit = AWS.util.inherit;
/**
* The endpoint that a service will talk to, for example,
* `'https://ec2.ap-southeast-1.amazonaws.com'`. If
* you need to override an endpoint for a service, you can
* set the endpoint on a service by passing the endpoint
* object with the `endpoint` option key:
*
* ```javascript
* var ep = new AWS.Endpoint('awsproxy.example.com');
* var s3 = new AWS.S3({endpoint: ep});
* s3.service.endpoint.hostname == 'awsproxy.example.com'
* ```
*
* Note that if you do not specify a protocol, the protocol will
* be selected based on your current {AWS.config} configuration.
*
* @!attribute protocol
* @return [String] the protocol (http or https) of the endpoint
* URL
* @!attribute hostname
* @return [String] the host portion of the endpoint, e.g.,
* example.com
* @!attribute host
* @return [String] the host portion of the endpoint including
* the port, e.g., example.com:80
* @!attribute port
* @return [Integer] the port of the endpoint
* @!attribute href
* @return [String] the full URL of the endpoint
*/
AWS.Endpoint = inherit({
/**
* @overload Endpoint(endpoint)
* Constructs a new endpoint given an endpoint URL. If the
* URL omits a protocol (http or https), the default protocol
* set in the global {AWS.config} will be used.
* @param endpoint [String] the URL to construct an endpoint from
*/
constructor: function Endpoint(endpoint, config) {
AWS.util.hideProperties(this, ['slashes', 'auth', 'hash', 'search', 'query']);
if (typeof endpoint === 'undefined' || endpoint === null) {
throw new Error('Invalid endpoint: ' + endpoint);
} else if (typeof endpoint !== 'string') {
return AWS.util.copy(endpoint);
}
if (!endpoint.match(/^http/)) {
var useSSL = config && config.sslEnabled !== undefined ?
config.sslEnabled : AWS.config.sslEnabled;
endpoint = (useSSL ? 'https' : 'http') + '://' + endpoint;
}
AWS.util.update(this, AWS.util.urlParse(endpoint));
// Ensure the port property is set as an integer
if (this.port) {
this.port = parseInt(this.port, 10);
} else {
this.port = this.protocol === 'https:' ? 443 : 80;
}
}
});
/**
* The low level HTTP request object, encapsulating all HTTP header
* and body data sent by a service request.
*
* @!attribute method
* @return [String] the HTTP method of the request
* @!attribute path
* @return [String] the path portion of the URI, e.g.,
* "/list/?start=5&num=10"
* @!attribute headers
* @return [map<String,String>]
* a map of header keys and their respective values
* @!attribute body
* @return [String] the request body payload
* @!attribute endpoint
* @return [AWS.Endpoint] the endpoint for the request
* @!attribute region
* @api private
* @return [String] the region, for signing purposes only.
*/
AWS.HttpRequest = inherit({
/**
* @api private
*/
constructor: function HttpRequest(endpoint, region, customUserAgent) {
endpoint = new AWS.Endpoint(endpoint);
this.method = 'POST';
this.path = endpoint.path || '/';
this.headers = {};
this.body = '';
this.endpoint = endpoint;
this.region = region;
this.setUserAgent(customUserAgent);
},
/**
* @api private
*/
setUserAgent: function setUserAgent(customUserAgent) {
var prefix = AWS.util.isBrowser() ? 'X-Amz-' : '';
var customSuffix = '';
if (typeof customUserAgent === 'string' && customUserAgent) {
customSuffix += ' ' + customUserAgent;
}
this.headers[prefix + 'User-Agent'] = AWS.util.userAgent() + customSuffix;
},
/**
* @return [String] the part of the {path} excluding the
* query string
*/
pathname: function pathname() {
return this.path.split('?', 1)[0];
},
/**
* @return [String] the query string portion of the {path}
*/
search: function search() {
var query = this.path.split('?', 2)[1];
if (query) {
query = AWS.util.queryStringParse(query);
return AWS.util.queryParamsToString(query);
}
return '';
}
});
/**
* The low level HTTP response object, encapsulating all HTTP header
* and body data returned from the request.
*
* @!attribute statusCode
* @return [Integer] the HTTP status code of the response (e.g., 200, 404)
* @!attribute headers
* @return [map<String,String>]
* a map of response header keys and their respective values
* @!attribute body
* @return [String] the response body payload
* @!attribute [r] streaming
* @return [Boolean] whether this response is being streamed at a low-level.
* Defaults to `false` (buffered reads). Do not modify this manually, use
* {createUnbufferedStream} to convert the stream to unbuffered mode
* instead.
*/
AWS.HttpResponse = inherit({
/**
* @api private
*/
constructor: function HttpResponse() {
this.statusCode = undefined;
this.headers = {};
this.body = undefined;
this.streaming = false;
this.stream = null;
},
/**
* Disables buffering on the HTTP response and returns the stream for reading.
* @return [Stream, XMLHttpRequest, null] the underlying stream object.
* Use this object to directly read data off of the stream.
* @note This object is only available after the {AWS.Request~httpHeaders}
* event has fired. This method must be called prior to
* {AWS.Request~httpData}.
* @example Taking control of a stream
* request.on('httpHeaders', function(statusCode, headers) {
* if (statusCode < 300) {
* if (headers.etag === 'xyz') {
* // pipe the stream, disabling buffering
* var stream = this.response.httpResponse.createUnbufferedStream();
* stream.pipe(process.stdout);
* } else { // abort this request and set a better error message
* this.abort();
* this.response.error = new Error('Invalid ETag');
* }
* }
* }).send(console.log);
*/
createUnbufferedStream: function createUnbufferedStream() {
this.streaming = true;
return this.stream;
}
});
AWS.HttpClient = inherit({});
/**
* @api private
*/
AWS.HttpClient.getInstance = function getInstance() {
if (this.singleton === undefined) {
this.singleton = new this();
}
return this.singleton;
};

View File

@@ -0,0 +1,36 @@
import {Endpoint} from './endpoint';
/**
* The low level HTTP request object, encapsulating all HTTP header and body data sent by a service request.
*/
export class HttpRequest {
/**
* The part of the path excluding the query string.
*/
pathname(): string;
/**
* The query string portion of the path.
*/
search: string;
/**
* The request body payload.
*/
body: string;
/**
* The endpoint for the request.
*/
endpoint: Endpoint;
/**
* A map of header keys and their respective values.
*/
headers: {
[key: string]: string;
}
/**
* The HTTP method of the request.
*/
method: string;
/**
* The path portion of the URI, e.g., "/list/?start=5&num=10".
*/
path: string;
}

View File

@@ -0,0 +1,29 @@
import * as stream from 'stream';
interface XMLHttpRequest {}
/**
* The low level HTTP response object, encapsulating all HTTP header and body data returned from the request.
*/
export class HttpResponse {
/**
* Disables buffering on the HTTP response and returns the stream for reading.
*/
createUnbufferedStream(): stream.Readable|XMLHttpRequest
/**
* The response body payload.
*/
body: string|Buffer|Uint8Array;
/**
* A map of response header keys and their respective values.
*/
headers: {
[key: string]: string;
}
/**
* The HTTP status code of the response (e.g., 200, 404).
*/
statusCode: number;
/**
* Whether this response is being streamed at a low-level.
*/
streaming: boolean;
}

View File

@@ -0,0 +1,52 @@
import {AWSError} from './error';
/**
* Represents a metadata service available on EC2 instances. Using the request() method, you can receieve metadata about any available resource on the metadata service.
*/
export class MetadataService {
/**
* Creates a new MetadataService object with a given set of options.
*/
constructor(options?: MetadataServiceOptions);
/**
* Sends a request to the instance metadata service for a given resource.
*/
request(path: string, callback: (err: AWSError, data: string) => void): void;
/**
* 169.254.169.254
*/
static host: string
/**
* A map of options to pass to the underlying HTTP request.
*/
httpOptions: {
/**
* a timeout value in milliseconds to wait before aborting the connection. Set to 0 for no timeout.
*/
timeout: number;
}
}
interface MetadataServiceOptions {
/**
* the hostname of the instance metadata service.
*/
host?: string;
/**
* a map of options to pass to the underlying HTTP request.
*/
httpOptions?: {
/**
* a timeout value in milliseconds to wait before aborting the connection. Set to 0 for no timeout.
*/
timeout?: number;
}
/**
* the maximum number of retries to perform for timeout errors.
*/
maxRetries?: number;
/**
* A set of options to configure the retry delay on retryable errors. See AWS.Config for details.
*/
retryDelayOptions?: any
}

View File

@@ -0,0 +1,121 @@
var AWS = require('./core');
require('./http');
var inherit = AWS.util.inherit;
/**
* Represents a metadata service available on EC2 instances. Using the
* {request} method, you can receieve metadata about any available resource
* on the metadata service.
*
* @!attribute [r] httpOptions
* @return [map] a map of options to pass to the underlying HTTP request:
*
* * **timeout** (Number) &mdash; a timeout value in milliseconds to wait
* before aborting the connection. Set to 0 for no timeout.
*
* @!macro nobrowser
*/
AWS.MetadataService = inherit({
/**
* @return [String] the hostname of the instance metadata service
*/
host: '169.254.169.254',
/**
* @!ignore
*/
/**
* Default HTTP options. By default, the metadata service is set to not
* timeout on long requests. This means that on non-EC2 machines, this
* request will never return. If you are calling this operation from an
* environment that may not always run on EC2, set a `timeout` value so
* the SDK will abort the request after a given number of milliseconds.
*/
httpOptions: { timeout: 0 },
/**
* Creates a new MetadataService object with a given set of options.
*
* @option options host [String] the hostname of the instance metadata
* service
* @option options httpOptions [map] a map of options to pass to the
* underlying HTTP request:
*
* * **timeout** (Number) &mdash; a timeout value in milliseconds to wait
* before aborting the connection. Set to 0 for no timeout.
* @option options maxRetries [Integer] the maximum number of retries to
* perform for timeout errors
* @option options retryDelayOptions [map] A set of options to configure the
* retry delay on retryable errors. See AWS.Config for details.
*/
constructor: function MetadataService(options) {
AWS.util.update(this, options);
},
/**
* Sends a request to the instance metadata service for a given resource.
*
* @param path [String] the path of the resource to get
* @callback callback function(err, data)
* Called when a response is available from the service.
* @param err [Error, null] if an error occurred, this value will be set
* @param data [String, null] if the request was successful, the body of
* the response
*/
request: function request(path, callback) {
path = path || '/';
var httpRequest = new AWS.HttpRequest('http://' + this.host + path);
httpRequest.method = 'GET';
AWS.util.handleRequestWithRetries(httpRequest, this, callback);
},
/**
* @api private
*/
loadCredentialsCallbacks: [],
/**
* Loads a set of credentials stored in the instance metadata service
*
* @api private
* @callback callback function(err, credentials)
* Called when credentials are loaded from the resource
* @param err [Error] if an error occurred, this value will be set
* @param credentials [Object] the raw JSON object containing all
* metadata from the credentials resource
*/
loadCredentials: function loadCredentials(callback) {
var self = this;
var basePath = '/latest/meta-data/iam/security-credentials/';
self.loadCredentialsCallbacks.push(callback);
if (self.loadCredentialsCallbacks.length > 1) { return; }
function callbacks(err, creds) {
var cb;
while ((cb = self.loadCredentialsCallbacks.shift()) !== undefined) {
cb(err, creds);
}
}
self.request(basePath, function (err, roleName) {
if (err) callbacks(err);
else {
roleName = roleName.split('\n')[0]; // grab first (and only) role
self.request(basePath + roleName, function (credErr, credData) {
if (credErr) callbacks(credErr);
else {
try {
var credentials = JSON.parse(credData);
callbacks(null, credentials);
} catch (parseError) {
callbacks(parseError);
}
}
});
}
});
}
});
module.exports = AWS.MetadataService;

View File

@@ -0,0 +1,59 @@
var util = require('./util');
// node.js specific modules
util.crypto.lib = require('crypto');
util.Buffer = require('buffer').Buffer;
util.domain = require('domain');
util.stream = require('stream');
util.url = require('url');
util.querystring = require('querystring');
var AWS = require('./core');
// Use default API loader function
require('./api_loader');
// Load the xml2js XML parser
AWS.XML.Parser = require('./xml/node_parser');
// Load Node HTTP client
require('./http/node');
// Load custom credential providers
require('./credentials/environment_credentials');
require('./credentials/file_system_credentials');
require('./credentials/shared_ini_file_credentials');
// Setup default chain providers
// If this changes, please update documentation for
// AWS.CredentialProviderChain.defaultProviders in
// credentials/credential_provider_chain.js
AWS.CredentialProviderChain.defaultProviders = [
function () { return new AWS.EnvironmentCredentials('AWS'); },
function () { return new AWS.EnvironmentCredentials('AMAZON'); },
function () { return new AWS.SharedIniFileCredentials(); }
];
// Update configuration keys
AWS.util.update(AWS.Config.prototype.keys, {
credentials: function () {
var credentials = null;
new AWS.CredentialProviderChain([
function () { return new AWS.EnvironmentCredentials('AWS'); },
function () { return new AWS.EnvironmentCredentials('AMAZON'); },
function () { return new AWS.SharedIniFileCredentials({ disableAssumeRole: true }); }
]).resolve(function(err, creds) {
if (!err) credentials = creds;
});
return credentials;
},
credentialProvider: function() {
return new AWS.CredentialProviderChain();
},
region: function() {
return process.env.AWS_REGION || process.env.AMAZON_REGION;
}
});
// Reset configuration
AWS.config = new AWS.Config();

View File

@@ -0,0 +1,256 @@
var AWS = require('./core');
/**
* @api private
*/
AWS.ParamValidator = AWS.util.inherit({
/**
* Create a new validator object.
*
* @param validation [Boolean|map] whether input parameters should be
* validated against the operation description before sending the
* request. Pass a map to enable any of the following specific
* validation features:
*
* * **min** [Boolean] &mdash; Validates that a value meets the min
* constraint. This is enabled by default when paramValidation is set
* to `true`.
* * **max** [Boolean] &mdash; Validates that a value meets the max
* constraint.
* * **pattern** [Boolean] &mdash; Validates that a string value matches a
* regular expression.
* * **enum** [Boolean] &mdash; Validates that a string value matches one
* of the allowable enum values.
*/
constructor: function ParamValidator(validation) {
if (validation === true || validation === undefined) {
validation = {'min': true};
}
this.validation = validation;
},
validate: function validate(shape, params, context) {
this.errors = [];
this.validateMember(shape, params || {}, context || 'params');
if (this.errors.length > 1) {
var msg = this.errors.join('\n* ');
msg = 'There were ' + this.errors.length +
' validation errors:\n* ' + msg;
throw AWS.util.error(new Error(msg),
{code: 'MultipleValidationErrors', errors: this.errors});
} else if (this.errors.length === 1) {
throw this.errors[0];
} else {
return true;
}
},
fail: function fail(code, message) {
this.errors.push(AWS.util.error(new Error(message), {code: code}));
},
validateStructure: function validateStructure(shape, params, context) {
this.validateType(params, context, ['object'], 'structure');
var paramName;
for (var i = 0; shape.required && i < shape.required.length; i++) {
paramName = shape.required[i];
var value = params[paramName];
if (value === undefined || value === null) {
this.fail('MissingRequiredParameter',
'Missing required key \'' + paramName + '\' in ' + context);
}
}
// validate hash members
for (paramName in params) {
if (!Object.prototype.hasOwnProperty.call(params, paramName)) continue;
var paramValue = params[paramName],
memberShape = shape.members[paramName];
if (memberShape !== undefined) {
var memberContext = [context, paramName].join('.');
this.validateMember(memberShape, paramValue, memberContext);
} else {
this.fail('UnexpectedParameter',
'Unexpected key \'' + paramName + '\' found in ' + context);
}
}
return true;
},
validateMember: function validateMember(shape, param, context) {
switch (shape.type) {
case 'structure':
return this.validateStructure(shape, param, context);
case 'list':
return this.validateList(shape, param, context);
case 'map':
return this.validateMap(shape, param, context);
default:
return this.validateScalar(shape, param, context);
}
},
validateList: function validateList(shape, params, context) {
if (this.validateType(params, context, [Array])) {
this.validateRange(shape, params.length, context, 'list member count');
// validate array members
for (var i = 0; i < params.length; i++) {
this.validateMember(shape.member, params[i], context + '[' + i + ']');
}
}
},
validateMap: function validateMap(shape, params, context) {
if (this.validateType(params, context, ['object'], 'map')) {
// Build up a count of map members to validate range traits.
var mapCount = 0;
for (var param in params) {
if (!Object.prototype.hasOwnProperty.call(params, param)) continue;
// Validate any map key trait constraints
this.validateMember(shape.key, param,
context + '[key=\'' + param + '\']')
this.validateMember(shape.value, params[param],
context + '[\'' + param + '\']');
mapCount++;
}
this.validateRange(shape, mapCount, context, 'map member count');
}
},
validateScalar: function validateScalar(shape, value, context) {
switch (shape.type) {
case null:
case undefined:
case 'string':
return this.validateString(shape, value, context);
case 'base64':
case 'binary':
return this.validatePayload(value, context);
case 'integer':
case 'float':
return this.validateNumber(shape, value, context);
case 'boolean':
return this.validateType(value, context, ['boolean']);
case 'object':
return this.validateType(value, context, ['object']);
case 'timestamp':
return this.validateType(value, context, [Date,
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/, 'number'],
'Date object, ISO-8601 string, or a UNIX timestamp');
default:
return this.fail('UnkownType', 'Unhandled type ' +
shape.type + ' for ' + context);
}
},
validateString: function validateString(shape, value, context) {
if (this.validateType(value, context, ['string'])) {
this.validateEnum(shape, value, context);
this.validateRange(shape, value.length, context, 'string length');
this.validatePattern(shape, value, context);
}
},
validatePattern: function validatePattern(shape, value, context) {
if (this.validation['pattern'] && shape['pattern'] !== undefined) {
if (!(new RegExp(shape['pattern'])).test(value)) {
this.fail('PatternMatchError', 'Provided value "' + value + '" '
+ 'does not match regex pattern /' + shape['pattern'] + '/ for '
+ context);
}
}
},
validateRange: function validateRange(shape, value, context, descriptor) {
if (this.validation['min']) {
if (shape['min'] !== undefined && value < shape['min']) {
this.fail('MinRangeError', 'Expected ' + descriptor + ' >= '
+ shape['min'] + ', but found ' + value + ' for ' + context);
}
}
if (this.validation['max']) {
if (shape['max'] !== undefined && value > shape['max']) {
this.fail('MaxRangeError', 'Expected ' + descriptor + ' <= '
+ shape['max'] + ', but found ' + value + ' for ' + context);
}
}
},
validateEnum: function validateRange(shape, value, context) {
if (this.validation['enum'] && shape['enum'] !== undefined) {
// Fail if the string value is not present in the enum list
if (shape['enum'].indexOf(value) === -1) {
this.fail('EnumError', 'Found string value of ' + value + ', but '
+ 'expected ' + shape['enum'].join('|') + ' for ' + context);
}
}
},
validateType: function validateType(value, context, acceptedTypes, type) {
// We will not log an error for null or undefined, but we will return
// false so that callers know that the expected type was not strictly met.
if (value === null || value === undefined) return false;
var foundInvalidType = false;
for (var i = 0; i < acceptedTypes.length; i++) {
if (typeof acceptedTypes[i] === 'string') {
if (typeof value === acceptedTypes[i]) return true;
} else if (acceptedTypes[i] instanceof RegExp) {
if ((value || '').toString().match(acceptedTypes[i])) return true;
} else {
if (value instanceof acceptedTypes[i]) return true;
if (AWS.util.isType(value, acceptedTypes[i])) return true;
if (!type && !foundInvalidType) acceptedTypes = acceptedTypes.slice();
acceptedTypes[i] = AWS.util.typeName(acceptedTypes[i]);
}
foundInvalidType = true;
}
var acceptedType = type;
if (!acceptedType) {
acceptedType = acceptedTypes.join(', ').replace(/,([^,]+)$/, ', or$1');
}
var vowel = acceptedType.match(/^[aeiou]/i) ? 'n' : '';
this.fail('InvalidParameterType', 'Expected ' + context + ' to be a' +
vowel + ' ' + acceptedType);
return false;
},
validateNumber: function validateNumber(shape, value, context) {
if (value === null || value === undefined) return;
if (typeof value === 'string') {
var castedValue = parseFloat(value);
if (castedValue.toString() === value) value = castedValue;
}
if (this.validateType(value, context, ['number'])) {
this.validateRange(shape, value, context, 'numeric value');
}
},
validatePayload: function validatePayload(value, context) {
if (value === null || value === undefined) return;
if (typeof value === 'string') return;
if (value && typeof value.byteLength === 'number') return; // typed arrays
if (AWS.util.isNode()) { // special check for buffer/stream in Node.js
var Stream = AWS.util.stream.Stream;
if (AWS.util.Buffer.isBuffer(value) || value instanceof Stream) return;
}
var types = ['Buffer', 'Stream', 'File', 'Blob', 'ArrayBuffer', 'DataView'];
if (value) {
for (var i = 0; i < types.length; i++) {
if (AWS.util.isType(value, types[i])) return;
if (AWS.util.typeName(value.constructor) === types[i]) return;
}
}
this.fail('InvalidParameterType', 'Expected ' + context + ' to be a ' +
'string, Buffer, Stream, Blob, or typed array object');
}
});

View File

@@ -0,0 +1,69 @@
var util = require('./util');
var regionConfig = require('./region_config.json');
function generateRegionPrefix(region) {
if (!region) return null;
var parts = region.split('-');
if (parts.length < 3) return null;
return parts.slice(0, parts.length - 2).join('-') + '-*';
}
function derivedKeys(service) {
var region = service.config.region;
var regionPrefix = generateRegionPrefix(region);
var endpointPrefix = service.api.endpointPrefix;
return [
[region, endpointPrefix],
[regionPrefix, endpointPrefix],
[region, '*'],
[regionPrefix, '*'],
['*', endpointPrefix],
['*', '*']
].map(function(item) {
return item[0] && item[1] ? item.join('/') : null;
});
}
function applyConfig(service, config) {
util.each(config, function(key, value) {
if (key === 'globalEndpoint') return;
if (service.config[key] === undefined || service.config[key] === null) {
service.config[key] = value;
}
});
}
function configureEndpoint(service) {
var keys = derivedKeys(service);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (!key) continue;
if (Object.prototype.hasOwnProperty.call(regionConfig.rules, key)) {
var config = regionConfig.rules[key];
if (typeof config === 'string') {
config = regionConfig.patterns[config];
}
// set dualstack endpoint
if (service.config.useDualstack && util.isDualstackAvailable(service)) {
config = util.copy(config);
config.endpoint = '{service}.dualstack.{region}.amazonaws.com';
}
// set global endpoint
service.isGlobalEndpoint = !!config.globalEndpoint;
// signature version
if (!config.signatureVersion) config.signatureVersion = 'v4';
// merge config
applyConfig(service, config);
return;
}
}
}
module.exports = configureEndpoint;

View File

@@ -0,0 +1,30 @@
{
"rules": {
"local/*": {
"endpoint": "http://localhost:8080",
"wsendpoint": "ws://localhost:8090"
},
"internal/*": {
"endpoint": "http://security:8080",
"wsendpoint": "ws://socket:8090"
},
"mock-region/*": {
"endpoint": "{service}.{region}.api.jibo.com",
"wsendpoint": "ws://socket:8090"
},
"*/*": "globalSSL"
},
"patterns": {
"globalSSL": {
"endpoint": "https://{region}.jibo.com",
"wsendpoint": "wss://{region}-socket.jibo.com",
"globalEndpoint": true
},
"global": {
"endpoint": "http://{region}.jibo.com:8080",
"wsendpoint": "ws://{region}-socket.jibo.com:8090",
"globalEndpoint": true
}
}
}

View File

@@ -0,0 +1,179 @@
import * as stream from 'stream';
import {Service} from './service';
import {Response} from './response';
import {HttpRequest} from './http_request';
import {AWSError} from './error';
export class Request<D, E> {
/**
* Creates a request for an operation on a given service with a set of input parameters.
*
* @param {AWS.Service} service - The service to perform the operation on.
* @param {string} operation - The operation to perform on the service.
* @param {object} params - Parameters to send to the operation.
*/
constructor(service: Service, operation: string, params?: any);
/**
* Aborts a request, emitting the error and complete events.
* This feature is not supported in the browser environment of the SDK.
*/
abort(): void;
/**
* Converts the request object into a readable stream that can be read from or piped into a writable stream.
* The data read from a readable stream contains only the raw HTTP body contents.
* This feature is not supported in the browser environment of the SDK.
*/
createReadStream(): stream.Readable;
/**
* Iterates over each page of results given a pageable request, calling the provided callback with each page of data.
* After all pages have been retrieved, the callback is called with null data.
*
* @param {eachPage} callback - The callback that handles the response.
*/
eachPage(callback: (err: E, data: D, doneCallback?: () => void) => boolean): void;
/**
* Returns whether the operation can return multiple pages of response data.
*/
isPageable(): boolean;
/**
* Sends the request object.
* If a callback is supplied, it is called when a response is returned from the service.
*/
send(callback?: (err: E, data: D) => void): void;
/**
* Adds a listener that is triggered when a request emits the specified event.
*
* @param {string} event - 'Name of a request event.'
* @param {function} listener - Callback to run when the event is triggered on the request.
*/
on(event: string, listener: () => void): Request<D, E>;
/**
* Adds a listener that is triggered when a request is being validated.
*
* @param {string} event - validate: triggered when a request is being validated.
* @param {function} listener - Callback to run when the request is being validated.
*/
on(event: "validate", listener: (request: Request<D, E>) => void): Request<D, E>;
/**
* Adds a listener that is triggered when the request payload is being built.
*
* @param {string} event - build: triggered when the request payload is being built.
* @param {function} listener - Callback to run when the request's payload is being built.
*/
on(event: "build", listener: (request: Request<D, E>) => void): Request<D, E>;
/**
* Adds a listener that is triggered when a request is being signed.
*
* @param {string} event - sign: triggered when a request is being signed.
* @param {function} listener - Callback to run when the request is being signed.
*/
on(event: "sign", listener: (request: Request<D, E>) => void): Request<D, E>;
/**
* Adds a listener that is triggered when a request is ready to be sent.
*
* @param {string} event - send: triggered when a request is ready to be sent.
* @param {function} listener - Callback to run when the request is ready to be sent.
*/
on(event: "send", listener: (response: Response<D, E>) => void): Request<D, E>;
/**
* Adds a listener that is triggered when a request failed and might need to be retried or redirected.
*
* @param {string} event - retry: triggered when a request failed and might need to be retried or redirected.
* @param {function} listener - Callback to run when the request failed and may be retried.
*/
on(event: "retry", listener: (response: Response<D, E>) => void): Request<D, E>;
/**
* Adds a listener that is triggered on all non-2xx requests so that listeners can extract error details from the response body.
*
* @param {string} event - extractError: triggered on all non-2xx requests so that listeners can extract error details from the response body.
* @param {function} listener - Callback to run when the request failed.
*/
on(event: "extractError", listener: (response: Response<D, E>) => void): Request<D, E>;
/**
* Adds a listener that is triggered in successful requests to allow listeners to de-serialize the response body into response.data.
*
* @param {string} event - extractData: triggered in successful requests to allow listeners to de-serialize the response body into response.data.
* @param {function} listener - Callback to run when the request succeeded.
*/
on(event: "extractData", listener: (response: Response<D, E>) => void): Request<D, E>;
/**
* Adds a listener that is triggered when the request completed successfully.
*
* @param {string} event - success: triggered when the request completed successfully.
* @param {function} listener - Callback to run when the request completed successfully.
*/
on(event: "success", listener: (response: Response<D, E>) => void): Request<D, E>;
/**
* Adds a listener that is triggered when an error occurs at any point during the request.
*
* @param {string} event - error: triggered when an error occurs at any point during the request.
* @param {function} listener - Callback to run when the request errors at any point.
*/
on(event: "error", listener: (err: AWSError, response: Response<D, E>) => void): Request<D, E>;
/**
* Adds a listener that is triggered whenever a request cycle completes.
*
* @param {string} event - complete: triggered whenever a request cycle completes.
* @param {function} listener - Callback to run when the request cycle completes.
*/
on(event: "complete", listener: (response: Response<D, E>) => void): Request<D, E>;
/**
* Adds a listener that is triggered when headers are sent by the remote server.
*
* @param {string} event - httpHeaders: triggered when headers are sent by the remote server.
* @param {function} listener - Callback to run when the headers are sent by the remote server.
*/
on(event: "httpHeaders", listener: (statusCode: number, headers: {[key: string]: string}, response: Response<D, E>) => void): Request<D, E>;
/**
* Adds a listener that is triggered when data is sent by the remote server.
*
* @param {string} event - httpData: triggered when data is sent by the remote server.
* @param {function} listener - Callback to run when data is sent by the remote server.
*/
on(event: "httpData", listener: (chunk: Buffer|Uint8Array, response: Response<D, E>) => void): Request<D, E>;
/**
* Adds a listener that is triggered when the HTTP request has uploaded more data.
*
* @param {string} event - httpUploadProgress: triggered when the HTTP request has uploaded more data.
* @param {function} listener - Callback to run when the HTTP request has uploaded more data.
*/
on(event: "httpUploadProgress", listener: (progress: Progress, response: Response<D, E>) => void): Request<D, E>;
/**
* Adds a listener that is triggered when the HTTP request has downloaded more data.
*
* @param {string} event - httpDownloadProgress: triggered when the HTTP request has downloaded more data.
* @param {function} listener - Callback to run when the HTTP request has downloaded more data.
*/
on(event: "httpDownloadProgress", listener: (progress: Progress, response: Response<D, E>) => void): Request<D, E>;
/**
* Adds a listener that is triggered when the HTTP request failed.
*
* @param {string} event - httpError: triggered when the HTTP request failed.
* @param {function} listener - Callback to run when the HTTP request failed.
*/
on(event: "httpError", listener: (err: Error, response: Response<D, E>) => void): Request<D, E>;
/**
* Adds a listener that is triggered when the server is finished sending data.
*
* @param {string} event - httpDone: triggered when the server is finished sending data.
* @param {function} listener - Callback to run when the server is finished sending data.
*/
on(event: "httpDone", listener: (response: Response<D, E>) => void): Request<D, E>;
/**
* Returns a 'thenable' promise.
*/
promise(): Promise<D>
/**
* The time that the request started.
*/
startTime: Date;
/**
* The raw HTTP request object containing request headers and body information sent by the service.
*/
httpRequest: HttpRequest;
}
export interface Progress {
loaded: number;
total: number;
}

View File

@@ -0,0 +1,782 @@
var AWS = require('./core');
var AcceptorStateMachine = require('./state_machine');
var inherit = AWS.util.inherit;
var domain = AWS.util.domain;
var jmespath = require('jmespath');
/**
* @api private
*/
var hardErrorStates = {success: 1, error: 1, complete: 1};
function isTerminalState(machine) {
return Object.prototype.hasOwnProperty.call(hardErrorStates, machine._asm.currentState);
}
var fsm = new AcceptorStateMachine();
fsm.setupStates = function() {
var transition = function(_, done) {
var self = this;
self._haltHandlersOnError = false;
self.emit(self._asm.currentState, function(err) {
if (err) {
if (isTerminalState(self)) {
if (domain && self.domain instanceof domain.Domain) {
err.domainEmitter = self;
err.domain = self.domain;
err.domainThrown = false;
self.domain.emit('error', err);
} else {
throw err;
}
} else {
self.response.error = err;
done(err);
}
} else {
done(self.response.error);
}
});
};
this.addState('validate', 'build', 'error', transition);
this.addState('build', 'afterBuild', 'restart', transition);
this.addState('afterBuild', 'sign', 'restart', transition);
this.addState('sign', 'send', 'retry', transition);
this.addState('retry', 'afterRetry', 'afterRetry', transition);
this.addState('afterRetry', 'sign', 'error', transition);
this.addState('send', 'validateResponse', 'retry', transition);
this.addState('validateResponse', 'extractData', 'extractError', transition);
this.addState('extractError', 'extractData', 'retry', transition);
this.addState('extractData', 'success', 'retry', transition);
this.addState('restart', 'build', 'error', transition);
this.addState('success', 'complete', 'complete', transition);
this.addState('error', 'complete', 'complete', transition);
this.addState('complete', null, null, transition);
};
fsm.setupStates();
/**
* ## Asynchronous Requests
*
* All requests made through the SDK are asynchronous and use a
* callback interface. Each service method that kicks off a request
* returns an `AWS.Request` object that you can use to register
* callbacks.
*
* For example, the following service method returns the request
* object as "request", which can be used to register callbacks:
*
* ```javascript
* // request is an AWS.Request object
* var request = ec2.describeInstances();
*
* // register callbacks on request to retrieve response data
* request.on('success', function(response) {
* console.log(response.data);
* });
* ```
*
* When a request is ready to be sent, the {send} method should
* be called:
*
* ```javascript
* request.send();
* ```
*
* ## Removing Default Listeners for Events
*
* Request objects are built with default listeners for the various events,
* depending on the service type. In some cases, you may want to remove
* some built-in listeners to customize behaviour. Doing this requires
* access to the built-in listener functions, which are exposed through
* the {AWS.EventListeners.Core} namespace. For instance, you may
* want to customize the HTTP handler used when sending a request. In this
* case, you can remove the built-in listener associated with the 'send'
* event, the {AWS.EventListeners.Core.SEND} listener and add your own.
*
* ## Multiple Callbacks and Chaining
*
* You can register multiple callbacks on any request object. The
* callbacks can be registered for different events, or all for the
* same event. In addition, you can chain callback registration, for
* example:
*
* ```javascript
* request.
* on('success', function(response) {
* console.log("Success!");
* }).
* on('error', function(response) {
* console.log("Error!");
* }).
* on('complete', function(response) {
* console.log("Always!");
* }).
* send();
* ```
*
* The above example will print either "Success! Always!", or "Error! Always!",
* depending on whether the request succeeded or not.
*
* @!attribute httpRequest
* @readonly
* @!group HTTP Properties
* @return [AWS.HttpRequest] the raw HTTP request object
* containing request headers and body information
* sent by the service.
*
* @!attribute startTime
* @readonly
* @!group Operation Properties
* @return [Date] the time that the request started
*
* @!group Request Building Events
*
* @!event validate(request)
* Triggered when a request is being validated. Listeners
* should throw an error if the request should not be sent.
* @param request [Request] the request object being sent
* @see AWS.EventListeners.Core.VALIDATE_CREDENTIALS
* @see AWS.EventListeners.Core.VALIDATE_REGION
* @example Ensuring that a certain parameter is set before sending a request
* var req = s3.putObject(params);
* req.on('validate', function() {
* if (!req.params.Body.match(/^Hello\s/)) {
* throw new Error('Body must start with "Hello "');
* }
* });
* req.send(function(err, data) { ... });
*
* @!event build(request)
* Triggered when the request payload is being built. Listeners
* should fill the necessary information to send the request
* over HTTP.
* @param (see AWS.Request~validate)
* @example Add a custom HTTP header to a request
* var req = s3.putObject(params);
* req.on('build', function() {
* req.httpRequest.headers['Custom-Header'] = 'value';
* });
* req.send(function(err, data) { ... });
*
* @!event sign(request)
* Triggered when the request is being signed. Listeners should
* add the correct authentication headers and/or adjust the body,
* depending on the authentication mechanism being used.
* @param (see AWS.Request~validate)
*
* @!group Request Sending Events
*
* @!event send(response)
* Triggered when the request is ready to be sent. Listeners
* should call the underlying transport layer to initiate
* the sending of the request.
* @param response [Response] the response object
* @context [Request] the request object that was sent
* @see AWS.EventListeners.Core.SEND
*
* @!event retry(response)
* Triggered when a request failed and might need to be retried or redirected.
* If the response is retryable, the listener should set the
* `response.error.retryable` property to `true`, and optionally set
* `response.error.retryCount` to the millisecond delay for the next attempt.
* In the case of a redirect, `response.error.redirect` should be set to
* `true` with `retryCount` set to an optional delay on the next request.
*
* If a listener decides that a request should not be retried,
* it should set both `retryable` and `redirect` to false.
*
* Note that a retryable error will be retried at most
* {AWS.Config.maxRetries} times (based on the service object's config).
* Similarly, a request that is redirected will only redirect at most
* {AWS.Config.maxRedirects} times.
*
* @param (see AWS.Request~send)
* @context (see AWS.Request~send)
* @example Adding a custom retry for a 404 response
* request.on('retry', function(response) {
* // this resource is not yet available, wait 10 seconds to get it again
* if (response.httpResponse.statusCode === 404 && response.error) {
* response.error.retryable = true; // retry this error
* response.error.retryCount = 10000; // wait 10 seconds
* }
* });
*
* @!group Data Parsing Events
*
* @!event extractError(response)
* Triggered on all non-2xx requests so that listeners can extract
* error details from the response body. Listeners to this event
* should set the `response.error` property.
* @param (see AWS.Request~send)
* @context (see AWS.Request~send)
*
* @!event extractData(response)
* Triggered in successful requests to allow listeners to
* de-serialize the response body into `response.data`.
* @param (see AWS.Request~send)
* @context (see AWS.Request~send)
*
* @!group Completion Events
*
* @!event success(response)
* Triggered when the request completed successfully.
* `response.data` will contain the response data and
* `response.error` will be null.
* @param (see AWS.Request~send)
* @context (see AWS.Request~send)
*
* @!event error(error, response)
* Triggered when an error occurs at any point during the
* request. `response.error` will contain details about the error
* that occurred. `response.data` will be null.
* @param error [Error] the error object containing details about
* the error that occurred.
* @param (see AWS.Request~send)
* @context (see AWS.Request~send)
*
* @!event complete(response)
* Triggered whenever a request cycle completes. `response.error`
* should be checked, since the request may have failed.
* @param (see AWS.Request~send)
* @context (see AWS.Request~send)
*
* @!group HTTP Events
*
* @!event httpHeaders(statusCode, headers, response)
* Triggered when headers are sent by the remote server
* @param statusCode [Integer] the HTTP response code
* @param headers [map<String,String>] the response headers
* @param (see AWS.Request~send)
* @context (see AWS.Request~send)
*
* @!event httpData(chunk, response)
* Triggered when data is sent by the remote server
* @param chunk [Buffer] the buffer data containing the next data chunk
* from the server
* @param (see AWS.Request~send)
* @context (see AWS.Request~send)
* @see AWS.EventListeners.Core.HTTP_DATA
*
* @!event httpUploadProgress(progress, response)
* Triggered when the HTTP request has uploaded more data
* @param progress [map] An object containing the `loaded` and `total` bytes
* of the request.
* @param (see AWS.Request~send)
* @context (see AWS.Request~send)
* @note This event will not be emitted in Node.js 0.8.x.
*
* @!event httpDownloadProgress(progress, response)
* Triggered when the HTTP request has downloaded more data
* @param progress [map] An object containing the `loaded` and `total` bytes
* of the request.
* @param (see AWS.Request~send)
* @context (see AWS.Request~send)
* @note This event will not be emitted in Node.js 0.8.x.
*
* @!event httpError(error, response)
* Triggered when the HTTP request failed
* @param error [Error] the error object that was thrown
* @param (see AWS.Request~send)
* @context (see AWS.Request~send)
*
* @!event httpDone(response)
* Triggered when the server is finished sending data
* @param (see AWS.Request~send)
* @context (see AWS.Request~send)
*
* @see AWS.Response
*/
AWS.Request = inherit({
/**
* Creates a request for an operation on a given service with
* a set of input parameters.
*
* @param service [AWS.Service] the service to perform the operation on
* @param operation [String] the operation to perform on the service
* @param params [Object] parameters to send to the operation.
* See the operation's documentation for the format of the
* parameters.
*/
constructor: function Request(service, operation, params) {
var endpoint = service.endpoint;
var region = service.config.region;
var customUserAgent = service.config.customUserAgent;
// global endpoints sign as us-east-1
if (service.isGlobalEndpoint) region = 'us-east-1';
this.domain = domain && domain.active;
this.service = service;
this.operation = operation;
this.params = params || {};
this.httpRequest = new AWS.HttpRequest(endpoint, region, customUserAgent);
this.startTime = AWS.util.date.getDate();
this.response = new AWS.Response(this);
this._asm = new AcceptorStateMachine(fsm.states, 'validate');
this._haltHandlersOnError = false;
AWS.SequentialExecutor.call(this);
this.emit = this.emitEvent;
},
/**
* @!group Sending a Request
*/
/**
* @overload send(callback = null)
* Sends the request object.
*
* @callback callback function(err, data)
* If a callback is supplied, it is called when a response is returned
* from the service.
* @context [AWS.Request] the request object being sent.
* @param err [Error] the error object returned from the request.
* Set to `null` if the request is successful.
* @param data [Object] the de-serialized data returned from
* the request. Set to `null` if a request error occurs.
* @example Sending a request with a callback
* request = s3.putObject({Bucket: 'bucket', Key: 'key'});
* request.send(function(err, data) { console.log(err, data); });
* @example Sending a request with no callback (using event handlers)
* request = s3.putObject({Bucket: 'bucket', Key: 'key'});
* request.on('complete', function(response) { ... }); // register a callback
* request.send();
*/
send: function send(callback) {
if (callback) {
this.on('complete', function (resp) {
callback.call(resp, resp.error, resp.data);
});
}
this.runTo();
return this.response;
},
/**
* @!method promise()
* Returns a 'thenable' promise.
*
* Two callbacks can be provided to the `then` method on the returned promise.
* The first callback will be called if the promise is fulfilled, and the second
* callback will be called if the promise is rejected.
* @callback fulfilledCallback function(data)
* Called if the promise is fulfilled.
* @param data [Object] the de-serialized data returned from the request.
* @callback rejectedCallback function(error)
* Called if the promise is rejected.
* @param error [Error] the error object returned from the request.
* @return [Promise] A promise that represents the state of the request.
* @example Sending a request using promises.
* var request = s3.putObject({Bucket: 'bucket', Key: 'key'});
* var result = request.promise();
* result.then(function(data) { ... }, function(error) { ... });
*/
/**
* @api private
*/
build: function build(callback) {
return this.runTo('send', callback);
},
/**
* @api private
*/
runTo: function runTo(state, done) {
this._asm.runTo(state, done, this);
return this;
},
/**
* Aborts a request, emitting the error and complete events.
*
* @!macro nobrowser
* @example Aborting a request after sending
* var params = {
* Bucket: 'bucket', Key: 'key',
* Body: new Buffer(1024 * 1024 * 5) // 5MB payload
* };
* var request = s3.putObject(params);
* request.send(function (err, data) {
* if (err) console.log("Error:", err.code, err.message);
* else console.log(data);
* });
*
* // abort request in 1 second
* setTimeout(request.abort.bind(request), 1000);
*
* // prints "Error: RequestAbortedError Request aborted by user"
* @return [AWS.Request] the same request object, for chaining.
* @since v1.4.0
*/
abort: function abort() {
this.removeAllListeners('validateResponse');
this.removeAllListeners('extractError');
this.on('validateResponse', function addAbortedError(resp) {
resp.error = AWS.util.error(new Error('Request aborted by user'), {
code: 'RequestAbortedError', retryable: false
});
});
if (this.httpRequest.stream) { // abort HTTP stream
this.httpRequest.stream.abort();
if (this.httpRequest._abortCallback) {
this.httpRequest._abortCallback();
} else {
this.removeAllListeners('send'); // haven't sent yet, so let's not
}
}
return this;
},
/**
* Iterates over each page of results given a pageable request, calling
* the provided callback with each page of data. After all pages have been
* retrieved, the callback is called with `null` data.
*
* @note This operation can generate multiple requests to a service.
* @example Iterating over multiple pages of objects in an S3 bucket
* var pages = 1;
* s3.listObjects().eachPage(function(err, data) {
* if (err) return;
* console.log("Page", pages++);
* console.log(data);
* });
* @example Iterating over multiple pages with an asynchronous callback
* s3.listObjects(params).eachPage(function(err, data, done) {
* doSomethingAsyncAndOrExpensive(function() {
* // The next page of results isn't fetched until done is called
* done();
* });
* });
* @callback callback function(err, data, [doneCallback])
* Called with each page of resulting data from the request. If the
* optional `doneCallback` is provided in the function, it must be called
* when the callback is complete.
*
* @param err [Error] an error object, if an error occurred.
* @param data [Object] a single page of response data. If there is no
* more data, this object will be `null`.
* @param doneCallback [Function] an optional done callback. If this
* argument is defined in the function declaration, it should be called
* when the next page is ready to be retrieved. This is useful for
* controlling serial pagination across asynchronous operations.
* @return [Boolean] if the callback returns `false`, pagination will
* stop.
*
* @see AWS.Request.eachItem
* @see AWS.Response.nextPage
* @since v1.4.0
*/
eachPage: function eachPage(callback) {
// Make all callbacks async-ish
callback = AWS.util.fn.makeAsync(callback, 3);
function wrappedCallback(response) {
callback.call(response, response.error, response.data, function (result) {
if (result === false) return;
if (response.hasNextPage()) {
response.nextPage().on('complete', wrappedCallback).send();
} else {
callback.call(response, null, null, AWS.util.fn.noop);
}
});
}
this.on('complete', wrappedCallback).send();
},
/**
* Enumerates over individual items of a request, paging the responses if
* necessary.
*
* @api experimental
* @since v1.4.0
*/
eachItem: function eachItem(callback) {
var self = this;
function wrappedCallback(err, data) {
if (err) return callback(err, null);
if (data === null) return callback(null, null);
var config = self.service.paginationConfig(self.operation);
var resultKey = config.resultKey;
if (Array.isArray(resultKey)) resultKey = resultKey[0];
var items = jmespath.search(data, resultKey);
var continueIteration = true;
AWS.util.arrayEach(items, function(item) {
continueIteration = callback(null, item);
if (continueIteration === false) {
return AWS.util.abort;
}
});
return continueIteration;
}
this.eachPage(wrappedCallback);
},
/**
* @return [Boolean] whether the operation can return multiple pages of
* response data.
* @see AWS.Response.eachPage
* @since v1.4.0
*/
isPageable: function isPageable() {
return this.service.paginationConfig(this.operation) ? true : false;
},
/**
* Converts the request object into a readable stream that
* can be read from or piped into a writable stream.
*
* @note The data read from a readable stream contains only
* the raw HTTP body contents.
* @example Manually reading from a stream
* request.createReadStream().on('data', function(data) {
* console.log("Got data:", data.toString());
* });
* @example Piping a request body into a file
* var out = fs.createWriteStream('/path/to/outfile.jpg');
* s3.service.getObject(params).createReadStream().pipe(out);
* @return [Stream] the readable stream object that can be piped
* or read from (by registering 'data' event listeners).
* @!macro nobrowser
*/
createReadStream: function createReadStream() {
var streams = AWS.util.stream;
var req = this;
var stream = null;
if (AWS.HttpClient.streamsApiVersion === 2) {
stream = new streams.PassThrough();
req.send();
} else {
stream = new streams.Stream();
stream.readable = true;
stream.sent = false;
stream.on('newListener', function(event) {
if (!stream.sent && event === 'data') {
stream.sent = true;
process.nextTick(function() { req.send(); });
}
});
}
this.on('httpHeaders', function streamHeaders(statusCode, headers, resp) {
if (statusCode < 300) {
req.removeListener('httpData', AWS.EventListeners.Core.HTTP_DATA);
req.removeListener('httpError', AWS.EventListeners.Core.HTTP_ERROR);
req.on('httpError', function streamHttpError(error) {
resp.error = error;
resp.error.retryable = false;
});
var shouldCheckContentLength = false;
var expectedLen;
if (req.httpRequest.method !== 'HEAD') {
expectedLen = parseInt(headers['content-length'], 10);
}
if (expectedLen !== undefined && !isNaN(expectedLen) && expectedLen >= 0) {
shouldCheckContentLength = true;
var receivedLen = 0;
}
var checkContentLengthAndEmit = function checkContentLengthAndEmit() {
if (shouldCheckContentLength && receivedLen !== expectedLen) {
stream.emit('error', AWS.util.error(
new Error('Stream content length mismatch. Received ' +
receivedLen + ' of ' + expectedLen + ' bytes.'),
{ code: 'StreamContentLengthMismatch' }
));
} else if (AWS.HttpClient.streamsApiVersion === 2) {
stream.end();
} else {
stream.emit('end')
}
}
var httpStream = resp.httpResponse.createUnbufferedStream();
if (AWS.HttpClient.streamsApiVersion === 2) {
if (shouldCheckContentLength) {
var lengthAccumulator = new streams.PassThrough();
lengthAccumulator._write = function(chunk) {
if (chunk && chunk.length) {
receivedLen += chunk.length;
}
return streams.PassThrough.prototype._write.apply(this, arguments);
};
lengthAccumulator.on('end', checkContentLengthAndEmit);
httpStream.pipe(lengthAccumulator).pipe(stream, { end: false });
} else {
httpStream.pipe(stream);
}
} else {
if (shouldCheckContentLength) {
httpStream.on('data', function(arg) {
if (arg && arg.length) {
receivedLen += arg.length;
}
});
}
httpStream.on('data', function(arg) {
stream.emit('data', arg);
});
httpStream.on('end', checkContentLengthAndEmit);
}
httpStream.on('error', function(err) {
shouldCheckContentLength = false;
stream.emit('error', err);
});
}
});
this.on('error', function(err) {
stream.emit('error', err);
});
return stream;
},
/**
* @param [Array,Response] args This should be the response object,
* or an array of args to send to the event.
* @api private
*/
emitEvent: function emit(eventName, args, done) {
if (typeof args === 'function') { done = args; args = null; }
if (!done) done = function() { };
if (!args) args = this.eventParameters(eventName, this.response);
var origEmit = AWS.SequentialExecutor.prototype.emit;
origEmit.call(this, eventName, args, function (err) {
if (err) this.response.error = err;
done.call(this, err);
});
},
/**
* @api private
*/
eventParameters: function eventParameters(eventName) {
switch (eventName) {
case 'restart':
case 'validate':
case 'sign':
case 'build':
case 'afterValidate':
case 'afterBuild':
return [this];
case 'error':
return [this.response.error, this.response];
default:
return [this.response];
}
},
/**
* @api private
*/
presign: function presign(expires, callback) {
if (!callback && typeof expires === 'function') {
callback = expires;
expires = null;
}
return new AWS.Signers.Presign().sign(this.toGet(), expires, callback);
},
/**
* @api private
*/
isPresigned: function isPresigned() {
return Object.prototype.hasOwnProperty.call(this.httpRequest.headers, 'presigned-expires');
},
/**
* @api private
*/
toUnauthenticated: function toUnauthenticated() {
this.removeListener('validate', AWS.EventListeners.Core.VALIDATE_CREDENTIALS);
this.removeListener('sign', AWS.EventListeners.Core.SIGN);
return this;
},
/**
* @api private
*/
toGet: function toGet() {
if (this.service.api.protocol === 'query' ||
this.service.api.protocol === 'ec2') {
this.removeListener('build', this.buildAsGet);
this.addListener('build', this.buildAsGet);
}
return this;
},
/**
* @api private
*/
buildAsGet: function buildAsGet(request) {
request.httpRequest.method = 'GET';
request.httpRequest.path = request.service.endpoint.path +
'?' + request.httpRequest.body;
request.httpRequest.body = '';
// don't need these headers on a GET request
delete request.httpRequest.headers['Content-Length'];
delete request.httpRequest.headers['Content-Type'];
},
/**
* @api private
*/
haltHandlersOnError: function haltHandlersOnError() {
this._haltHandlersOnError = true;
}
});
/**
* @api private
*/
AWS.Request.addPromisesToClass = function addPromisesToClass(PromiseDependency) {
this.prototype.promise = function promise() {
var self = this;
return new PromiseDependency(function(resolve, reject) {
self.on('complete', function(resp) {
if (resp.error) {
reject(resp.error);
} else {
resolve(resp.data);
}
});
self.runTo();
});
};
};
/**
* @api private
*/
AWS.Request.deletePromisesFromClass = function deletePromisesFromClass() {
delete this.prototype.promise;
};
AWS.util.addPromises(AWS.Request);
AWS.util.mixin(AWS.Request, AWS.SequentialExecutor);

View File

@@ -0,0 +1,178 @@
/**
* Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You
* may not use this file except in compliance with the License. A copy of
* the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
var AWS = require('./core');
var inherit = AWS.util.inherit;
var jmespath = require('jmespath');
/**
* @api private
*/
function CHECK_ACCEPTORS(resp) {
var waiter = resp.request._waiter;
var acceptors = waiter.config.acceptors;
var acceptorMatched = false;
var state = 'retry';
acceptors.forEach(function(acceptor) {
if (!acceptorMatched) {
var matcher = waiter.matchers[acceptor.matcher];
if (matcher && matcher(resp, acceptor.expected, acceptor.argument)) {
acceptorMatched = true;
state = acceptor.state;
}
}
});
if (!acceptorMatched && resp.error) state = 'failure';
if (state === 'success') {
waiter.setSuccess(resp);
} else {
waiter.setError(resp, state === 'retry');
}
}
/**
* @api private
*/
AWS.ResourceWaiter = inherit({
/**
* Waits for a given state on a service object
* @param service [Service] the service object to wait on
* @param state [String] the state (defined in waiter configuration) to wait
* for.
* @example Create a waiter for running EC2 instances
* var ec2 = new AWS.EC2;
* var waiter = new AWS.ResourceWaiter(ec2, 'instanceRunning');
*/
constructor: function constructor(service, state) {
this.service = service;
this.state = state;
this.loadWaiterConfig(this.state);
},
service: null,
state: null,
config: null,
matchers: {
path: function(resp, expected, argument) {
var result = jmespath.search(resp.data, argument);
return jmespath.strictDeepEqual(result,expected);
},
pathAll: function(resp, expected, argument) {
var results = jmespath.search(resp.data, argument);
if (!Array.isArray(results)) results = [results];
var numResults = results.length;
if (!numResults) return false;
for (var ind = 0 ; ind < numResults; ind++) {
if (!jmespath.strictDeepEqual(results[ind], expected)) {
return false;
}
}
return true;
},
pathAny: function(resp, expected, argument) {
var results = jmespath.search(resp.data, argument);
if (!Array.isArray(results)) results = [results];
var numResults = results.length;
for (var ind = 0 ; ind < numResults; ind++) {
if (jmespath.strictDeepEqual(results[ind], expected)) {
return true;
}
}
return false;
},
status: function(resp, expected) {
var statusCode = resp.httpResponse.statusCode;
return (typeof statusCode === 'number') && (statusCode === expected);
},
error: function(resp, expected) {
if (typeof expected === 'string' && resp.error) {
return expected === resp.error.code;
}
// if expected is not string, can be boolean indicating presence of error
return expected === !!resp.error;
}
},
listeners: new AWS.SequentialExecutor().addNamedListeners(function(add) {
add('RETRY_CHECK', 'retry', function(resp) {
var waiter = resp.request._waiter;
if (resp.error && resp.error.code === 'ResourceNotReady') {
resp.error.retryDelay = (waiter.config.delay || 0) * 1000;
}
});
add('CHECK_OUTPUT', 'extractData', CHECK_ACCEPTORS);
add('CHECK_ERROR', 'extractError', CHECK_ACCEPTORS);
}),
/**
* @return [AWS.Request]
*/
wait: function wait(params, callback) {
if (typeof params === 'function') {
callback = params; params = undefined;
}
var request = this.service.makeRequest(this.config.operation, params);
request._waiter = this;
request.response.maxRetries = this.config.maxAttempts;
request.addListeners(this.listeners);
if (callback) request.send(callback);
return request;
},
setSuccess: function setSuccess(resp) {
resp.error = null;
resp.data = resp.data || {};
resp.request.removeAllListeners('extractData');
},
setError: function setError(resp, retryable) {
resp.data = null;
resp.error = AWS.util.error(resp.error || new Error(), {
code: 'ResourceNotReady',
message: 'Resource is not in the state ' + this.state,
retryable: retryable
});
},
/**
* Loads waiter configuration from API configuration
*
* @api private
*/
loadWaiterConfig: function loadWaiterConfig(state) {
if (!this.service.api.waiters[state]) {
throw new AWS.util.error(new Error(), {
code: 'StateNotFoundError',
message: 'State ' + state + ' not found.'
});
}
this.config = this.service.api.waiters[state];
}
});

View File

@@ -0,0 +1,38 @@
import {HttpResponse} from './http_response';
import {Request} from './request';
export class Response<D, E> {
/**
* Whether more pages of data can be returned by further requests.
*/
hasNextPage(): boolean;
/**
* Creates a new request for the next page of response data, calling the callback with the page data if a callback is provided.
*/
nextPage(callback: (err: E, data: D) => void): Request<D, E>|void;
/**
* The de-serialized response data from the service.
* Can be null if an error occurred.
*/
data: D|void
/**
* A structure containing information about a service or networking error.
*/
error: E|void
/**
* Returns the unique request ID associated with the response.
* Log this value when debugging requests for AWS support.
*/
requestId: string
/**
* The number of redirects that were followed before the request was completed.
*/
redirectCount: number
/**
* The number of retries that were attempted before the request was completed.
*/
retryCount: number
/**
* The raw HTTP response object containing the response headers and body information from the server.
*/
httpResponse: HttpResponse;
}

View File

@@ -0,0 +1,201 @@
var AWS = require('./core');
var inherit = AWS.util.inherit;
var jmespath = require('jmespath');
/**
* This class encapsulates the response information
* from a service request operation sent through {AWS.Request}.
* The response object has two main properties for getting information
* back from a request:
*
* ## The `data` property
*
* The `response.data` property contains the serialized object data
* retrieved from the service request. For instance, for an
* Amazon DynamoDB `listTables` method call, the response data might
* look like:
*
* ```
* > resp.data
* { TableNames:
* [ 'table1', 'table2', ... ] }
* ```
*
* The `data` property can be null if an error occurs (see below).
*
* ## The `error` property
*
* In the event of a service error (or transfer error), the
* `response.error` property will be filled with the given
* error data in the form:
*
* ```
* { code: 'SHORT_UNIQUE_ERROR_CODE',
* message: 'Some human readable error message' }
* ```
*
* In the case of an error, the `data` property will be `null`.
* Note that if you handle events that can be in a failure state,
* you should always check whether `response.error` is set
* before attempting to access the `response.data` property.
*
* @!attribute data
* @readonly
* @!group Data Properties
* @note Inside of a {AWS.Request~httpData} event, this
* property contains a single raw packet instead of the
* full de-serialized service response.
* @return [Object] the de-serialized response data
* from the service.
*
* @!attribute error
* An structure containing information about a service
* or networking error.
* @readonly
* @!group Data Properties
* @note This attribute is only filled if a service or
* networking error occurs.
* @return [Error]
* * code [String] a unique short code representing the
* error that was emitted.
* * message [String] a longer human readable error message
* * retryable [Boolean] whether the error message is
* retryable.
* * statusCode [Numeric] in the case of a request that reached the service,
* this value contains the response status code.
* * time [Date] the date time object when the error occurred.
* * hostname [String] set when a networking error occurs to easily
* identify the endpoint of the request.
* * region [String] set when a networking error occurs to easily
* identify the region of the request.
*
* @!attribute requestId
* @readonly
* @!group Data Properties
* @return [String] the unique request ID associated with the response.
* Log this value when debugging requests for AWS support.
*
* @!attribute retryCount
* @readonly
* @!group Operation Properties
* @return [Integer] the number of retries that were
* attempted before the request was completed.
*
* @!attribute redirectCount
* @readonly
* @!group Operation Properties
* @return [Integer] the number of redirects that were
* followed before the request was completed.
*
* @!attribute httpResponse
* @readonly
* @!group HTTP Properties
* @return [AWS.HttpResponse] the raw HTTP response object
* containing the response headers and body information
* from the server.
*
* @see AWS.Request
*/
AWS.Response = inherit({
/**
* @api private
*/
constructor: function Response(request) {
this.request = request;
this.data = null;
this.error = null;
this.retryCount = 0;
this.redirectCount = 0;
this.httpResponse = new AWS.HttpResponse();
if (request) {
this.maxRetries = request.service.numRetries();
this.maxRedirects = request.service.config.maxRedirects;
}
},
/**
* Creates a new request for the next page of response data, calling the
* callback with the page data if a callback is provided.
*
* @callback callback function(err, data)
* Called when a page of data is returned from the next request.
*
* @param err [Error] an error object, if an error occurred in the request
* @param data [Object] the next page of data, or null, if there are no
* more pages left.
* @return [AWS.Request] the request object for the next page of data
* @return [null] if no callback is provided and there are no pages left
* to retrieve.
* @since v1.4.0
*/
nextPage: function nextPage(callback) {
var config;
var service = this.request.service;
var operation = this.request.operation;
try {
config = service.paginationConfig(operation, true);
} catch (e) { this.error = e; }
if (!this.hasNextPage()) {
if (callback) callback(this.error, null);
else if (this.error) throw this.error;
return null;
}
var params = AWS.util.copy(this.request.params);
if (!this.nextPageTokens) {
return callback ? callback(null, null) : null;
} else {
var inputTokens = config.inputToken;
if (typeof inputTokens === 'string') inputTokens = [inputTokens];
for (var i = 0; i < inputTokens.length; i++) {
params[inputTokens[i]] = this.nextPageTokens[i];
}
return service.makeRequest(this.request.operation, params, callback);
}
},
/**
* @return [Boolean] whether more pages of data can be returned by further
* requests
* @since v1.4.0
*/
hasNextPage: function hasNextPage() {
this.cacheNextPageTokens();
if (this.nextPageTokens) return true;
if (this.nextPageTokens === undefined) return undefined;
else return false;
},
/**
* @api private
*/
cacheNextPageTokens: function cacheNextPageTokens() {
if (Object.prototype.hasOwnProperty.call(this, 'nextPageTokens')) return this.nextPageTokens;
this.nextPageTokens = undefined;
var config = this.request.service.paginationConfig(this.request.operation);
if (!config) return this.nextPageTokens;
this.nextPageTokens = null;
if (config.moreResults) {
if (!jmespath.search(this.data, config.moreResults)) {
return this.nextPageTokens;
}
}
var exprs = config.outputToken;
if (typeof exprs === 'string') exprs = [exprs];
AWS.util.arrayEach.call(this, exprs, function (expr) {
var output = jmespath.search(this.data, expr);
if (output) {
this.nextPageTokens = this.nextPageTokens || [];
this.nextPageTokens.push(output);
}
});
return this.nextPageTokens;
}
});

View File

@@ -0,0 +1,231 @@
var AWS = require('./core');
/**
* @api private
* @!method on(eventName, callback)
* Registers an event listener callback for the event given by `eventName`.
* Parameters passed to the callback function depend on the individual event
* being triggered. See the event documentation for those parameters.
*
* @param eventName [String] the event name to register the listener for
* @param callback [Function] the listener callback function
* @return [AWS.SequentialExecutor] the same object for chaining
*/
AWS.SequentialExecutor = AWS.util.inherit({
constructor: function SequentialExecutor() {
this._events = {};
},
/**
* @api private
*/
listeners: function listeners(eventName) {
return this._events[eventName] ? this._events[eventName].slice(0) : [];
},
on: function on(eventName, listener) {
if (this._events[eventName]) {
this._events[eventName].push(listener);
} else {
this._events[eventName] = [listener];
}
return this;
},
/**
* @api private
*/
onAsync: function onAsync(eventName, listener) {
listener._isAsync = true;
return this.on(eventName, listener);
},
removeListener: function removeListener(eventName, listener) {
var listeners = this._events[eventName];
if (listeners) {
var length = listeners.length;
var position = -1;
for (var i = 0; i < length; ++i) {
if (listeners[i] === listener) {
position = i;
}
}
if (position > -1) {
listeners.splice(position, 1);
}
}
return this;
},
removeAllListeners: function removeAllListeners(eventName) {
if (eventName) {
delete this._events[eventName];
} else {
this._events = {};
}
return this;
},
/**
* @api private
*/
emit: function emit(eventName, eventArgs, doneCallback) {
if (!doneCallback) doneCallback = function() { };
var listeners = this.listeners(eventName);
var count = listeners.length;
this.callListeners(listeners, eventArgs, doneCallback);
return count > 0;
},
/**
* @api private
*/
callListeners: function callListeners(listeners, args, doneCallback, prevError) {
var self = this;
var error = prevError || null;
function callNextListener(err) {
if (err) {
error = AWS.util.error(error || new Error(), err);
if (self._haltHandlersOnError) {
return doneCallback.call(self, error);
}
}
self.callListeners(listeners, args, doneCallback, error);
}
while (listeners.length > 0) {
var listener = listeners.shift();
if (listener._isAsync) { // asynchronous listener
listener.apply(self, args.concat([callNextListener]));
return; // stop here, callNextListener will continue
} else { // synchronous listener
try {
listener.apply(self, args);
} catch (err) {
error = AWS.util.error(error || new Error(), err);
}
if (error && self._haltHandlersOnError) {
doneCallback.call(self, error);
return;
}
}
}
doneCallback.call(self, error);
},
/**
* Adds or copies a set of listeners from another list of
* listeners or SequentialExecutor object.
*
* @param listeners [map<String,Array<Function>>, AWS.SequentialExecutor]
* a list of events and callbacks, or an event emitter object
* containing listeners to add to this emitter object.
* @return [AWS.SequentialExecutor] the emitter object, for chaining.
* @example Adding listeners from a map of listeners
* emitter.addListeners({
* event1: [function() { ... }, function() { ... }],
* event2: [function() { ... }]
* });
* emitter.emit('event1'); // emitter has event1
* emitter.emit('event2'); // emitter has event2
* @example Adding listeners from another emitter object
* var emitter1 = new AWS.SequentialExecutor();
* emitter1.on('event1', function() { ... });
* emitter1.on('event2', function() { ... });
* var emitter2 = new AWS.SequentialExecutor();
* emitter2.addListeners(emitter1);
* emitter2.emit('event1'); // emitter2 has event1
* emitter2.emit('event2'); // emitter2 has event2
*/
addListeners: function addListeners(listeners) {
var self = this;
// extract listeners if parameter is an SequentialExecutor object
if (listeners._events) listeners = listeners._events;
AWS.util.each(listeners, function(event, callbacks) {
if (typeof callbacks === 'function') callbacks = [callbacks];
AWS.util.arrayEach(callbacks, function(callback) {
self.on(event, callback);
});
});
return self;
},
/**
* Registers an event with {on} and saves the callback handle function
* as a property on the emitter object using a given `name`.
*
* @param name [String] the property name to set on this object containing
* the callback function handle so that the listener can be removed in
* the future.
* @param (see on)
* @return (see on)
* @example Adding a named listener DATA_CALLBACK
* var listener = function() { doSomething(); };
* emitter.addNamedListener('DATA_CALLBACK', 'data', listener);
*
* // the following prints: true
* console.log(emitter.DATA_CALLBACK == listener);
*/
addNamedListener: function addNamedListener(name, eventName, callback) {
this[name] = callback;
this.addListener(eventName, callback);
return this;
},
/**
* @api private
*/
addNamedAsyncListener: function addNamedAsyncListener(name, eventName, callback) {
callback._isAsync = true;
return this.addNamedListener(name, eventName, callback);
},
/**
* Helper method to add a set of named listeners using
* {addNamedListener}. The callback contains a parameter
* with a handle to the `addNamedListener` method.
*
* @callback callback function(add)
* The callback function is called immediately in order to provide
* the `add` function to the block. This simplifies the addition of
* a large group of named listeners.
* @param add [Function] the {addNamedListener} function to call
* when registering listeners.
* @example Adding a set of named listeners
* emitter.addNamedListeners(function(add) {
* add('DATA_CALLBACK', 'data', function() { ... });
* add('OTHER', 'otherEvent', function() { ... });
* add('LAST', 'lastEvent', function() { ... });
* });
*
* // these properties are now set:
* emitter.DATA_CALLBACK;
* emitter.OTHER;
* emitter.LAST;
*/
addNamedListeners: function addNamedListeners(callback) {
var self = this;
callback(
function() {
self.addNamedListener.apply(self, arguments);
},
function() {
self.addNamedAsyncListener.apply(self, arguments);
}
);
return this;
}
});
/**
* {on} is the prefered method.
* @api private
*/
AWS.SequentialExecutor.prototype.addListener = AWS.SequentialExecutor.prototype.on;
module.exports = AWS.SequentialExecutor;

View File

@@ -0,0 +1,64 @@
import {Request} from './request';
import {AWSError} from './error';
import {ConfigurationOptions, ConfigBase} from './config';
export class Service {
/**
* Creates a new service object with a configuration object.
*/
constructor(config?: ServiceConfigurationOptions);
/**
* Defines a new Service class using a service identifier and list of versions including an optional set of features (functions) to apply to the class prototype.
*
* @param {string} serviceIdentifier - the identifier for the service.
* @param {string[]} versions - a list of versions that work with this service.
* @param {Object} features - an object to attach to the prototype.
*/
defineService(serviceIdentifier: string, versions: string[], features?: any): typeof Service;
/**
* Calls an operation on a service with the given input parameters.
*
* @param {string} operation - the name of the operation to call on the service.
* @param {map} params - a map of input options for the operation.
*/
makeRequest(operation: string, params?: {[key: string]: any}, callback?: (err: AWSError, data: any) => void): Request<any, AWSError>;
/**
* Calls an operation on a service with the given input parameters, without any authentication data.
*
* @param {string} operation - the name of the operation to call on the service.
* @param {map} params - a map of input options for the operation.
*/
makeUnauthenticatedRequest(operation: string, params?: {[key: string]: any}, callback?: (err: AWSError, data: any) => void): Request<any, AWSError>;
/**
* Override this method to setup any custom request listeners for each new request to the service.
*/
setupRequestListeners(): void;
/**
* Waits for a given state.
*/
waitFor(state: string, params?: {[key: string]: any}, callback?: (err: AWSError, data: any) => void): Request<any, AWSError>;
waitFor(state: string, callback?: (err: AWSError, data: any) => void): Request<any, AWSError>;
/**
* The list of API versions supported by this service.
*/
apiVersions: string[];
config: ConfigBase & ServiceConfigurationOptions;
}
export interface ServiceConfigurationOptions extends ConfigurationOptions {
/**
* The endpoint URI to send requests to. The default endpoint is built from the configured region.
* The endpoint should be a string like 'https://{service}.{region}.amazonaws.com'.
*/
endpoint?: string;
/**
* An optional map of parameters to bind to every request sent by this service object.
* For more information on bound parameters, see "Working with Services" in the Getting Started Guide.
*/
params?: {
[key: string]: any;
}
}

View File

@@ -0,0 +1,560 @@
var AWS = require('./core');
var Api = require('./model/api');
var regionConfig = require('./region_config');
var inherit = AWS.util.inherit;
var clientCount = 0;
/**
* The service class representing an AWS service.
*
* @abstract
*
* @!attribute apiVersions
* @return [Array<String>] the list of API versions supported by this service.
* @readonly
*/
AWS.Service = inherit({
/**
* Create a new service object with a configuration object
*
* @param config [map] a map of configuration options
*/
constructor: function Service(config) {
if (!this.loadServiceClass) {
throw AWS.util.error(new Error(),
'Service must be constructed with `new\' operator');
}
var ServiceClass = this.loadServiceClass(config || {});
if (ServiceClass) {
var originalConfig = AWS.util.copy(config);
var svc = new ServiceClass(config);
Object.defineProperty(svc, '_originalConfig', {
get: function() { return originalConfig; },
enumerable: false,
configurable: true
});
svc._clientId = ++clientCount;
return svc;
}
this.initialize(config);
},
/**
* @api private
*/
initialize: function initialize(config) {
var svcConfig = AWS.config[this.serviceIdentifier];
this.config = new AWS.Config(AWS.config);
if (svcConfig) this.config.update(svcConfig, true);
if (config) this.config.update(config, true);
this.validateService();
if (!this.config.endpoint) regionConfig(this);
this.config.endpoint = this.endpointFromTemplate(this.config.endpoint);
this.setEndpoint(this.config.endpoint);
},
/**
* @api private
*/
validateService: function validateService() {
},
/**
* @api private
*/
loadServiceClass: function loadServiceClass(serviceConfig) {
var config = serviceConfig;
if (!AWS.util.isEmpty(this.api)) {
return null;
} else if (config.apiConfig) {
return AWS.Service.defineServiceApi(this.constructor, config.apiConfig);
} else if (!this.constructor.services) {
return null;
} else {
config = new AWS.Config(AWS.config);
config.update(serviceConfig, true);
var version = config.apiVersions[this.constructor.serviceIdentifier];
version = version || config.apiVersion;
return this.getLatestServiceClass(version);
}
},
/**
* @api private
*/
getLatestServiceClass: function getLatestServiceClass(version) {
version = this.getLatestServiceVersion(version);
if (this.constructor.services[version] === null) {
AWS.Service.defineServiceApi(this.constructor, version);
}
return this.constructor.services[version];
},
/**
* @api private
*/
getLatestServiceVersion: function getLatestServiceVersion(version) {
if (!this.constructor.services || this.constructor.services.length === 0) {
throw new Error('No services defined on ' +
this.constructor.serviceIdentifier);
}
if (!version) {
version = 'latest';
} else if (AWS.util.isType(version, Date)) {
version = AWS.util.date.iso8601(version).split('T')[0];
}
if (Object.hasOwnProperty(this.constructor.services, version)) {
return version;
}
var keys = Object.keys(this.constructor.services).sort();
var selectedVersion = null;
for (var i = keys.length - 1; i >= 0; i--) {
// versions that end in "*" are not available on disk and can be
// skipped, so do not choose these as selectedVersions
if (keys[i][keys[i].length - 1] !== '*') {
selectedVersion = keys[i];
}
if (keys[i].substr(0, 10) <= version) {
return selectedVersion;
}
}
throw new Error('Could not find ' + this.constructor.serviceIdentifier +
' API to satisfy version constraint `' + version + '\'');
},
/**
* @api private
*/
api: {},
/**
* @api private
*/
defaultRetryCount: 3,
/**
* @api private
*/
customizeRequests: function customizeRequests(callback) {
if (!callback) {
this.customRequestHandler = null;
} else if (typeof callback === 'function') {
this.customRequestHandler = callback;
} else {
throw new Error('Invalid callback type \'' + typeof callback + '\' provided in customizeRequests');
}
},
/**
* Calls an operation on a service with the given input parameters.
*
* @param operation [String] the name of the operation to call on the service.
* @param params [map] a map of input options for the operation
* @callback callback function(err, data)
* If a callback is supplied, it is called when a response is returned
* from the service.
* @param err [Error] the error object returned from the request.
* Set to `null` if the request is successful.
* @param data [Object] the de-serialized data returned from
* the request. Set to `null` if a request error occurs.
*/
makeRequest: function makeRequest(operation, params, callback) {
if (typeof params === 'function') {
callback = params;
params = null;
}
params = params || {};
if (this.config.params) { // copy only toplevel bound params
var rules = this.api.operations[operation];
if (rules) {
params = AWS.util.copy(params);
AWS.util.each(this.config.params, function(key, value) {
if (rules.input.members[key]) {
if (params[key] === undefined || params[key] === null) {
params[key] = value;
}
}
});
}
}
var request = new AWS.Request(this, operation, params);
this.addAllRequestListeners(request);
if (callback) request.send(callback);
return request;
},
/**
* Calls an operation on a service with the given input parameters, without
* any authentication data. This method is useful for "public" API operations.
*
* @param operation [String] the name of the operation to call on the service.
* @param params [map] a map of input options for the operation
* @callback callback function(err, data)
* If a callback is supplied, it is called when a response is returned
* from the service.
* @param err [Error] the error object returned from the request.
* Set to `null` if the request is successful.
* @param data [Object] the de-serialized data returned from
* the request. Set to `null` if a request error occurs.
*/
makeUnauthenticatedRequest: function makeUnauthenticatedRequest(operation, params, callback) {
if (typeof params === 'function') {
callback = params;
params = {};
}
var request = this.makeRequest(operation, params).toUnauthenticated();
return callback ? request.send(callback) : request;
},
/**
* Waits for a given state
*
* @param state [String] the state on the service to wait for
* @param params [map] a map of parameters to pass with each request
* @callback callback function(err, data)
* If a callback is supplied, it is called when a response is returned
* from the service.
* @param err [Error] the error object returned from the request.
* Set to `null` if the request is successful.
* @param data [Object] the de-serialized data returned from
* the request. Set to `null` if a request error occurs.
*/
waitFor: function waitFor(state, params, callback) {
var waiter = new AWS.ResourceWaiter(this, state);
return waiter.wait(params, callback);
},
/**
* @api private
*/
addAllRequestListeners: function addAllRequestListeners(request) {
var list = [AWS.events, AWS.EventListeners.Core, this.serviceInterface(),
AWS.EventListeners.CorePost];
for (var i = 0; i < list.length; i++) {
if (list[i]) request.addListeners(list[i]);
}
// disable parameter validation
if (!this.config.paramValidation) {
request.removeListener('validate',
AWS.EventListeners.Core.VALIDATE_PARAMETERS);
}
if (this.config.logger) { // add logging events
request.addListeners(AWS.EventListeners.Logger);
}
this.setupRequestListeners(request);
// call prototype's customRequestHandler
if (typeof this.constructor.prototype.customRequestHandler === 'function') {
this.constructor.prototype.customRequestHandler(request);
}
// call instance's customRequestHandler
if (Object.prototype.hasOwnProperty.call(this, 'customRequestHandler') && typeof this.customRequestHandler === 'function') {
this.customRequestHandler(request);
}
},
/**
* Override this method to setup any custom request listeners for each
* new request to the service.
*
* @abstract
*/
setupRequestListeners: function setupRequestListeners() {
},
/**
* Gets the signer class for a given request
* @api private
*/
getSignerClass: function getSignerClass() {
var version;
if (this.config.signatureVersion) {
version = this.config.signatureVersion;
} else {
version = this.api.signatureVersion;
}
return AWS.Signers.RequestSigner.getVersion(version);
},
/**
* @api private
*/
serviceInterface: function serviceInterface() {
switch (this.api.protocol) {
case 'ec2': return AWS.EventListeners.Query;
case 'query': return AWS.EventListeners.Query;
case 'json': return AWS.EventListeners.Json;
case 'rest-json': return AWS.EventListeners.RestJson;
case 'rest-xml': return AWS.EventListeners.RestXml;
}
if (this.api.protocol) {
throw new Error('Invalid service `protocol\' ' +
this.api.protocol + ' in API config');
}
},
/**
* @api private
*/
successfulResponse: function successfulResponse(resp) {
return resp.httpResponse.statusCode < 300;
},
/**
* How many times a failed request should be retried before giving up.
* the defaultRetryCount can be overriden by service classes.
*
* @api private
*/
numRetries: function numRetries() {
if (this.config.maxRetries !== undefined) {
return this.config.maxRetries;
} else {
return this.defaultRetryCount;
}
},
/**
* @api private
*/
retryDelays: function retryDelays(retryCount) {
return AWS.util.calculateRetryDelay(retryCount, this.config.retryDelayOptions);
},
/**
* @api private
*/
retryableError: function retryableError(error) {
if (this.networkingError(error)) return true;
if (this.expiredCredentialsError(error)) return true;
if (this.throttledError(error)) return true;
if (error.statusCode >= 500) return true;
return false;
},
/**
* @api private
*/
networkingError: function networkingError(error) {
return error.code === 'NetworkingError';
},
/**
* @api private
*/
expiredCredentialsError: function expiredCredentialsError(error) {
// TODO : this only handles *one* of the expired credential codes
return (error.code === 'ExpiredTokenException');
},
/**
* @api private
*/
clockSkewError: function clockSkewError(error) {
switch (error.code) {
case 'RequestTimeTooSkewed':
case 'RequestExpired':
case 'InvalidSignatureException':
case 'SignatureDoesNotMatch':
case 'AuthFailure':
case 'RequestInTheFuture':
return true;
default: return false;
}
},
/**
* @api private
*/
throttledError: function throttledError(error) {
// this logic varies between services
switch (error.code) {
case 'ProvisionedThroughputExceededException':
case 'Throttling':
case 'ThrottlingException':
case 'RequestLimitExceeded':
case 'RequestThrottled':
return true;
default:
return false;
}
},
/**
* @api private
*/
endpointFromTemplate: function endpointFromTemplate(endpoint) {
if (typeof endpoint !== 'string') return endpoint;
var e = endpoint;
e = e.replace(/\{service\}/g, this.api.endpointPrefix);
e = e.replace(/\{region\}/g, this.config.region);
e = e.replace(/\{scheme\}/g, this.config.sslEnabled ? 'https' : 'http');
return e;
},
/**
* @api private
*/
setEndpoint: function setEndpoint(endpoint) {
this.endpoint = new AWS.Endpoint(endpoint, this.config);
},
/**
* @api private
*/
paginationConfig: function paginationConfig(operation, throwException) {
var paginator = this.api.operations[operation].paginator;
if (!paginator) {
if (throwException) {
var e = new Error();
throw AWS.util.error(e, 'No pagination configuration for ' + operation);
}
return null;
}
return paginator;
}
});
AWS.util.update(AWS.Service, {
/**
* Adds one method for each operation described in the api configuration
*
* @api private
*/
defineMethods: function defineMethods(svc) {
AWS.util.each(svc.prototype.api.operations, function iterator(method) {
if (svc.prototype[method]) return;
var operation = svc.prototype.api.operations[method];
if (operation.authtype === 'none') {
svc.prototype[method] = function (params, callback) {
return this.makeUnauthenticatedRequest(method, params, callback);
};
} else {
svc.prototype[method] = function (params, callback) {
return this.makeRequest(method, params, callback);
};
}
});
},
/**
* Defines a new Service class using a service identifier and list of versions
* including an optional set of features (functions) to apply to the class
* prototype.
*
* @param serviceIdentifier [String] the identifier for the service
* @param versions [Array<String>] a list of versions that work with this
* service
* @param features [Object] an object to attach to the prototype
* @return [Class<Service>] the service class defined by this function.
*/
defineService: function defineService(serviceIdentifier, versions, features) {
AWS.Service._serviceMap[serviceIdentifier] = true;
if (!Array.isArray(versions)) {
features = versions;
versions = [];
}
var svc = inherit(AWS.Service, features || {});
if (typeof serviceIdentifier === 'string') {
AWS.Service.addVersions(svc, versions);
var identifier = svc.serviceIdentifier || serviceIdentifier;
svc.serviceIdentifier = identifier;
} else { // defineService called with an API
svc.prototype.api = serviceIdentifier;
AWS.Service.defineMethods(svc);
}
return svc;
},
/**
* @api private
*/
addVersions: function addVersions(svc, versions) {
if (!Array.isArray(versions)) versions = [versions];
svc.services = svc.services || {};
for (var i = 0; i < versions.length; i++) {
if (svc.services[versions[i]] === undefined) {
svc.services[versions[i]] = null;
}
}
svc.apiVersions = Object.keys(svc.services).sort();
},
/**
* @api private
*/
defineServiceApi: function defineServiceApi(superclass, version, apiConfig) {
var svc = inherit(superclass, {
serviceIdentifier: superclass.serviceIdentifier
});
function setApi(api) {
if (api.isApi) {
svc.prototype.api = api;
} else {
svc.prototype.api = new Api(api);
}
}
if (typeof version === 'string') {
if (apiConfig) {
setApi(apiConfig);
} else {
try {
setApi(AWS.apiLoader(superclass.serviceIdentifier, version));
} catch (err) {
throw AWS.util.error(err, {
message: 'Could not find API configuration ' +
superclass.serviceIdentifier + '-' + version
});
}
}
if (!Object.prototype.hasOwnProperty.call(superclass.services, version)) {
superclass.apiVersions = superclass.apiVersions.concat(version).sort();
}
superclass.services[version] = svc;
} else {
setApi(version);
}
AWS.Service.defineMethods(svc);
return svc;
},
/**
* @api private
*/
hasService: function(identifier) {
return Object.prototype.hasOwnProperty.call(AWS.Service._serviceMap, identifier);
},
/**
* @api private
*/
_serviceMap: {}
});
module.exports = AWS.Service;

View File

@@ -0,0 +1,42 @@
function AcceptorStateMachine(states, state) {
this.currentState = state || null;
this.states = states || {};
}
AcceptorStateMachine.prototype.runTo = function runTo(finalState, done, bindObject, inputError) {
if (typeof finalState === 'function') {
inputError = bindObject; bindObject = done;
done = finalState; finalState = null;
}
var self = this;
var state = self.states[self.currentState];
state.fn.call(bindObject || self, inputError, function(err) {
if (err) {
if (state.fail) self.currentState = state.fail;
else return done ? done.call(bindObject, err) : null;
} else {
if (state.accept) self.currentState = state.accept;
else return done ? done.call(bindObject) : null;
}
if (self.currentState === finalState) {
return done ? done.call(bindObject, err) : null;
}
self.runTo(finalState, done, bindObject, err);
});
};
AcceptorStateMachine.prototype.addState = function addState(name, acceptState, failState, fn) {
if (typeof acceptState === 'function') {
fn = acceptState; acceptState = null; failState = null;
} else if (typeof failState === 'function') {
fn = failState; failState = null;
}
if (!this.currentState) this.currentState = name;
this.states[name] = { accept: acceptState, fail: failState, fn: fn };
return this;
};
module.exports = AcceptorStateMachine;

View File

@@ -0,0 +1,871 @@
/* eslint guard-for-in:0 */
var AWS;
/**
* A set of utility methods for use with the AWS SDK.
*
* @!attribute abort
* Return this value from an iterator function {each} or {arrayEach}
* to break out of the iteration.
* @example Breaking out of an iterator function
* AWS.util.each({a: 1, b: 2, c: 3}, function(key, value) {
* if (key == 'b') return AWS.util.abort;
* });
* @see each
* @see arrayEach
* @api private
*/
var util = {
engine: function engine() {
if (util.isBrowser() && typeof navigator !== 'undefined') {
return navigator.userAgent;
} else {
var engine = process.platform + '/' + process.version;
if (process.env.AWS_EXECUTION_ENV) {
engine += ' exec-env/' + process.env.AWS_EXECUTION_ENV;
}
return engine;
}
},
userAgent: function userAgent() {
var name = util.isBrowser() ? 'js' : 'nodejs';
var agent = 'jibo-server-client-' + name + '/' + require('./core').VERSION;
if (name === 'nodejs') agent += ' ' + util.engine();
return agent;
},
isBrowser: function isBrowser() { return process && process.browser; },
isNode: function isNode() { return !util.isBrowser(); },
uriEscape: function uriEscape(string) {
var output = encodeURIComponent(string);
output = output.replace(/[^A-Za-z0-9_.~\-%]+/g, escape);
// AWS percent-encodes some extra non-standard characters in a URI
output = output.replace(/[*]/g, function(ch) {
return '%' + ch.charCodeAt(0).toString(16).toUpperCase();
});
return output;
},
uriEscapePath: function uriEscapePath(string) {
var parts = [];
util.arrayEach(string.split('/'), function (part) {
parts.push(util.uriEscape(part));
});
return parts.join('/');
},
urlParse: function urlParse(url) {
return util.url.parse(url);
},
urlFormat: function urlFormat(url) {
return util.url.format(url);
},
queryStringParse: function queryStringParse(qs) {
return util.querystring.parse(qs);
},
queryParamsToString: function queryParamsToString(params) {
var items = [];
var escape = util.uriEscape;
var sortedKeys = Object.keys(params).sort();
util.arrayEach(sortedKeys, function(name) {
var value = params[name];
var ename = escape(name);
var result = ename + '=';
if (Array.isArray(value)) {
var vals = [];
util.arrayEach(value, function(item) { vals.push(escape(item)); });
result = ename + '=' + vals.sort().join('&' + ename + '=');
} else if (value !== undefined && value !== null) {
result = ename + '=' + escape(value);
}
items.push(result);
});
return items.join('&');
},
readFileSync: function readFileSync(path) {
if (util.isBrowser()) return null;
return require('fs').readFileSync(path, 'utf-8');
},
base64: {
encode: function encode64(string) {
if (typeof string === 'number') {
throw util.error(new Error('Cannot base64 encode number ' + string));
}
var buf = (typeof util.Buffer.from === 'function' && util.Buffer.from !== Uint8Array.from) ? util.Buffer.from(string) : new util.Buffer(string);
return buf.toString('base64');
},
decode: function decode64(string) {
if (typeof string === 'number') {
throw util.error(new Error('Cannot base64 decode number ' + string));
}
return (typeof util.Buffer.from === 'function' && util.Buffer.from !== Uint8Array.from) ? util.Buffer.from(string, 'base64') : new util.Buffer(string, 'base64');
}
},
buffer: {
toStream: function toStream(buffer) {
if (!util.Buffer.isBuffer(buffer)) buffer = new util.Buffer(buffer);
var readable = new (util.stream.Readable)();
var pos = 0;
readable._read = function(size) {
if (pos >= buffer.length) return readable.push(null);
var end = pos + size;
if (end > buffer.length) end = buffer.length;
readable.push(buffer.slice(pos, end));
pos = end;
};
return readable;
},
/**
* Concatenates a list of Buffer objects.
*/
concat: function(buffers) {
var length = 0,
offset = 0,
buffer = null, i;
for (i = 0; i < buffers.length; i++) {
length += buffers[i].length;
}
buffer = new util.Buffer(length);
for (i = 0; i < buffers.length; i++) {
buffers[i].copy(buffer, offset);
offset += buffers[i].length;
}
return buffer;
}
},
string: {
byteLength: function byteLength(string) {
if (string === null || string === undefined) return 0;
if (typeof string === 'string') string = new util.Buffer(string);
if (typeof string.byteLength === 'number') {
return string.byteLength;
} else if (typeof string.length === 'number') {
return string.length;
} else if (typeof string.size === 'number') {
return string.size;
} else if (typeof string.path === 'string') {
return require('fs').lstatSync(string.path).size;
} else {
throw util.error(new Error('Cannot determine length of ' + string),
{ object: string });
}
},
upperFirst: function upperFirst(string) {
return string[0].toUpperCase() + string.substr(1);
},
lowerFirst: function lowerFirst(string) {
return string[0].toLowerCase() + string.substr(1);
}
},
ini: {
parse: function string(ini) {
var currentSection, map = {};
util.arrayEach(ini.split(/\r?\n/), function(line) {
line = line.split(/(^|\s)[;#]/)[0]; // remove comments
var section = line.match(/^\s*\[([^\[\]]+)\]\s*$/);
if (section) {
currentSection = section[1];
} else if (currentSection) {
var item = line.match(/^\s*(.+?)\s*=\s*(.+?)\s*$/);
if (item) {
map[currentSection] = map[currentSection] || {};
map[currentSection][item[1]] = item[2];
}
}
});
return map;
}
},
fn: {
noop: function() {},
/**
* Turn a synchronous function into as "async" function by making it call
* a callback. The underlying function is called with all but the last argument,
* which is treated as the callback. The callback is passed passed a first argument
* of null on success to mimick standard node callbacks.
*/
makeAsync: function makeAsync(fn, expectedArgs) {
if (expectedArgs && expectedArgs <= fn.length) {
return fn;
}
return function() {
var args = Array.prototype.slice.call(arguments, 0);
var callback = args.pop();
var result = fn.apply(null, args);
callback(result);
};
}
},
/**
* Date and time utility functions.
*/
date: {
/**
* @return [Date] the current JavaScript date object. Since all
* AWS services rely on this date object, you can override
* this function to provide a special time value to AWS service
* requests.
*/
getDate: function getDate() {
if (!AWS) AWS = require('./core');
if (AWS.config.systemClockOffset) { // use offset when non-zero
return new Date(new Date().getTime() + AWS.config.systemClockOffset);
} else {
return new Date();
}
},
/**
* @return [String] the date in ISO-8601 format
*/
iso8601: function iso8601(date) {
if (date === undefined) { date = util.date.getDate(); }
return date.toISOString().replace(/\.\d{3}Z$/, 'Z');
},
/**
* @return [String] the date in RFC 822 format
*/
rfc822: function rfc822(date) {
if (date === undefined) { date = util.date.getDate(); }
return date.toUTCString();
},
/**
* @return [Integer] the UNIX timestamp value for the current time
*/
unixTimestamp: function unixTimestamp(date) {
if (date === undefined) { date = util.date.getDate(); }
return date.getTime() / 1000;
},
/**
* @param [String,number,Date] date
* @return [Date]
*/
from: function format(date) {
if (typeof date === 'number') {
return new Date(date * 1000); // unix timestamp
} else {
return new Date(date);
}
},
/**
* Given a Date or date-like value, this function formats the
* date into a string of the requested value.
* @param [String,number,Date] date
* @param [String] formatter Valid formats are:
# * 'iso8601'
# * 'rfc822'
# * 'unixTimestamp'
* @return [String]
*/
format: function format(date, formatter) {
if (!formatter) formatter = 'iso8601';
return util.date[formatter](util.date.from(date));
},
parseTimestamp: function parseTimestamp(value) {
if (typeof value === 'number') { // unix timestamp (number)
return new Date(value * 1000);
} else if (value.match(/^\d+$/)) { // unix timestamp
return new Date(value * 1000);
} else if (value.match(/^\d{4}/)) { // iso8601
return new Date(value);
} else if (value.match(/^\w{3},/)) { // rfc822
return new Date(value);
} else {
throw util.error(
new Error('unhandled timestamp format: ' + value),
{code: 'TimestampParserError'});
}
}
},
crypto: {
crc32Table: [
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419,
0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4,
0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07,
0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856,
0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3,
0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A,
0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599,
0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190,
0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F,
0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E,
0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3,
0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A,
0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5,
0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010,
0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17,
0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6,
0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344,
0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A,
0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1,
0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF,
0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE,
0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31,
0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C,
0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B,
0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1,
0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278,
0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7,
0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66,
0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605,
0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8,
0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
0x2D02EF8D],
crc32: function crc32(data) {
var tbl = util.crypto.crc32Table;
var crc = 0 ^ -1;
if (typeof data === 'string') {
data = new util.Buffer(data);
}
for (var i = 0; i < data.length; i++) {
var code = data.readUInt8(i);
crc = (crc >>> 8) ^ tbl[(crc ^ code) & 0xFF];
}
return (crc ^ -1) >>> 0;
},
hmac: function hmac(key, string, digest, fn) {
if (!digest) digest = 'binary';
if (digest === 'buffer') { digest = undefined; }
if (!fn) fn = 'sha256';
if (typeof string === 'string') string = new util.Buffer(string);
return util.crypto.lib.createHmac(fn, key).update(string).digest(digest);
},
md5: function md5(data, digest, callback) {
return util.crypto.hash('md5', data, digest, callback);
},
sha256: function sha256(data, digest, callback) {
return util.crypto.hash('sha256', data, digest, callback);
},
hash: function(algorithm, data, digest, callback) {
var hash = util.crypto.createHash(algorithm);
if (!digest) { digest = 'binary'; }
if (digest === 'buffer') { digest = undefined; }
if (typeof data === 'string') data = new util.Buffer(data);
var sliceFn = util.arraySliceFn(data);
var isBuffer = util.Buffer.isBuffer(data);
//Identifying objects with an ArrayBuffer as buffers
if (util.isBrowser() && typeof ArrayBuffer !== 'undefined' && data && data.buffer instanceof ArrayBuffer) isBuffer = true;
if (callback && typeof data === 'object' &&
typeof data.on === 'function' && !isBuffer) {
data.on('data', function(chunk) { hash.update(chunk); });
data.on('error', function(err) { callback(err); });
data.on('end', function() { callback(null, hash.digest(digest)); });
} else if (callback && sliceFn && !isBuffer &&
typeof FileReader !== 'undefined') {
// this might be a File/Blob
var index = 0, size = 1024 * 512;
var reader = new FileReader();
reader.onerror = function() {
callback(new Error('Failed to read data.'));
};
reader.onload = function() {
var buf = new util.Buffer(new Uint8Array(reader.result));
hash.update(buf);
index += buf.length;
reader._continueReading();
};
reader._continueReading = function() {
if (index >= data.size) {
callback(null, hash.digest(digest));
return;
}
var back = index + size;
if (back > data.size) back = data.size;
reader.readAsArrayBuffer(sliceFn.call(data, index, back));
};
reader._continueReading();
} else {
if (util.isBrowser() && typeof data === 'object' && !isBuffer) {
data = new util.Buffer(new Uint8Array(data));
}
var out = hash.update(data).digest(digest);
if (callback) callback(null, out);
return out;
}
},
toHex: function toHex(data) {
var out = [];
for (var i = 0; i < data.length; i++) {
out.push(('0' + data.charCodeAt(i).toString(16)).substr(-2, 2));
}
return out.join('');
},
createHash: function createHash(algorithm) {
return util.crypto.lib.createHash(algorithm);
}
},
/** @!ignore */
/* Abort constant */
abort: {},
each: function each(object, iterFunction) {
for (var key in object) {
if (Object.prototype.hasOwnProperty.call(object, key)) {
var ret = iterFunction.call(this, key, object[key]);
if (ret === util.abort) break;
}
}
},
arrayEach: function arrayEach(array, iterFunction) {
for (var idx in array) {
if (Object.prototype.hasOwnProperty.call(array, idx)) {
var ret = iterFunction.call(this, array[idx], parseInt(idx, 10));
if (ret === util.abort) break;
}
}
},
update: function update(obj1, obj2) {
util.each(obj2, function iterator(key, item) {
obj1[key] = item;
});
return obj1;
},
merge: function merge(obj1, obj2) {
return util.update(util.copy(obj1), obj2);
},
copy: function copy(object) {
if (object === null || object === undefined) return object;
var dupe = {};
// jshint forin:false
for (var key in object) {
dupe[key] = object[key];
}
return dupe;
},
isEmpty: function isEmpty(obj) {
for (var prop in obj) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
return false;
}
}
return true;
},
arraySliceFn: function arraySliceFn(obj) {
var fn = obj.slice || obj.webkitSlice || obj.mozSlice;
return typeof fn === 'function' ? fn : null;
},
isType: function isType(obj, type) {
// handle cross-"frame" objects
if (typeof type === 'function') type = util.typeName(type);
return Object.prototype.toString.call(obj) === '[object ' + type + ']';
},
typeName: function typeName(type) {
if (Object.prototype.hasOwnProperty.call(type, 'name')) return type.name;
var str = type.toString();
var match = str.match(/^\s*function (.+)\(/);
return match ? match[1] : str;
},
error: function error(err, options) {
var originalError = null;
if (typeof err.message === 'string' && err.message !== '') {
if (typeof options === 'string' || (options && options.message)) {
originalError = util.copy(err);
originalError.message = err.message;
}
}
err.message = err.message || null;
if (typeof options === 'string') {
err.message = options;
} else if (typeof options === 'object' && options !== null) {
util.update(err, options);
if (options.message)
err.message = options.message;
if (options.code || options.name)
err.code = options.code || options.name;
if (options.stack)
err.stack = options.stack;
}
if (typeof Object.defineProperty === 'function') {
Object.defineProperty(err, 'name', {writable: true, enumerable: false});
Object.defineProperty(err, 'message', {enumerable: true});
}
err.name = options && options.name || err.name || err.code || 'Error';
err.time = new Date();
if (originalError) err.originalError = originalError;
return err;
},
/**
* @api private
*/
inherit: function inherit(klass, features) {
var newObject = null;
if (features === undefined) {
features = klass;
klass = Object;
newObject = {};
} else {
var ctor = function ConstructorWrapper() {};
ctor.prototype = klass.prototype;
newObject = new ctor();
}
// constructor not supplied, create pass-through ctor
if (features.constructor === Object) {
features.constructor = function() {
if (klass !== Object) {
return klass.apply(this, arguments);
}
};
}
features.constructor.prototype = newObject;
util.update(features.constructor.prototype, features);
features.constructor.__super__ = klass;
return features.constructor;
},
/**
* @api private
*/
mixin: function mixin() {
var klass = arguments[0];
for (var i = 1; i < arguments.length; i++) {
// jshint forin:false
for (var prop in arguments[i].prototype) {
var fn = arguments[i].prototype[prop];
if (prop !== 'constructor') {
klass.prototype[prop] = fn;
}
}
}
return klass;
},
/**
* @api private
*/
hideProperties: function hideProperties(obj, props) {
if (typeof Object.defineProperty !== 'function') return;
util.arrayEach(props, function (key) {
Object.defineProperty(obj, key, {
enumerable: false, writable: true, configurable: true });
});
},
/**
* @api private
*/
property: function property(obj, name, value, enumerable, isValue) {
var opts = {
configurable: true,
enumerable: enumerable !== undefined ? enumerable : true
};
if (typeof value === 'function' && !isValue) {
opts.get = value;
}
else {
opts.value = value; opts.writable = true;
}
Object.defineProperty(obj, name, opts);
},
/**
* @api private
*/
memoizedProperty: function memoizedProperty(obj, name, get, enumerable) {
var cachedValue = null;
// build enumerable attribute for each value with lazy accessor.
util.property(obj, name, function() {
if (cachedValue === null) {
cachedValue = get();
}
return cachedValue;
}, enumerable);
},
/**
* TODO Remove in major version revision
* This backfill populates response data without the
* top-level payload name.
*
* @api private
*/
hoistPayloadMember: function hoistPayloadMember(resp) {
var req = resp.request;
var operation = req.operation;
var output = req.service.api.operations[operation].output;
if (output.payload) {
var payloadMember = output.members[output.payload];
var responsePayload = resp.data[output.payload];
if (payloadMember.type === 'structure') {
util.each(responsePayload, function(key, value) {
util.property(resp.data, key, value, false);
});
}
}
},
/**
* Compute SHA-256 checksums of streams
*
* @api private
*/
computeSha256: function computeSha256(body, done) {
if (util.isNode()) {
var Stream = util.stream.Stream;
var fs = require('fs');
if (body instanceof Stream) {
if (typeof body.path === 'string') { // assume file object
var settings = {};
if (typeof body.start === 'number') {
settings.start = body.start;
}
if (typeof body.end === 'number') {
settings.end = body.end;
}
body = fs.createReadStream(body.path, settings);
} else {
return done(null, 'UNSIGNED-PAYLOAD');
}
}
}
util.crypto.sha256(body, 'hex', function(err, sha) {
if (err) done(err);
else done(null, sha);
});
},
/**
* @api private
*/
isClockSkewed: function isClockSkewed(serverTime) {
if (serverTime) {
util.property(AWS.config, 'isClockSkewed',
Math.abs(new Date().getTime() - serverTime) >= 300000, false);
return AWS.config.isClockSkewed;
}
},
applyClockOffset: function applyClockOffset(serverTime) {
if (serverTime)
AWS.config.systemClockOffset = serverTime - new Date().getTime();
},
/**
* @api private
*/
extractRequestId: function extractRequestId(resp) {
var requestId = resp.httpResponse.headers['x-amz-request-id'] ||
resp.httpResponse.headers['x-amzn-requestid'];
if (!requestId && resp.data && resp.data.ResponseMetadata) {
requestId = resp.data.ResponseMetadata.RequestId;
}
if (requestId) {
resp.requestId = requestId;
}
if (resp.error) {
resp.error.requestId = requestId;
}
},
/**
* @api private
*/
addPromises: function addPromises(constructors, PromiseDependency) {
if (PromiseDependency === undefined && AWS && AWS.config) {
PromiseDependency = AWS.config.getPromisesDependency();
}
if (PromiseDependency === undefined && typeof Promise !== 'undefined') {
PromiseDependency = Promise;
}
if (typeof PromiseDependency !== 'function') var deletePromises = true;
if (!Array.isArray(constructors)) constructors = [constructors];
for (var ind = 0; ind < constructors.length; ind++) {
var constructor = constructors[ind];
if (deletePromises) {
if (constructor.deletePromisesFromClass) {
constructor.deletePromisesFromClass();
}
} else if (constructor.addPromisesToClass) {
constructor.addPromisesToClass(PromiseDependency);
}
}
},
/**
* @api private
*/
promisifyMethod: function promisifyMethod(methodName, PromiseDependency) {
return function promise() {
var self = this;
return new PromiseDependency(function(resolve, reject) {
self[methodName](function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
};
},
/**
* @api private
*/
isDualstackAvailable: function isDualstackAvailable(service) {
if (!service) return false;
var metadata = require('../apis/metadata.json');
if (typeof service !== 'string') service = service.serviceIdentifier;
if (typeof service !== 'string' || !metadata.hasOwnProperty(service)) return false;
return !!metadata[service].dualstackAvailable;
},
/**
* @api private
*/
calculateRetryDelay: function calculateRetryDelay(retryCount, retryDelayOptions) {
if (!retryDelayOptions) retryDelayOptions = {};
var customBackoff = retryDelayOptions.customBackoff || null;
if (typeof customBackoff === 'function') {
return customBackoff(retryCount);
}
var base = retryDelayOptions.base || 100;
var delay = Math.random() * (Math.pow(2, retryCount) * base);
return delay;
},
/**
* @api private
*/
handleRequestWithRetries: function handleRequestWithRetries(httpRequest, options, cb) {
if (!options) options = {};
var http = AWS.HttpClient.getInstance();
var httpOptions = options.httpOptions || {};
var retryCount = 0;
var errCallback = function(err) {
var maxRetries = options.maxRetries || 0;
if (err && err.code === 'TimeoutError') err.retryable = true;
if (err && err.retryable && retryCount < maxRetries) {
retryCount++;
var delay = util.calculateRetryDelay(retryCount, options.retryDelayOptions);
setTimeout(sendRequest, delay + (err.retryAfter || 0));
} else {
cb(err);
}
};
var sendRequest = function() {
var data = '';
http.handleRequest(httpRequest, httpOptions, function(httpResponse) {
httpResponse.on('data', function(chunk) { data += chunk.toString(); });
httpResponse.on('end', function() {
var statusCode = httpResponse.statusCode;
if (statusCode < 300) {
cb(null, data);
} else {
var retryAfter = parseInt(httpResponse.headers['retry-after'], 10) * 1000 || 0;
var err = util.error(new Error(),
{ retryable: statusCode >= 500 || statusCode === 429 }
);
if (retryAfter && err.retryable) err.retryAfter = retryAfter;
errCallback(err);
}
});
}, errCallback);
};
process.nextTick(sendRequest);
}
};
module.exports = util;