Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

executable file 1569 lines (1345 sloc) 51.172 kb
<?php
/*
* Copyright 2010-2012 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.
*/
/*%******************************************************************************************%*/
// EXCEPTIONS
/**
* Default CFRuntime Exception.
*/
class CFRuntime_Exception extends Exception {}
/**
* Parsing Exception.
*/
class Parser_Exception extends Exception {}
/*%******************************************************************************************%*/
// DETERMINE WHAT ENVIRONMENT DATA TO ADD TO THE USERAGENT FOR METRIC TRACKING
/*
Define a temporary callback function for this calculation. Get the PHP version and any
required/optional extensions that are leveraged.
Tracking this data gives Amazon better metrics about what configurations are being used
so that forward-looking plans for the code can be made with more certainty (e.g. What
version of PHP are most people running? Do they tend to have the latest PCRE?).
*/
function __aws_sdk_ua_callback()
{
$ua_append = '';
$extensions = get_loaded_extensions();
$sorted_extensions = array();
if ($extensions)
{
foreach ($extensions as $extension)
{
if ($extension === 'curl' && function_exists('curl_version'))
{
$curl_version = curl_version();
$sorted_extensions[strtolower($extension)] = $curl_version['version'];
}
elseif ($extension === 'pcre' && defined('PCRE_VERSION'))
{
$pcre_version = explode(' ', PCRE_VERSION);
$sorted_extensions[strtolower($extension)] = $pcre_version[0];
}
elseif ($extension === 'openssl' && defined('OPENSSL_VERSION_TEXT'))
{
$openssl_version = explode(' ', OPENSSL_VERSION_TEXT);
$sorted_extensions[strtolower($extension)] = $openssl_version[1];
}
else
{
$sorted_extensions[strtolower($extension)] = phpversion($extension);
}
}
}
foreach (array('simplexml', 'json', 'pcre', 'spl', 'curl', 'openssl', 'apc', 'xcache', 'memcache', 'memcached', 'pdo', 'pdo_sqlite', 'sqlite', 'sqlite3', 'zlib', 'xdebug') as $ua_ext)
{
if (isset($sorted_extensions[$ua_ext]) && $sorted_extensions[$ua_ext])
{
$ua_append .= ' ' . $ua_ext . '/' . $sorted_extensions[$ua_ext];
}
elseif (isset($sorted_extensions[$ua_ext]))
{
$ua_append .= ' ' . $ua_ext . '/0';
}
}
foreach (array('memory_limit', 'date.timezone', 'open_basedir', 'safe_mode', 'zend.enable_gc') as $cfg)
{
$cfg_value = ini_get($cfg);
if (in_array($cfg, array('memory_limit', 'date.timezone'), true))
{
$ua_append .= ' ' . $cfg . '/' . str_replace('/', '.', $cfg_value);
}
elseif (in_array($cfg, array('open_basedir', 'safe_mode', 'zend.enable_gc'), true))
{
if ($cfg_value === false || $cfg_value === '' || $cfg_value === 0)
{
$cfg_value = 'off';
}
elseif ($cfg_value === true || $cfg_value === '1' || $cfg_value === 1)
{
$cfg_value = 'on';
}
$ua_append .= ' ' . $cfg . '/' . $cfg_value;
}
}
return $ua_append;
}
/*%******************************************************************************************%*/
// INTERMEDIARY CONSTANTS
define('CFRUNTIME_NAME', 'aws-sdk-php');
define('CFRUNTIME_VERSION', '1.5.16');
define('CFRUNTIME_BUILD', '20121109104359');
define('CFRUNTIME_USERAGENT', CFRUNTIME_NAME . '/' . CFRUNTIME_VERSION . ' PHP/' . PHP_VERSION . ' ' . str_replace(' ', '_', php_uname('s')) . '/' . str_replace(' ', '_', php_uname('r')) . ' Arch/' . php_uname('m') . ' SAPI/' . php_sapi_name() . ' Integer/' . PHP_INT_MAX . ' Build/' . CFRUNTIME_BUILD . __aws_sdk_ua_callback());
/*%******************************************************************************************%*/
// CLASS
/**
* Core functionality and default settings shared across all SDK classes. All methods and properties in this
* class are inherited by the service-specific classes.
*
* @version 2012.05.25
* @license See the included NOTICE.md file for more information.
* @copyright See the included NOTICE.md file for more information.
* @link http://aws.amazon.com/php/ PHP Developer Center
*/
class CFRuntime
{
/*%******************************************************************************************%*/
// CONSTANTS
/**
* Name of the software.
*/
const NAME = CFRUNTIME_NAME;
/**
* Version of the software.
*/
const VERSION = CFRUNTIME_VERSION;
/**
* Build ID of the software.
*/
const BUILD = CFRUNTIME_BUILD;
/**
* User agent string used to identify the software.
*/
const USERAGENT = CFRUNTIME_USERAGENT;
/*%******************************************************************************************%*/
// PROPERTIES
/**
* The Amazon API Key.
*/
public $key;
/**
* The Amazon API Secret Key.
*/
public $secret_key;
/**
* The Amazon Authentication Token.
*/
public $auth_token;
/**
* Handle for the utility functions.
*/
public $util;
/**
* An identifier for the current AWS service.
*/
public $service = null;
/**
* The supported API version.
*/
public $api_version = null;
/**
* The state of whether auth should be handled as AWS Query.
*/
public $use_aws_query = true;
/**
* The default class to use for utilities (defaults to <CFUtilities>).
*/
public $utilities_class = 'CFUtilities';
/**
* The default class to use for HTTP requests (defaults to <CFRequest>).
*/
public $request_class = 'CFRequest';
/**
* The default class to use for HTTP responses (defaults to <CFResponse>).
*/
public $response_class = 'CFResponse';
/**
* The default class to use for parsing XML (defaults to <CFSimpleXML>).
*/
public $parser_class = 'CFSimpleXML';
/**
* The default class to use for handling batch requests (defaults to <CFBatchRequest>).
*/
public $batch_class = 'CFBatchRequest';
/**
* The state of SSL/HTTPS use.
*/
public $use_ssl = true;
/**
* The state of SSL certificate verification.
*/
public $ssl_verification = true;
/**
* The proxy to use for connecting.
*/
public $proxy = null;
/**
* The alternate hostname to use, if any.
*/
public $hostname = null;
/**
* The state of the capability to override the hostname with <set_hostname()>.
*/
public $override_hostname = true;
/**
* The alternate port number to use, if any.
*/
public $port_number = null;
/**
* The alternate resource prefix to use, if any.
*/
public $resource_prefix = null;
/**
* The state of cache flow usage.
*/
public $use_cache_flow = false;
/**
* The caching class to use.
*/
public $cache_class = null;
/**
* The caching location to use.
*/
public $cache_location = null;
/**
* When the cache should be considered stale.
*/
public $cache_expires = null;
/**
* The state of cache compression.
*/
public $cache_compress = null;
/**
* The current instantiated cache object.
*/
public $cache_object = null;
/**
* The current instantiated batch request object.
*/
public $batch_object = null;
/**
* The internally instantiated batch request object.
*/
public $internal_batch_object = null;
/**
* The state of batch flow usage.
*/
public $use_batch_flow = false;
/**
* The state of the cache deletion setting.
*/
public $delete_cache = false;
/**
* The state of the debug mode setting.
*/
public $debug_mode = false;
/**
* The number of times to retry failed requests.
*/
public $max_retries = 3;
/**
* The user-defined callback function to call when a stream is read from.
*/
public $registered_streaming_read_callback = null;
/**
* The user-defined callback function to call when a stream is written to.
*/
public $registered_streaming_write_callback = null;
/**
* The credentials to use for authentication.
*/
public $credentials = array();
/**
* The authentication class to use.
*/
public $auth_class = null;
/**
* The operation to execute.
*/
public $operation = null;
/**
* The payload to send.
*/
public $payload = array();
/**
* The string prefix to prepend to the operation name.
*/
public $operation_prefix = '';
/**
* The number of times a request has been retried.
*/
public $redirects = 0;
/**
* The state of whether the response should be parsed or not.
*/
public $parse_the_response = true;
/*%******************************************************************************************%*/
// CONSTRUCTOR
/**
* The constructor. This class should not be instantiated directly. Rather, a service-specific class
* should be instantiated.
*
* @param array $options (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>certificate_authority</code> - <code>boolean</code> - Optional - Determines which Cerificate Authority file to use. A value of boolean <code>false</code> will use the Certificate Authority file available on the system. A value of boolean <code>true</code> will use the Certificate Authority provided by the SDK. Passing a file system path to a Certificate Authority file (chmodded to <code>0755</code>) will use that. Leave this set to <code>false</code> if you're not sure.</li>
* <li><code>credentials</code> - <code>string</code> - Optional - The name of the credential set to use for authentication.</li>
* <li><code>default_cache_config</code> - <code>string</code> - Optional - This option allows a preferred storage type to be configured for long-term caching. This can be changed later using the <set_cache_config()> method. Valid values are: <code>apc</code>, <code>xcache</code>, or a file system path such as <code>./cache</code> or <code>/tmp/cache/</code>.</li>
* <li><code>key</code> - <code>string</code> - Optional - Your AWS key, or a session key. If blank, the default credential set will be used.</li>
* <li><code>instance_profile_timeout</code> - <code>integer</code> - Optional - When retrieving IAM instance profile credentials, there is a hard connection timeout that defaults to 2 seconds to prevent unnecessary on non-EC2 systems. This setting allows you to change that timeout if needed.</li>
* <li><code>secret</code> - <code>string</code> - Optional - Your AWS secret key, or a session secret key. If blank, the default credential set will be used.</li>
* <li><code>token</code> - <code>string</code> - Optional - An AWS session token.</li>
* <li><code>use_instance_profile_credentials</code> - <code>boolean</code> - Optional - Forces the use of IAM Instance Profile credentials, even when regular credentials are provided.</li></ul>
* @return void
*/
public function __construct(array $options = array())
{
// Instantiate the utilities class.
$this->util = new $this->utilities_class();
// Determine the current service.
$this->service = get_class($this);
// Create credentials based on the options
$runtime_credentials = new CFCredential($options);
$credentials_provided = false;
// Retrieve a credential set from config.inc.php if it exists
if (isset($options['credentials']))
{
// Use a specific credential set and merge with the runtime credentials
$this->credentials = CFCredentials::get($options['credentials'])
->merge($runtime_credentials);
}
else
{
try
{
// Use the default credential set and merge with the runtime credentials
$this->credentials = CFCredentials::get(CFCredentials::DEFAULT_KEY)
->merge($runtime_credentials);
}
catch (CFCredentials_Exception $e)
{
// Only the runtime credentials were provided
$this->credentials = $runtime_credentials;
}
}
// Check if keys were actually provided
if (isset($this->credentials['key']) && isset($this->credentials['secret']))
{
$credentials_provided = true;
}
// Check for an instance profile credentials override
if (isset($this->credentials['use_instance_profile_credentials']) && $this->credentials['use_instance_profile_credentials'])
{
$credentials_provided = false;
}
// Automatically enable whichever caching mechanism is set to default.
$this->set_cache_config($this->credentials->default_cache_config);
// If no credentials were provided, try to get them from the EC2 instance profile
if (!$credentials_provided)
{
// Default caching mechanism is required
if (!$this->credentials->default_cache_config)
{
// @codeCoverageIgnoreStart
throw new CFCredentials_Exception('No credentials were provided. The SDK attempts to retrieve Instance '
. 'Profile credentials from the EC2 Instance Metadata Service, but doing this requires the '
. '"default_cache_config" option to be set in the config.inc.php file or constructor. In order to '
. 'cache the retrieved credentials.');
// @codeCoverageIgnoreEnd
}
// Instantiate and invoke the cache for instance profile credentials
$cache = new $this->cache_class('instance_profile_credentials', $this->cache_location, 0, $this->cache_compress);
if ($data = $cache->read())
{
$cache->expire_in((strtotime($data['expires']) - time()) * 0.85);
}
$instance_profile_credentials = $cache->response_manager(array($this, 'cache_instance_profile_credentials'), array($cache, $options));
$this->credentials->key = $instance_profile_credentials['key'];
$this->credentials->secret = $instance_profile_credentials['secret'];
$this->credentials->token = $instance_profile_credentials['token'];
}
// Set internal credentials after they are resolved
$this->key = $this->credentials->key;
$this->secret_key = $this->credentials->secret;
$this->auth_token = $this->credentials->token;
}
/**
* Alternate approach to constructing a new instance. Supports chaining.
*
* @param array $options (Optional) An associative array of parameters that can have the following keys: <ul>
* <li><code>certificate_authority</code> - <code>boolean</code> - Optional - Determines which Cerificate Authority file to use. A value of boolean <code>false</code> will use the Certificate Authority file available on the system. A value of boolean <code>true</code> will use the Certificate Authority provided by the SDK. Passing a file system path to a Certificate Authority file (chmodded to <code>0755</code>) will use that. Leave this set to <code>false</code> if you're not sure.</li>
* <li><code>credentials</code> - <code>string</code> - Optional - The name of the credential set to use for authentication.</li>
* <li><code>default_cache_config</code> - <code>string</code> - Optional - This option allows a preferred storage type to be configured for long-term caching. This can be changed later using the <set_cache_config()> method. Valid values are: <code>apc</code>, <code>xcache</code>, or a file system path such as <code>./cache</code> or <code>/tmp/cache/</code>.</li>
* <li><code>key</code> - <code>string</code> - Optional - Your AWS key, or a session key. If blank, the default credential set will be used.</li>
* <li><code>secret</code> - <code>string</code> - Optional - Your AWS secret key, or a session secret key. If blank, the default credential set will be used.</li>
* <li><code>token</code> - <code>string</code> - Optional - An AWS session token.</li></ul>
* @return void
*/
public static function factory(array $options = array())
{
if (version_compare(PHP_VERSION, '5.3.0', '<'))
{
throw new Exception('PHP 5.3 or newer is required to instantiate a new class with CLASS::factory().');
}
$self = get_called_class();
return new $self($options);
}
/*%******************************************************************************************%*/
// MAGIC METHODS
/**
* A magic method that allows `camelCase` method names to be translated into `snake_case` names.
*
* @param string $name (Required) The name of the method.
* @param array $arguments (Required) The arguments passed to the method.
* @return mixed The results of the intended method.
*/
public function __call($name, $arguments)
{
// Convert camelCase method calls to snake_case.
$method_name = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $name));
if (method_exists($this, $method_name))
{
return call_user_func_array(array($this, $method_name), $arguments);
}
throw new CFRuntime_Exception('The method ' . $name . '() is undefined. Attempted to map to ' . $method_name . '() which is also undefined. Error occurred');
}
/*%******************************************************************************************%*/
// SET CUSTOM SETTINGS
/**
* Set the proxy settings to use.
*
* @param string $proxy (Required) Accepts proxy credentials in the following format: `proxy://user:pass@hostname:port`
* @return $this A reference to the current instance.
*/
public function set_proxy($proxy)
{
$this->proxy = $proxy;
return $this;
}
/**
* Set the hostname to connect to. This is useful for alternate services that are API-compatible with
* AWS, but run from a different hostname.
*
* @param string $hostname (Required) The alternate hostname to use in place of the default one. Useful for mock or test applications living on different hostnames.
* @param integer $port_number (Optional) The alternate port number to use in place of the default one. Useful for mock or test applications living on different port numbers.
* @return $this A reference to the current instance.
*/
public function set_hostname($hostname, $port_number = null)
{
if ($this->override_hostname)
{
$this->hostname = $hostname;
if ($port_number)
{
$this->port_number = $port_number;
$this->hostname .= ':' . (string) $this->port_number;
}
}
return $this;
}
/**
* Set the resource prefix to use. This method is useful for alternate services that are API-compatible
* with AWS.
*
* @param string $prefix (Required) An alternate prefix to prepend to the resource path. Useful for mock or test applications.
* @return $this A reference to the current instance.
*/
public function set_resource_prefix($prefix)
{
$this->resource_prefix = $prefix;
return $this;
}
/**
* Disables any subsequent use of the <set_hostname()> method.
*
* @param boolean $override (Optional) Whether or not subsequent calls to <set_hostname()> should be obeyed. A `false` value disables the further effectiveness of <set_hostname()>. Defaults to `true`.
* @return $this A reference to the current instance.
*/
public function allow_hostname_override($override = true)
{
$this->override_hostname = $override;
return $this;
}
/**
* Disables SSL/HTTPS connections for hosts that don't support them. Some services, however, still
* require SSL support.
*
* This method will throw a user warning when invoked, which can be hidden by changing your
* <php:error_reporting()> settings.
*
* @return $this A reference to the current instance.
*/
public function disable_ssl()
{
trigger_error('Disabling SSL connections is potentially unsafe and highly discouraged.', E_USER_WARNING);
$this->use_ssl = false;
return $this;
}
/**
* Disables the verification of the SSL Certificate Authority. Doing so can enable an attacker to carry
* out a man-in-the-middle attack.
*
* https://secure.wikimedia.org/wikipedia/en/wiki/Man-in-the-middle_attack
*
* This method will throw a user warning when invoked, which can be hidden by changing your
* <php:error_reporting()> settings.
*
* @return $this A reference to the current instance.
*/
public function disable_ssl_verification($ssl_verification = false)
{
trigger_error('Disabling the verification of SSL certificates can lead to man-in-the-middle attacks. It is potentially unsafe and highly discouraged.', E_USER_WARNING);
$this->ssl_verification = $ssl_verification;
return $this;
}
/**
* Enables HTTP request/response header logging to `STDERR`.
*
* @param boolean $enabled (Optional) Whether or not to enable debug mode. Defaults to `true`.
* @return $this A reference to the current instance.
*/
public function enable_debug_mode($enabled = true)
{
$this->debug_mode = $enabled;
return $this;
}
/**
* Sets the maximum number of times to retry failed requests.
*
* @param integer $retries (Optional) The maximum number of times to retry failed requests. Defaults to `3`.
* @return $this A reference to the current instance.
*/
public function set_max_retries($retries = 3)
{
$this->max_retries = $retries;
return $this;
}
/**
* Set the caching configuration to use for response caching.
*
* @param string $location (Required) <p>The location to store the cache object in. This may vary by cache method.</p><ul><li>File - The local file system paths such as <code>./cache</code> (relative) or <code>/tmp/cache/</code> (absolute). The location must be server-writable.</li><li>APC - Pass in <code>apc</code> to use this lightweight cache. You must have the <a href="http://php.net/apc">APC extension</a> installed.</li><li>XCache - Pass in <code>xcache</code> to use this lightweight cache. You must have the <a href="http://xcache.lighttpd.net">XCache</a> extension installed.</li><li>Memcached - Pass in an indexed array of associative arrays. Each associative array should have a <code>host</code> and a <code>port</code> value representing a <a href="http://php.net/memcached">Memcached</a> server to connect to.</li><li>PDO - A URL-style string (e.g. <code>pdo.mysql://user:pass@localhost/cache</code>) or a standard DSN-style string (e.g. <code>pdo.sqlite:/sqlite/cache.db</code>). MUST be prefixed with <code>pdo.</code>. See <code>CachePDO</code> and <a href="http://php.net/pdo">PDO</a> for more details.</li></ul>
* @param boolean $gzip (Optional) Whether or not data should be gzipped before being stored. A value of `true` will compress the contents before caching them. A value of `false` will leave the contents uncompressed. Defaults to `true`.
* @return $this A reference to the current instance.
*/
public function set_cache_config($location, $gzip = true)
{
// If location is empty, don't do anything.
if (empty($location))
{
return $this;
}
// If we have an array, we're probably passing in Memcached servers and ports.
if (is_array($location))
{
$this->cache_class = 'CacheMC';
}
else
{
// I would expect locations like `/tmp/cache`, `pdo.mysql://user:pass@hostname:port`, `pdo.sqlite:memory:`, and `apc`.
$type = strtolower(substr($location, 0, 3));
switch ($type)
{
case 'apc':
$this->cache_class = 'CacheAPC';
break;
case 'xca': // First three letters of `xcache`
$this->cache_class = 'CacheXCache';
break;
case 'pdo':
$this->cache_class = 'CachePDO';
$location = substr($location, 4);
break;
default:
$this->cache_class = 'CacheFile';
break;
}
}
// Set the remaining cache information.
$this->cache_location = $location;
$this->cache_compress = $gzip;
return $this;
}
/**
* Register a callback function to execute whenever a data stream is read from using
* <CFRequest::streaming_read_callback()>.
*
* The user-defined callback function should accept three arguments:
*
* <ul>
* <li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
* <li><code>$file_handle</code> - <code>resource</code> - Required - The file handle resource that represents the file on the local file system.</li>
* <li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
* </ul>
*
* @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
* <li>The name of a global function to execute, passed as a string.</li>
* <li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
* <li>An anonymous function (PHP 5.3+).</li></ul>
* @return $this A reference to the current instance.
*/
public function register_streaming_read_callback($callback)
{
$this->registered_streaming_read_callback = $callback;
return $this;
}
/**
* Register a callback function to execute whenever a data stream is written to using
* <CFRequest::streaming_write_callback()>.
*
* The user-defined callback function should accept two arguments:
*
* <ul>
* <li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
* <li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
* </ul>
*
* @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
* <li>The name of a global function to execute, passed as a string.</li>
* <li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
* <li>An anonymous function (PHP 5.3+).</li></ul>
* @return $this A reference to the current instance.
*/
public function register_streaming_write_callback($callback)
{
$this->registered_streaming_write_callback = $callback;
return $this;
}
/**
* Fetches and caches STS credentials. This is meant to be used by the constructor, and is not to be
* manually invoked.
*
* @param CacheCore $cache (Required) The a reference to the cache object that is being used to handle the caching.
* @param array $options (Required) The options that were passed into the constructor.
* @return mixed The data to be cached, or NULL.
*/
public function cache_sts_credentials($cache, $options)
{
$token = new AmazonSTS($options);
$response = $token->get_session_token();
if ($response->isOK())
{
// Update the expiration
$expiration_time = strtotime((string) $response->body->GetSessionTokenResult->Credentials->Expiration);
$expiration_duration = round(($expiration_time - time()) * 0.85);
$cache->expire_in($expiration_duration);
// Return the important data
$credentials = $response->body->GetSessionTokenResult->Credentials;
return array(
'key' => (string) $credentials->AccessKeyId,
'secret' => (string) $credentials->SecretAccessKey,
'token' => (string) $credentials->SessionToken,
'expires' => (string) $credentials->Expiration,
);
}
// @codeCoverageIgnoreStart
throw new STS_Exception('Temporary credentials from the AWS Security '
. 'Token Service could not be retrieved using the provided long '
. 'term credentials. It\'s possible that the provided long term '
. 'credentials were invalid.');
// @codeCoverageIgnoreEnd
}
/**
* Fetches and caches EC2 instance profile credentials. This is meant to be used by the constructor, and is not to
* be manually invoked.
*
* @param CacheCore $cache (Required) The a reference to the cache object that is being used to handle the caching.
* @param array $options (Required) The options that were passed into the constructor.
* @return mixed The data to be cached, or NULL.
*/
public function cache_instance_profile_credentials($cache, $options)
{
$instance_profile_url = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/';
$connect_timeout = isset($options['instance_profile_timeout']) ? $options['instance_profile_timeout'] : 2;
try
{
// Make a call to the EC2 Metadata Service to find the available instance profile
$request = new RequestCore($instance_profile_url);
$request->set_curlopts(array(CURLOPT_CONNECTTIMEOUT => $connect_timeout));
$response = $request->send_request(true);
if ($response->isOK())
{
// Get the instance profile name
$profile = (string) $response->body;
// Make a call to the EC2 Metadata Service to get the instance profile credentials
$request = new RequestCore($instance_profile_url . $profile);
$request->set_curlopts(array(CURLOPT_CONNECTTIMEOUT => $connect_timeout));
$response = $request->send_request(true);
if ($response->isOK())
{
// Get the credentials
$credentials = json_decode($response->body, true);
if ($credentials['Code'] === 'Success')
{
// Determine the expiration time
$expiration_time = strtotime((string) $credentials['Expiration']);
$expiration_duration = round(($expiration_time - time()) * 0.85);
$cache->expire_in($expiration_duration);
// Return the credential information
return array(
'key' => $credentials['AccessKeyId'],
'secret' => $credentials['SecretAccessKey'],
'token' => $credentials['Token'],
'expires' => $credentials['Expiration'],
);
}
}
}
}
catch (cURL_Exception $e)
{
// The EC2 Metadata Service does not exist or had timed out.
// An exception will be thrown on the next line.
}
// @codeCoverageIgnoreStart
throw new CFCredentials_Exception('No credentials were provided. The SDK attempted to retrieve Instance '
. 'Profile credentials from the EC2 Instance Metadata Service, but failed to do so. Instance profile '
. 'credentials are only accessible on EC2 instances configured with a specific IAM role.');
// @codeCoverageIgnoreEnd
}
/*%******************************************************************************************%*/
// SET CUSTOM CLASSES
/**
* Set a custom class for this functionality. Use this method when extending/overriding existing classes
* with new functionality.
*
* The replacement class must extend from <CFUtilities>.
*
* @param string $class (Optional) The name of the new class to use for this functionality.
* @return $this A reference to the current instance.
*/
public function set_utilities_class($class = 'CFUtilities')
{
$this->utilities_class = $class;
$this->util = new $this->utilities_class();
return $this;
}
/**
* Set a custom class for this functionality. Use this method when extending/overriding existing classes
* with new functionality.
*
* The replacement class must extend from <CFRequest>.
*
* @param string $class (Optional) The name of the new class to use for this functionality.
* @param $this A reference to the current instance.
*/
public function set_request_class($class = 'CFRequest')
{
$this->request_class = $class;
return $this;
}
/**
* Set a custom class for this functionality. Use this method when extending/overriding existing classes
* with new functionality.
*
* The replacement class must extend from <CFResponse>.
*
* @param string $class (Optional) The name of the new class to use for this functionality.
* @return $this A reference to the current instance.
*/
public function set_response_class($class = 'CFResponse')
{
$this->response_class = $class;
return $this;
}
/**
* Set a custom class for this functionality. Use this method when extending/overriding existing classes
* with new functionality.
*
* The replacement class must extend from <CFSimpleXML>.
*
* @param string $class (Optional) The name of the new class to use for this functionality.
* @return $this A reference to the current instance.
*/
public function set_parser_class($class = 'CFSimpleXML')
{
$this->parser_class = $class;
return $this;
}
/**
* Set a custom class for this functionality. Use this method when extending/overriding existing classes
* with new functionality.
*
* The replacement class must extend from <CFBatchRequest>.
*
* @param string $class (Optional) The name of the new class to use for this functionality.
* @return $this A reference to the current instance.
*/
public function set_batch_class($class = 'CFBatchRequest')
{
$this->batch_class = $class;
return $this;
}
/*%******************************************************************************************%*/
// AUTHENTICATION
/**
* Default, shared method for authenticating a connection to AWS.
*
* @param string $operation (Required) Indicates the operation to perform.
* @param array $payload (Required) An associative array of parameters for authenticating. See the individual methods for allowed keys.
* @return CFResponse Object containing a parsed HTTP response.
*/
public function authenticate($operation, $payload)
{
$original_payload = $payload;
$method_arguments = func_get_args();
$curlopts = array();
$return_curl_handle = false;
if (substr($operation, 0, strlen($this->operation_prefix)) !== $this->operation_prefix)
{
$operation = $this->operation_prefix . $operation;
}
// Extract the custom CURLOPT settings from the payload
if (is_array($payload) && isset($payload['curlopts']))
{
$curlopts = $payload['curlopts'];
unset($payload['curlopts']);
}
// Determine whether the response or curl handle should be returned
if (is_array($payload) && isset($payload['returnCurlHandle']))
{
$return_curl_handle = isset($payload['returnCurlHandle']) ? $payload['returnCurlHandle'] : false;
unset($payload['returnCurlHandle']);
}
// Use the caching flow to determine if we need to do a round-trip to the server.
if ($this->use_cache_flow)
{
// Generate an identifier specific to this particular set of arguments.
$cache_id = $this->key . '_' . get_class($this) . '_' . $operation . '_' . sha1(serialize($method_arguments));
// Instantiate the appropriate caching object.
$this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress);
if ($this->delete_cache)
{
$this->use_cache_flow = false;
$this->delete_cache = false;
return $this->cache_object->delete();
}
// Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request.
$data = $this->cache_object->response_manager(array($this, 'cache_callback'), $method_arguments);
// Parse the XML body
$data = $this->parse_callback($data);
// End!
return $data;
}
/*%******************************************************************************************%*/
// Signer
$signer = new $this->auth_class($this->hostname, $operation, $payload, $this->credentials);
$signer->key = $this->key;
$signer->secret_key = $this->secret_key;
$signer->auth_token = $this->auth_token;
$signer->api_version = $this->api_version;
$signer->utilities_class = $this->utilities_class;
$signer->request_class = $this->request_class;
$signer->response_class = $this->response_class;
$signer->use_ssl = $this->use_ssl;
$signer->proxy = $this->proxy;
$signer->util = $this->util;
$signer->registered_streaming_read_callback = $this->registered_streaming_read_callback;
$signer->registered_streaming_write_callback = $this->registered_streaming_write_callback;
$request = $signer->authenticate();
// Update RequestCore settings
$request->request_class = $this->request_class;
$request->response_class = $this->response_class;
$request->ssl_verification = $this->ssl_verification;
/*%******************************************************************************************%*/
// Debug mode
if ($this->debug_mode)
{
$request->debug_mode = $this->debug_mode;
}
// Set custom CURLOPT settings
if (count($curlopts))
{
$request->set_curlopts($curlopts);
}
// Manage the (newer) batch request API or the (older) returnCurlHandle setting.
if ($this->use_batch_flow)
{
$handle = $request->prep_request();
$this->batch_object->add($handle);
$this->use_batch_flow = false;
return $handle;
}
elseif ($return_curl_handle)
{
return $request->prep_request();
}
// Send!
$request->send_request();
// Prepare the response.
$headers = $request->get_response_header();
$headers['x-aws-stringtosign'] = $signer->string_to_sign;
if (isset($signer->canonical_request))
{
$headers['x-aws-canonicalrequest'] = $signer->canonical_request;
}
$headers['x-aws-request-headers'] = $request->request_headers;
$headers['x-aws-body'] = $signer->querystring;
$data = new $this->response_class($headers, ($this->parse_the_response === true) ? $this->parse_callback($request->get_response_body()) : $request->get_response_body(), $request->get_response_code());
// Was it Amazon's fault the request failed? Retry the request until we reach $max_retries.
if (
(integer) $request->get_response_code() === 500 || // Internal Error (presumably transient)
(integer) $request->get_response_code() === 503) // Service Unavailable (presumably transient)
{
if ($this->redirects <= $this->max_retries)
{
// Exponential backoff
$delay = (integer) (pow(4, $this->redirects) * 100000);
usleep($delay);
$this->redirects++;
$data = $this->authenticate($operation, $original_payload);
}
}
// DynamoDB has custom logic
elseif (
(integer) $request->get_response_code() === 400 &&
stripos((string) $request->get_response_body(), 'com.amazonaws.dynamodb.') !== false && (
stripos((string) $request->get_response_body(), 'ProvisionedThroughputExceededException') !== false
)
)
{
if ($this->redirects === 0)
{
$this->redirects++;
$data = $this->authenticate($operation, $original_payload);
}
elseif ($this->redirects <= max($this->max_retries, 10))
{
// Exponential backoff
$delay = (integer) (pow(2, ($this->redirects - 1)) * 50000);
usleep($delay);
$this->redirects++;
$data = $this->authenticate($operation, $original_payload);
}
}
$this->redirects = 0;
return $data;
}
/*%******************************************************************************************%*/
// BATCH REQUEST LAYER
/**
* Specifies that the intended request should be queued for a later batch request.
*
* @param CFBatchRequest $queue (Optional) The <CFBatchRequest> instance to use for managing batch requests. If not available, it generates a new instance of <CFBatchRequest>.
* @return $this A reference to the current instance.
*/
public function batch(CFBatchRequest &$queue = null)
{
if ($queue)
{
$this->batch_object = $queue;
}
elseif ($this->internal_batch_object)
{
$this->batch_object = &$this->internal_batch_object;
}
else
{
$this->internal_batch_object = new $this->batch_class();
$this->batch_object = &$this->internal_batch_object;
}
$this->use_batch_flow = true;
return $this;
}
/**
* Executes the batch request queue by sending all queued requests.
*
* @param boolean $clear_after_send (Optional) Whether or not to clear the batch queue after sending a request. Defaults to `true`. Set this to `false` if you are caching batch responses and want to retrieve results later.
* @return array An array of <CFResponse> objects.
*/
public function send($clear_after_send = true)
{
if ($this->use_batch_flow)
{
// When we send the request, disable batch flow.
$this->use_batch_flow = false;
// If we're not caching, simply send the request.
if (!$this->use_cache_flow)
{
$response = $this->batch_object->send();
$parsed_data = array_map(array($this, 'parse_callback'), $response);
$parsed_data = new CFArray($parsed_data);
// Clear the queue
if ($clear_after_send)
{
$this->batch_object->queue = array();
}
return $parsed_data;
}
// Generate an identifier specific to this particular set of arguments.
$cache_id = $this->key . '_' . get_class($this) . '_' . sha1(serialize($this->batch_object));
// Instantiate the appropriate caching object.
$this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress);
if ($this->delete_cache)
{
$this->use_cache_flow = false;
$this->delete_cache = false;
return $this->cache_object->delete();
}
// Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request.
$data_set = $this->cache_object->response_manager(array($this, 'cache_callback_batch'), array($this->batch_object));
$parsed_data = array_map(array($this, 'parse_callback'), $data_set);
$parsed_data = new CFArray($parsed_data);
// Clear the queue
if ($clear_after_send)
{
$this->batch_object->queue = array();
}
// End!
return $parsed_data;
}
// Load the class
$null = new CFBatchRequest();
unset($null);
throw new CFBatchRequest_Exception('You must use $object->batch()->send()');
}
/**
* Parses a response body into a PHP object if appropriate.
*
* @param CFResponse|string $response (Required) The <CFResponse> object to parse, or an XML string that would otherwise be a response body.
* @param string $content_type (Optional) The content-type to use when determining how to parse the content.
* @return CFResponse|string A parsed <CFResponse> object, or parsed XML.
*/
public function parse_callback($response, $headers = null)
{
// Bail out
if (!$this->parse_the_response) return $response;
// Shorten this so we have a (mostly) single code path
if (isset($response->body))
{
if (is_string($response->body))
{
$body = $response->body;
}
else
{
return $response;
}
}
elseif (is_string($response))
{
$body = $response;
}
else
{
return $response;
}
// Decompress gzipped content
if (isset($headers['content-encoding']))
{
switch (strtolower(trim($headers['content-encoding'], "\x09\x0A\x0D\x20")))
{
case 'gzip':
case 'x-gzip':
$decoder = new CFGzipDecode($body);
if ($decoder->parse())
{
$body = $decoder->data;
}
break;
case 'deflate':
if (($uncompressed = gzuncompress($body)) !== false)
{
$body = $uncompressed;
}
elseif (($uncompressed = gzinflate($body)) !== false)
{
$body = $uncompressed;
}
break;
}
}
// Look for XML cues
if (
(isset($headers['content-type']) && ($headers['content-type'] === 'text/xml' || $headers['content-type'] === 'application/xml')) || // We know it's XML
(!isset($headers['content-type']) && (stripos($body, '<?xml') === 0 || strpos($body, '<Error>') === 0) || preg_match('/^<(\w*) xmlns="http(s?):\/\/(\w*).amazon(aws)?.com/im', $body)) // Sniff for XML
)
{
// Strip the default XML namespace to simplify XPath expressions
$body = str_replace("xmlns=", "ns=", $body);
try {
// Parse the XML body
$body = new $this->parser_class($body);
}
catch (Exception $e)
{
throw new Parser_Exception($e->getMessage());
}
}
// Look for JSON cues
elseif (
(isset($headers['content-type']) && ($headers['content-type'] === 'application/json') || $headers['content-type'] === 'application/x-amz-json-1.0') || // We know it's JSON
(!isset($headers['content-type']) && $this->util->is_json($body)) // Sniff for JSON
)
{
// Normalize JSON to a CFSimpleXML object
$body = CFJSON::to_xml($body, $this->parser_class);
}
// Put the parsed data back where it goes
if (isset($response->body))
{
$response->body = $body;
}
else
{
$response = $body;
}
return $response;
}
/*%******************************************************************************************%*/
// CACHING LAYER
/**
* Specifies that the resulting <CFResponse> object should be cached according to the settings from
* <set_cache_config()>.
*
* @param string|integer $expires (Required) The time the cache is to expire. Accepts a number of seconds as an integer, or an amount of time, as a string, that is understood by <php:strtotime()> (e.g. "1 hour").
* @param $this A reference to the current instance.
* @return $this
*/
public function cache($expires)
{
// Die if they haven't used set_cache_config().
if (!$this->cache_class)
{
throw new CFRuntime_Exception('Must call set_cache_config() before using cache()');
}
if (is_string($expires))
{
$expires = strtotime($expires);
$this->cache_expires = $expires - time();
}
elseif (is_int($expires))
{
$this->cache_expires = $expires;
}
$this->use_cache_flow = true;
return $this;
}
/**
* The callback function that is executed when the cache doesn't exist or has expired. The response of
* this method is cached. Accepts identical parameters as the <authenticate()> method. Never call this
* method directly -- it is used internally by the caching system.
*
* @param string $operation (Required) Indicates the operation to perform.
* @param array $payload (Required) An associative array of parameters for authenticating. See the individual methods for allowed keys.
* @return CFResponse A parsed HTTP response.
*/
public function cache_callback($operation, $payload)
{
// Disable the cache flow since it's already been handled.
$this->use_cache_flow = false;
// Make the request
$response = $this->authenticate($operation, $payload);
// If this is an XML document, convert it back to a string.
if (isset($response->body) && ($response->body instanceof SimpleXMLElement))
{
$response->body = $response->body->asXML();
}
return $response;
}
/**
* Used for caching the results of a batch request. Never call this method directly; it is used
* internally by the caching system.
*
* @param CFBatchRequest $batch (Required) The batch request object to send.
* @return CFResponse A parsed HTTP response.
*/
public function cache_callback_batch(CFBatchRequest $batch)
{
return $batch->send();
}
/**
* Deletes a cached <CFResponse> object using the specified cache storage type.
*
* @return boolean A value of `true` if cached object exists and is successfully deleted, otherwise `false`.
*/
public function delete_cache()
{
$this->use_cache_flow = true;
$this->delete_cache = true;
return $this;
}
}
/**
* Contains the functionality for auto-loading service classes.
*/
class CFLoader
{
/*%******************************************************************************************%*/
// AUTO-LOADER
/**
* Automatically load classes that aren't included.
*
* @param string $class (Required) The classname to load.
* @return boolean Whether or not the file was successfully loaded.
*/
public static function autoloader($class)
{
$path = dirname(__FILE__) . DIRECTORY_SEPARATOR;
// Amazon SDK classes
if (strstr($class, 'Amazon'))
{
if (file_exists($require_this = $path . 'services' . DIRECTORY_SEPARATOR . str_ireplace('Amazon', '', strtolower($class)) . '.class.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Utility classes
elseif (strstr($class, 'CF'))
{
if (file_exists($require_this = $path . 'utilities' . DIRECTORY_SEPARATOR . str_ireplace('CF', '', strtolower($class)) . '.class.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load CacheCore
elseif (strstr($class, 'Cache'))
{
if (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'cachecore' . DIRECTORY_SEPARATOR . strtolower($class) . '.class.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load RequestCore
elseif (strstr($class, 'RequestCore') || strstr($class, 'ResponseCore'))
{
if (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'requestcore' . DIRECTORY_SEPARATOR . 'requestcore.class.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load Transmogrifier
elseif (strstr($class, 'Transmogrifier'))
{
if (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'dom' . DIRECTORY_SEPARATOR . 'Transmogrifier.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load Authentication Signers
elseif (strstr($class, 'Auth'))
{
if (file_exists($require_this = $path . 'authentication' . DIRECTORY_SEPARATOR . str_replace('auth', 'signature_', strtolower($class)) . '.class.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load Signer interface
elseif ($class === 'Signer')
{
if (!interface_exists('Signable', false) &&
file_exists($require_this = $path . 'authentication' . DIRECTORY_SEPARATOR . 'signable.interface.php'))
{
require_once $require_this;
}
if (file_exists($require_this = $path . 'authentication' . DIRECTORY_SEPARATOR . 'signer.abstract.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load Symfony YAML classes
elseif (strstr($class, 'sfYaml'))
{
if (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'yaml' . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'sfYaml.php'))
{
require_once $require_this;
return true;
}
return false;
}
return false;
}
}
// Register the autoloader.
spl_autoload_register(array('CFLoader', 'autoloader'));
/*%******************************************************************************************%*/
// CONFIGURATION
// Look for include file in the same directory (e.g. `./config.inc.php`).
if (file_exists(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'config.inc.php'))
{
include_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'config.inc.php';
}
// Fallback to `~/.aws/sdk/config.inc.php`
else
{
if (!isset($_ENV['HOME']) && isset($_SERVER['HOME']))
{
$_ENV['HOME'] = $_SERVER['HOME'];
}
elseif (!isset($_ENV['HOME']) && !isset($_SERVER['HOME']))
{
$os = strtolower(PHP_OS);
if (in_array($os, array('windows', 'winnt', 'win32')))
{
$_ENV['HOME'] = false;
}
else
{
$dir = exec('(cd ~ && pwd) 2>&1', $out, $exit);
if ($exit === 0)
{
$_ENV['HOME'] = trim($dir);
}
else
{
error_log('Failed to determine HOME directory after trying "' . $dir . '" (exit code ' . $exit . ')');
$_ENV['HOME'] = false;
}
}
if (!$_ENV['HOME'])
{
switch ($os)
{
case 'darwin':
$_ENV['HOME'] = '/Users/' . get_current_user();
break;
case 'windows':
case 'winnt':
case 'win32':
$_ENV['HOME'] = 'c:' . DIRECTORY_SEPARATOR . 'Documents and Settings' . DIRECTORY_SEPARATOR . get_current_user();
break;
default:
$_ENV['HOME'] = '/home/' . get_current_user();
break;
}
}
}
$path = DIRECTORY_SEPARATOR . '.aws' . DIRECTORY_SEPARATOR . 'sdk' . DIRECTORY_SEPARATOR . 'config.inc.php';
if (isset($_ENV['HOME']) && file_exists($_ENV['HOME'] . $path))
{
include_once $_ENV['HOME'] . $path;
}
unset($os, $dir, $out, $exit, $path);
}
Jump to Line
Something went wrong with that request. Please try again.